Skip to content

Commit

Permalink
refactor(fw): make exception classes kw_only dataclasses to improve…
Browse files Browse the repository at this point in the history
… testability (ethereum#386)
  • Loading branch information
danceratopz authored Jan 18, 2024
1 parent 8c7cb87 commit afb3164
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 8 deletions.
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

0 comments on commit afb3164

Please sign in to comment.