Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: use our ApeWorX action in tests #2513

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,26 @@ jobs:
with:
version: 1.14.12

- name: Setup Ape
uses: ApeWorX/github-action@main

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip uninstall eth-ape --yes
pip install .[test]

- name: Install Ape Dependencies
run: ape pm install

- name: Run Functional Tests
run: ape test tests/functional -m "not fuzzing" -s --cov=src --cov-append -n auto --dist loadgroup
run: ape test tests/functional -m "not fuzzing" -s --cov=src --cov-append -n auto --dist loadgroup -v ERROR

- name: Run Integration Tests
run: ape test tests/integration -m "not fuzzing" -s --cov=src --cov-append -n auto --dist loadgroup
run: ape test tests/integration -m "not fuzzing" -s --cov=src --cov-append -n auto --dist loadgroup -v ERROR

- name: Run Performance Tests
run: ape test tests/performance -s
run: ape test tests/performance -s -v ERROR

fuzzing:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repos:
name: black

- repo: https://github.com/pycqa/flake8
rev: 7.1.1
rev: 7.1.2
hooks:
- id: flake8
additional_dependencies: [flake8-breakpoint, flake8-print, flake8-pydantic, flake8-type-checking]
Expand Down
14 changes: 14 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
[build-system]
requires = ["setuptools>=75.0.0", "wheel", "setuptools_scm[toml]>=5.0"]

[tool.ape]
contracts_folder = "tests/functional/data/contracts"

[[tool.ape.dependencies]]
name = "openzeppelin"
version = "4.4.2"
github = "OpenZeppelin/openzeppelin-contracts"
config_override = { "solidity" = { "version" = "0.8.22" } }

[[tool.ape.dependencies]]
name = "openzeppelin"
version = "5.2.0"
github = "OpenZeppelin/openzeppelin-contracts"

[tool.ape.test]
show_internal = true

