Skip to content

refactor(fw): make exception classes kw_only dataclasses to improve testability #386

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

Merged
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
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- 💥 `StateTest`, spec format used to write tests, is now limited to a single transaction per test ([#361](https://github.com/ethereum/execution-spec-tests/pull/361))
- ✨ `StateTestOnly`, spec format is now available and its only difference with `StateTest` is that it does not produce a `BlockchainTest` ([#368](https://github.com/ethereum/execution-spec-tests/pull/368))
- ✨ Add a helper class `ethereum_test_tools.TestParameterGroup` to define test parameters as dataclasses and auto-generate test IDs ([#364](https://github.com/ethereum/execution-spec-tests/pull/364)).
- 🔀 Change custom exception classes to dataclasses to improve testability ([#386](https://github.com/ethereum/execution-spec-tests/pull/386)).

### 🔧 EVM Tools

Expand Down
34 changes: 26 additions & 8 deletions src/ethereum_test_tools/common/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ class Storage(SupportsJSON):
Dictionary type to be used when defining an input to initialize a storage.
"""

@dataclass(kw_only=True)
class InvalidType(Exception):
"""
Invalid type used when describing test's expected storage key or value.
Expand All @@ -278,6 +279,7 @@ def __str__(self):
"""Print exception string"""
return f"invalid type for key/value: {self.key_or_value}"

@dataclass(kw_only=True)
class InvalidValue(Exception):
"""
Invalid value used when describing test's expected storage key or
Expand All @@ -294,6 +296,7 @@ def __str__(self):
"""Print exception string"""
return f"invalid value for key/value: {self.key_or_value}"

@dataclass(kw_only=True)
class AmbiguousKeyValue(Exception):
"""
Key is represented twice in the storage.
Expand Down Expand Up @@ -326,6 +329,7 @@ def __str__(self):
s[{self.key_1}] = {self.val_1} and s[{self.key_2}] = {self.val_2}
"""

@dataclass(kw_only=True)
class MissingKey(Exception):
"""
Test expected to find a storage key set but key was missing.
Expand All @@ -341,6 +345,7 @@ def __str__(self):
"""Print exception string"""
return "key {0} not found in storage".format(Storage.key_value_to_string(self.key))

@dataclass(kw_only=True)
class KeyValueMismatch(Exception):
"""
Test expected a certain value in a storage key but value found
Expand Down Expand Up @@ -380,10 +385,10 @@ def parse_key_value(input: str | int | bytes | SupportsBytes) -> int:
elif isinstance(input, bytes) or isinstance(input, SupportsBytes):
input = int.from_bytes(bytes(input), "big")
else:
raise Storage.InvalidType(input)
raise Storage.InvalidType(key_or_value=input)

if input > MAX_STORAGE_KEY_VALUE or input < MIN_STORAGE_KEY_VALUE:
raise Storage.InvalidValue(input)
raise Storage.InvalidValue(key_or_value=input)
return input

@staticmethod
Expand Down Expand Up @@ -460,7 +465,9 @@ def __json__(self, encoder: JSONEncoder) -> Mapping[str, str]:
key_repr = Storage.key_value_to_string(key)
val_repr = Storage.key_value_to_string(self.data[key])
if key_repr in res and val_repr != res[key_repr]:
raise Storage.AmbiguousKeyValue(key_repr, res[key_repr], key, val_repr)
raise Storage.AmbiguousKeyValue(
key_1=key_repr, val_1=res[key_repr], key_2=key, val_2=val_repr
)
res[key_repr] = val_repr
return res

Expand Down Expand Up @@ -490,9 +497,11 @@ def must_contain(self, address: str, other: "Storage"):
if key not in self.data:
# storage[key]==0 is equal to missing storage
if other[key] != 0:
raise Storage.MissingKey(key)
raise Storage.MissingKey(key=key)
elif self.data[key] != other.data[key]:
raise Storage.KeyValueMismatch(address, key, self.data[key], other.data[key])
raise Storage.KeyValueMismatch(
address=address, key=key, got=self.data[key], want=other.data[key]
)

def must_be_equal(self, address: str, other: "Storage"):
"""
Expand All @@ -501,16 +510,22 @@ def must_be_equal(self, address: str, other: "Storage"):
# Test keys contained in both storage objects
for key in self.data.keys() & other.data.keys():
if self.data[key] != other.data[key]:
raise Storage.KeyValueMismatch(address, key, self.data[key], other.data[key])
raise Storage.KeyValueMismatch(
address=address, key=key, got=self.data[key], want=other.data[key]
)

# Test keys contained in either one of the storage objects
for key in self.data.keys() ^ other.data.keys():
if key in self.data:
if self.data[key] != 0:
raise Storage.KeyValueMismatch(address, key, self.data[key], 0)
raise Storage.KeyValueMismatch(
address=address, key=key, want=self.data[key], got=0
)

elif other.data[key] != 0:
raise Storage.KeyValueMismatch(address, key, 0, other.data[key])
raise Storage.KeyValueMismatch(
address=address, key=key, want=0, got=other.data[key]
)


@dataclass(kw_only=True)
Expand Down Expand Up @@ -573,6 +588,7 @@ class Account:
state.
"""

@dataclass(kw_only=True)
class NonceMismatch(Exception):
"""
Test expected a certain nonce value for an account but a different
Expand All @@ -596,6 +612,7 @@ def __str__(self):
+ f"want {self.want}, got {self.got}"
)

@dataclass(kw_only=True)
class BalanceMismatch(Exception):
"""
Test expected a certain balance for an account but a different
Expand All @@ -619,6 +636,7 @@ def __str__(self):
+ f"want {self.want}, got {self.got}"
)

@dataclass(kw_only=True)
class CodeMismatch(Exception):
"""
Test expected a certain bytecode for an account but a different
Expand Down
1 change: 1 addition & 0 deletions whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ substring
sudo
t8n
tamasfe
testability
TestAddress
TestContractCreationGasUsage
TestMultipleWithdrawalsSameAddress
Expand Down