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

✨ cli(make): Add test command for interactively creating new tests #950

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

raxhvl
Copy link
Contributor

@raxhvl raxhvl commented Nov 13, 2024

πŸ—’οΈ Description

Scaffold tests using CLI.

πŸ”— Related Issues

closes #926, closes #946

βœ… Checklist

  • All: Set appropriate labels for the changes.
  • All: Considered squashing commits to improve commit history.
  • All: Added an entry to CHANGELOG.md.
  • All: Considered updating the online docs in the ./docs/ directory.
  • Tests: All converted JSON/YML tests from ethereum/tests have been added to converted-ethereum-tests.txt.
  • Tests: A PR with removal of converted JSON/YML tests from ethereum/tests have been opened.
  • Tests: Included the type and version of evm t8n tool used to locally execute test cases: e.g., ref with commit hash or geth 1.13.1-stable-3f40e65.
  • Tests: Ran mkdocs serve locally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.

Copy link
Member

@danceratopz danceratopz left a comment

Choose a reason for hiding this comment

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

Love it, really fun to use. Few minor changes.

I'll get back to the fork options in a bit πŸ™‚

src/cli/make/cli.py Outdated Show resolved Hide resolved
src/cli/make/templates/blockchain_test.py.j2 Outdated Show resolved Hide resolved
src/cli/make/templates/state_test.py.j2 Outdated Show resolved Hide resolved
src/cli/make/templates/blockchain_test.py.j2 Outdated Show resolved Hide resolved
src/cli/make/templates/state_test.py.j2 Outdated Show resolved Hide resolved
src/cli/make/cli.py Outdated Show resolved Hide resolved
src/cli/make/cli.py Outdated Show resolved Hide resolved
src/cli/make/cli.py Outdated Show resolved Hide resolved
@danceratopz danceratopz self-assigned this Nov 18, 2024
@danceratopz danceratopz added type:feat type: Feature scope:gentest Scope: gentest CLI labels Nov 18, 2024
@raxhvl
Copy link
Contributor Author

raxhvl commented Nov 18, 2024

@danceratopz I have patched in all your feedback.

The make command group

I made a major change: Add a parent command called make. make_test was a make shift interface. This leaves room for future make commands ( test being on of the subcommands).

This also provides a nice UX (using click) . If an invalid subcommand is chosen, it throws an error and shows a list of valid subcommands. If no subcommand is present, it shows a list of valid subcommands to choose from (along with nice descriptions).

image

A bit of flare πŸ–ŒοΈ

Shows a quote when using this CLI. I know this a little extra. But I have pleasant memories of how Laravel would sprinkle quotes accross their dev CLI.

image

I can remove it if you think this is too much.

@raxhvl raxhvl marked this pull request as ready for review November 18, 2024 13:28
@raxhvl raxhvl changed the title ✨(Make): Test ✨cli(make): Add test command to interactively new create test files Nov 19, 2024
@raxhvl raxhvl changed the title ✨cli(make): Add test command to interactively new create test files ✨ cli(make): Add test command for interactively creating new tests Nov 19, 2024
Copy link
Member

@danceratopz danceratopz left a comment

Choose a reason for hiding this comment

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

Thanks, I really like the idea of a more generalized command structure!

As we discussed async, @spencer-tb proposed an eest namespace in #461. Inspired by uv I would personally propose a shorter top-level command et and add make as a sub-command of that. Then we'd have uv run et make test. I'm not a fan of using "make" without a top-level command (as GNU Make is a very popular command - GNU Make), but not if it's a sub-command. Should we add et in the scope of this PR with the sole sub-command et and then add other sub-commands in follow-up PRs?

I'm enjoying the flair πŸ–ŒοΈ, I'm not sure I like it in the context of make test, as I want to prominently see the module name I'm creating (and perhaps the command to fill it, including --until=<fork> if <fork> is a development fork). Could you remove the quotes from the make test output for now and we see where we can add something like this later on? But, feel free to leave src/cli/make/commands/quotes.py as part of the PR if you like.

There's a couple of comments below, I'm starting to have more ideas, but perhaps we should try and not get too carried away for now πŸ™‚