Expand Down
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"pytest-timeout>=2.2.0,<3", # For avoiding timing out during tests
"hypothesis>=6.2.0,<7.0", # Strategy-based fuzzer
"hypothesis-jsonschema==0.19.0", # JSON Schema fuzzer extension
"ape-vyper", # Needed for compiling test contracts
"ape-solidity", # Needed for compiling test contracts
],
"lint": [
"black>=25.1.0,<26", # Auto-formatter and linter
Expand All @@ -31,7 +33,7 @@
"types-toml", # Needed due to mypy typeshed
"types-SQLAlchemy>=1.4.49", # Needed due to mypy typeshed
"types-python-dateutil", # Needed due to mypy typeshed
"flake8>=7.1.1,<8", # Style linter
"flake8>=7.1.2,<8", # Style linter
"flake8-breakpoint>=1.1.0,<2", # Detect breakpoints left in code
"flake8-print>=4.0.1,<5", # Detect print statements left in code
"flake8-pydantic", # For detecting issues with Pydantic models
Expand Down
2 changes: 1 addition & 1 deletion src/ape/contracts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,7 @@ def identifier_lookup(self) -> dict[str, "ABI_W_SELECTOR_T"]:
def source_path(self) -> Optional[Path]:
"""
Returns the path to the local contract if determined that this container
belongs to the active project by cross checking source_id.
belongs to the active project by cross-checking source_id.
"""
if not (source_id := self.contract_type.source_id):
return None
Expand Down
43 changes: 29 additions & 14 deletions src/ape/managers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def _path_to_source_id(path: Path, root_path: Path) -> str:

class SourceManager(BaseManager):
"""
A manager of a local-project's sources-paths.
A manager of a local-project's source-paths.
Access via ``project.sources``. Allows source-access
from both ``source_id`` as well as ``path``. Handles
from both ``source_id`` and ``path``. Handles
detecting modified sources as well as excluded sources.
Is meant to resemble a PackageManifest's source dict
but with more functionality for active development.
Expand Down Expand Up @@ -177,7 +177,7 @@ def _all_files(self) -> list[Path]:
@property
def paths(self) -> Iterator[Path]:
"""
All contract sources paths.
All contract source paths.
"""
for path in self._all_files:
if self.is_excluded(path):
Expand Down Expand Up @@ -643,7 +643,7 @@ def manifest_path(self) -> Path:
def api_path(self) -> Path:
"""
The path to the dependency's API data-file. This data is necessary
for managing the install of the dependency.
for managing the installation of the dependency.
"""
return self._cache.get_api_path(self.package_id, self.version)

Expand Down Expand Up @@ -676,15 +676,19 @@ def uri(self) -> str:
return self.api.uri

def install(
self, use_cache: bool = True, config_override: Optional[dict] = None
self,
use_cache: bool = True,
config_override: Optional[dict] = None,
recurse: bool = True,
) -> "ProjectManager":
"""
Install this dependency.

Args:
use_cache (bool): To force a re-install, like a refresh, set this
use_cache (bool): To force reinstalling, like a refresh, set this
to ``False``.
config_override (dict): Optionally change the configuration during install.
recurse (bool): Set to ``False`` to avoid installing dependency of dependencies.

Returns:
:class:`~ape.managers.project.ProjectManager`: The resulting project, ready
Expand Down Expand Up @@ -729,8 +733,7 @@ def install(

did_fetch = True

# Reset global tried-fetch if it succeeded, so it can refresh
# if needbe.
# Reset global tried-fetch if it succeeded, so it can refresh.
self._tried_fetch = False

# Set name / version for the project, if it needs.
Expand Down Expand Up @@ -782,9 +785,8 @@ def install(
# Cache for next time.
self._installation = project

# Also, install dependencies of dependencies, if fetching for the
# first time.
if did_fetch:
# Install dependencies of dependencies if fetching for the first time.
if did_fetch and recurse:
spec = project.dependencies.get_project_dependencies(use_cache=use_cache)
list(spec)

Expand Down Expand Up @@ -992,7 +994,7 @@ def cache_api(self, api: DependencyAPI) -> Path:
api_file.parent.mkdir(parents=True, exist_ok=True)
api_file.unlink(missing_ok=True)

# NOTE: All the excludes only for sabing disk space.
# NOTE: All the excludes only for saving disk space.
json_text = api.model_dump_json(
by_alias=True,
mode="json",
Expand Down Expand Up @@ -1021,6 +1023,18 @@ def remove(self, package_id: str, version: str):
manifest_file = self.get_manifest_path(package_id, version)
manifest_file.unlink(missing_ok=True)

@contextmanager
def isolate_cache_changes(self):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with create_tempdir() as tmpdir:
packages_cache = tmpdir / "packages"
shutil.copytree(self.root, packages_cache)
try:
yield
finally:
# Restore.
shutil.rmtree(self.root)
shutil.copytree(packages_cache, self.root)


def _version_to_options(version: str) -> tuple[str, ...]:
if version.startswith("v"):
Expand All @@ -1043,6 +1057,7 @@ class DependencyVersionMap(dict[str, "ProjectManager"]):

def __init__(self, name: str):
self._name = name
super().__init__()

@log_instead_of_fail(default="<DependencyVersionMap>")
def __repr__(self) -> str:
Expand Down Expand Up @@ -1507,7 +1522,7 @@ def install(self, **dependency: Any) -> Union[Dependency, list[Dependency]]:
Args:
**dependency: Dependency data, same to what you put in `dependencies:` config.
When excluded, installs all project-specified dependencies. Also, use
``use_cache=False`` to force a re-install.
``use_cache=False`` to force re-installing.

Returns:
:class:`~ape.managers.project.Dependency` when given data else a list
Expand All @@ -1520,7 +1535,7 @@ def install(self, **dependency: Any) -> Union[Dependency, list[Dependency]]:
# Install all project's.
result: list[Dependency] = []

# Log the errors as they happen but don't crash the full install.
# Log the errors as they happen but don't crash the full installation.
for dep in self.get_project_dependencies(use_cache=use_cache):
result.append(dep)

Expand Down
2 changes: 1 addition & 1 deletion src/ape/pytest/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def pytest_exception_interact(self, report, call):

def pytest_runtest_setup(self, item):
"""
By default insert isolation fixtures into each test cases list of fixtures
By default, insert isolation fixtures into each test cases list of fixtures
prior to actually executing the test case.

https://docs.pytest.org/en/6.2.x/reference.html#pytest.hookspec.pytest_runtest_setup
Expand Down
13 changes: 7 additions & 6 deletions src/ape_cache/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import click

from ape.cli import ape_cli_context
from ape.cli.commands import ConnectedProviderCommand
from ape.cli.options import network_option
from ape.logging import logger
Expand All @@ -25,8 +26,9 @@ def cli():


@cli.command(short_help="Initialize a new cache database")
@ape_cli_context()
@network_option(required=True)
def init(ecosystem, network):
def init(cli_ctx, ecosystem, network):
"""
Initializes an SQLite database and creates a file to store data
from the provider.
Expand Down Expand Up @@ -63,8 +65,9 @@ def query(query_str):


@cli.command(short_help="Purges entire database")
@ape_cli_context()
@network_option(required=True)
def purge(ecosystem, network):
def purge(cli_ctx, ecosystem, network):
"""
Purges data from the selected database instance.

Expand All @@ -76,7 +79,5 @@ def purge(ecosystem, network):
purge the database of choice.
"""

ecosystem_name = network.ecosystem.name
network_name = network.name
get_engine().purge_database(ecosystem_name, network_name)
logger.success(f"Caching database purged for {ecosystem_name}:{network_name}.")
get_engine().purge_database(ecosystem.name, network.name)
logger.success(f"Caching database purged for {ecosystem.name}:{network.name}.")
3 changes: 1 addition & 2 deletions src/ape_compile/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,9 @@ def cli(
project.dependencies
) > 0:
for dependency in project.dependencies:
# Even if compiling we failed, we at least tried
# Even if compiling failed, we at least tried,
# and so we don't need to warn "Nothing to compile".
compiled = True

try:
contract_types = dependency.project.load_contracts(use_cache=use_cache)
except Exception as err:
Expand Down
2 changes: 1 addition & 1 deletion src/ape_ethereum/ecosystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ def str_to_slot(text):
# TODO perf: use a batch call here when ape adds support
storage = self.provider.get_storage(address, slot)
except APINotImplementedError:
continue
break

if sum(storage) == 0:
continue
Expand Down
2 changes: 1 addition & 1 deletion src/ape_pm/dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def fetch(self, destination: Path):

class GithubDependency(DependencyAPI):
"""
A dependency from Github. Use the ``github`` key in your ``dependencies:``
A dependency from GitHub. Use the ``github`` key in your ``dependencies:``
section of your ``ape-config.yaml`` file to declare a dependency from GitHub.

Config example::
Expand Down
63 changes: 23 additions & 40 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@

import pytest
from click.testing import CliRunner
from ethpm_types import ContractType

import ape
from ape.contracts import ContractContainer
from ape.logging import LogLevel, logger
from ape.managers.project import Project
from ape.pytest.config import ConfigWrapper
Expand All @@ -23,6 +21,7 @@
from ape.types.units import CurrencyValue
from ape.utils.basemodel import only_raise_attribute_error
from ape.utils.misc import ZERO_ADDRESS
from ape.utils.os import create_tempdir
from ape.utils.testing import DEFAULT_TEST_CHAIN_ID

# Needed to test tracing support in core `ape test` command.
Expand All @@ -34,12 +33,11 @@

# Ensure we don't persist any .ape data or using existing.

_DATA_FOLDER_CTX = ape.config.isolate_data_folder()
_DATA_FOLDER_CTX = ape.config.isolate_data_folder(keep="packages")
DATA_FOLDER = _DATA_FOLDER_CTX.__enter__()
ape.config.DATA_FOLDER = DATA_FOLDER
SHARED_CONTRACTS_FOLDER = (
Path(__file__).parent / "functional" / "data" / "contracts" / "ethereum" / "local"
)
TESTS_FOLDER = Path(__file__).parent
FUNCTIONAL_TESTS_FOLDER = TESTS_FOLDER / "functional"
SHARED_CONTRACTS_FOLDER = FUNCTIONAL_TESTS_FOLDER / "data" / "contracts"


EXPECTED_MYSTRUCT_C = 244
Expand Down Expand Up @@ -282,8 +280,16 @@ def empty_data_folder():
if "global_config" in (ape.config.__dict__ or {}):
del ape.config.__dict__["global_config"]

shutil.rmtree(DATA_FOLDER, ignore_errors=True)
DATA_FOLDER.mkdir(parents=True, exist_ok=True)
packages = DATA_FOLDER / "packages"
with create_tempdir() as temp_dir:
temp_packages = temp_dir / "packages"
shutil.copytree(packages, temp_packages)

shutil.rmtree(DATA_FOLDER, ignore_errors=True)
DATA_FOLDER.mkdir(parents=True, exist_ok=True)
shutil.copytree(temp_packages, DATA_FOLDER / "packages")

shutil.rmtree(temp_packages, ignore_errors=True)
yield


Expand Down Expand Up @@ -611,35 +617,6 @@ def gas_tracker(config_wrapper):
return GasTracker(config_wrapper)


@pytest.fixture(scope="session")
def solidity_contract_type(get_contract_type) -> ContractType:
return get_contract_type("SolidityContract")


@pytest.fixture(scope="session")
def get_contract_type():
def fn(name: str) -> ContractType:
content = (SHARED_CONTRACTS_FOLDER / f"{name}.json").read_text(encoding="utf8")
return ContractType.model_validate_json(content)

return fn


@pytest.fixture(scope="session")
def solidity_contract_container(solidity_contract_type) -> ContractContainer:
return ContractContainer(contract_type=solidity_contract_type)


@pytest.fixture(scope="session")
def vyper_contract_type(get_contract_type) -> ContractType:
return get_contract_type("VyperContract")


@pytest.fixture(scope="session")
def vyper_contract_container(vyper_contract_type) -> ContractContainer:
return ContractContainer(contract_type=vyper_contract_type)


@pytest.fixture(scope="session")
def shared_contracts_folder():
return SHARED_CONTRACTS_FOLDER
Expand All @@ -651,5 +628,11 @@ def project_with_contracts(with_dependencies_project_path):


@pytest.fixture
def geth_contract(geth_account, vyper_contract_container, geth_provider):
return geth_account.deploy(vyper_contract_container, 0)
def temp_project(project):
with project.isolate_in_tempdir() as temp_project:
yield temp_project


@pytest.fixture
def geth_contract(geth_account, project, geth_provider):
return geth_account.deploy(project.VyperContract, 0)
Loading
Loading