@@ -0,0 +1,23 @@
"""
A blockchain test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{eip_number}}).
Copy link
Member

Choose a reason for hiding this comment

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

Ooh, I think that was my mistake.

Suggested change
A blockchain test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{eip_number}}).
A blockchain test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{{eip_number}}).

@@ -0,0 +1,23 @@
"""
A state test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{eip_number}}).
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
A state test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{eip_number}}).
A state test for [EIP-{{eip_number}} {{eip_name}}](https://eips.ethereum.org/EIPS/eip-{{eip_number}}).

is saved in the appropriate directory with a rendered template using Jinja2.
"""

import os
Copy link
Member

Choose a reason for hiding this comment

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

To exit the command with an error we'll use sys.exit(1)

Suggested change
import os
import os
import sys

file_name = f"test_{eip_name.lower().replace(' ', '_')}.py"

directory_path = f"tests/{fork.lower()}/eip{eip_number}_{eip_name.lower().replace(' ', '_')}"
file_path = f"{directory_path}/{file_name}"
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't clobber the file if it exists, please check this runs as expected:

Suggested change
file_path = f"{directory_path}/{file_name}"
file_path = f"{directory_path}/{file_name}"
if file_path.exists():
click.echo(f"The target test module {file_path} already exists!", err=True)
sys.exit(1)

Copy link
Member

Choose a reason for hiding this comment

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

Ah sorry, this won't work as-is as file_path is a str, not a pathlib.Path

fork=fork,
eip_number=eip_number,
eip_name=eip_name,
test_name=eip_name.lower().replace(" ", "_"),
Copy link
Member

Choose a reason for hiding this comment

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

I think we should prompt users for a test_module and a test_name. They might want to run this wizard multiple times for the same EIP.

This has implications for the UX though. If the folder (e.g. tests/prague/eip123_example_x/) exists, it would be more user-friendly to provided the folder as the first argument and the corresponding prompts skipped? Feel free to keep it simple for now. Just ideas.

Copy link
Member

Choose a reason for hiding this comment

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

I think we should prompt users for a test_module and a test_name. They might want to run this wizard multiple times for the same EIP.

But perhaps only if the "default" test module doesn't exits? E.g. if tests/prague/eip124_exhibit_x/test_exhibit_x.py exits then prompt for a test_module and test_name, otherwise keep it simple.

@raxhvl
Copy link
Contributor Author

raxhvl commented Nov 20, 2024

re: command structure

Strongly agree with uv run et make test . I will do it this PR.

re: quotes

I wasn't too sure if this is the right place. We could either:

  1. I bring the config command evm_init under make (uv run et make config ) and add the quote there in a separate PR.

OR

  1. We remove the quote.py file for now. My fork will have the code and we could add it back when we find a right place for it. We shouldn't keep files that are not referenced, this is a critical repository.

For this CLI, we could show a message like so:

πŸŽ‰ Success! Test file created at: {file_path}

πŸ“ Get started with tests:  https://ethereum.github.io/execution-spec-tests/v3.0.0/writing_tests

β›½ To fill this test, run: `uv run fill {file_path} --until={fork}`

@danceratopz
Copy link
Member

Strongly agree with uv run et make test . I will do it this PR.
Nice!

re: quotes

1. I bring the config command `evm_init` under `make` (`uv run et make config` ) and add the quote there in a separate PR.

This sounds good! I would prefer uv run et make env to create env.yaml though.
In the future, I'd quite like to add the ability to add a (per-use) config profile. Perhaps we can use make config for this. We don't need to decide now, but env feels right for this specific file.

For this CLI, we could show a message like so:

πŸŽ‰ Success! Test file created at: {file_path}

πŸ“ Get started with tests:  https://ethereum.github.io/execution-spec-tests/v3.0.0/writing_tests

β›½ To fill this test, run: `uv run fill {file_path} --until={fork}`

This looks great.

Please note that, since we fixed the the test module path in the reviews above, fill will now give an error:

_______ ERROR at setup of test_exhibit_x[fork_Prague-blockchain_test] _________
src/pytest_plugins/spec_version_checker/spec_version_checker.py:70: in reference_spec
    return get_ref_spec_from_module(request.module)
src/pytest_plugins/spec_version_checker/spec_version_checker.py:59: in get_ref_spec_from_module
    raise Exception("Test doesn't define REFERENCE_SPEC_GIT_PATH and REFERENCE_SPEC_VERSION")
E   Exception: Test doesn't define REFERENCE_SPEC_GIT_PATH and REFERENCE_SPEC_VERSION

For now we can populate these expected global variables with dummy values to make allow fill to run on these tests. I don't think it's worth trying to retrieve the required hash from ethereum/EIPs from Github in make test as I think we should remove this feature from fill sooner rather than later. It's not maintained or actively used.

@raxhvl
Copy link
Contributor Author

raxhvl commented Nov 22, 2024

Done

  1. Wrapped this command inside et πŸ‘½
  2. The command throws an error if the file exists.
  3. Using pathlib
  4. Removed quotes from this CLI.
  5. Fetching the list of forks dynamically.

Pending

1. UX with test module name

I'm not too sure how this should be because I haven't had a lot of experience writing tests.

2. Ensure the new test file passes fill command.

Unsure how to get the fill to pass. But I'm looking into it.

@danceratopz
Copy link
Member

danceratopz commented Nov 22, 2024

Done

1. Wrapped this command inside `et` πŸ‘½

2. The command throws an error if the file exists.

3. Using `pathlib`

4. Removed quotes from this CLI.

5. Fetching the list of forks dynamically.

Niiice! The fully populated fork selection menu is so nice.

Pending

1. UX with test module name

I'm not too sure how this should be because I haven't had a lot of experience writing tests

We can leave this for now, gather feedback from the team & close collaborators and iterate in follow-up PR(s).

2. Ensure the new test file passes fill command.

Unsure how to get the fill to pass. But I'm looking into it.

Had a quick look, sorry to gatecrash.

It looks like the default to field in a Transaction is a contract, so we should explicitly set this in the template to a harmless value, e.g.,

     env = Environment()

    sender = pre.fund_eoa()

    tx = Transaction(to=sender, sender=sender)

    post: dict[str, dict] = {}

    state_test(env=env, pre=pre, post=post, tx=tx)

I don't understand why this triggers the exception it does (TransactionException.TYPE_4_TX_CONTRACT_CREATION) and complaining about a type 4 transaction here is particularly fishy. So if you want to dig deeper, go for it. If not, please just create an issue for this, I think it'd be worth understanding.

I got this far quite quickly by running the generated test with the --evm-dump-dir flag and checking the input objects to t8n:

 uv run fill tests/constantinople/eip123_x_of_y/test_x_of_y.py --until=Paris --evm-dump-dir=/tmp/tx4

More info here:
https://ethereum.github.io/execution-spec-tests/main/filling_tests/debugging_t8n_tools/#evm-dump-directory

Copy link
Member

@danceratopz danceratopz left a comment

Choose a reason for hiding this comment

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

Thanks a lot for adding et up front here. @spencer-tb gonna love it!

Just a couple of quick comments after trying the CLI out.

Comment on lines 24 to 25
DOCS_BASE_URL: str = f"https://ethereum.github.io/execution-spec-tests/v{AppConfig().version}"

# Documentation URLs prefixed with `DOCS_URL__` to avoid conflicts with other URLs
DOCS_URL__WRITING_TESTS: str = f"{DOCS_BASE_URL}/writing_tests/"
Copy link
Member

Choose a reason for hiding this comment

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

Great idea to use the config for this.

I think I'd prefer to point to main (for now) as we're pushing improvements to the docs much faster than we are releasing.


from ethereum_test_tools import Alloc, Block, BlockchainTestFiller, Transaction

REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md"
Copy link
Member

Choose a reason for hiding this comment

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

You could actually populate the correct information for REFERENCE_SPEC_GIT_PATH, but as mentioned, don't waste too much time as I'd prefer to remove this feature from EEST entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. I have created an issue to remove it #963.

Once we merge this, I will reference these lines in the issue.

@raxhvl
Copy link
Contributor Author

raxhvl commented Nov 23, 2024

Fill now passes for all forks. This PR is now ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope:gentest Scope: gentest CLI type:feat type: Feature
Projects
None yet
2 participants