-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Testing Natspec JSON via isoltest #14433
Comments
As a first step, we need to extract source code and expectations from The script itself will not be useful after we run it once so I will not be submitting it as a part of my upcoming PR. It might be still serve as a template for writing similar one-off parsers though so I'm posting it here for future reference. Parser scriptScript expects the content of
The script will create an input Error expectations are ignored - the current tests do not specify error messages and I will make JSON files are passed through Python's JSON parser to convert them to a uniform style. For Solidity files indentation is converted to our usual style. #!/usr/bin/env python
import json
import re
from textwrap import dedent
from textwrap import indent
import sys
import pyparsing as pp
pp.ParserElement.inlineLiteralsUsing(pp.Suppress)
CPP_STRING = (
pp.QuotedString(quote_char='"', esc_char='\\', multiline=True)
| pp.QuotedString(quote_char='R"(', end_quote_char=')"', multiline=True)
| pp.QuotedString(quote_char='R"X(', end_quote_char=')X"', multiline=True)
| pp.QuotedString(quote_char='R"R(', end_quote_char=')R"', multiline=True)
| pp.QuotedString(quote_char='R"ABCDEF(', end_quote_char=')ABCDEF"', multiline=True)
);
IDENTIFIER = pp.Word(pp.identchars + pp.nums)
BOOL_LITERAL = pp.Literal('true') | pp.Literal('false')
STRING_VARIABLE = pp.Group(
pp.Suppress('char') - 'const' - '*'
- IDENTIFIER('variable_name') - '='
- pp.Group(CPP_STRING[...])('content') - ';'
)
CHECK_NATSPEC_CALL = pp.Group(
pp.Suppress('checkNatspec') - '('
- IDENTIFIER('source_variable_name') - ','
- CPP_STRING('contract_name') - ','
- IDENTIFIER('natspec_variable_name') - ','
- BOOL_LITERAL('userdoc_flag')
- ')' - ';'
)
EXPECT_NATSPEC_ERROR_CALL = pp.Group(
pp.Suppress('expectNatspecError') - '('
- IDENTIFIER('source_variable_name')
- ')' - ';'
)
TEST_STEP = (
STRING_VARIABLE('string_variable')
| CHECK_NATSPEC_CALL('positive_expectation')
| EXPECT_NATSPEC_ERROR_CALL('negative_expectation')
)
TEST_BODY = '{' - TEST_STEP[...]('steps') + '}'
TEST_HEADER = pp.Suppress('BOOST_AUTO_TEST_CASE') - '(' - IDENTIFIER('name') - ')'
NATSPEC_TEST = pp.Group(TEST_HEADER - TEST_BODY)
TEST_FILE = NATSPEC_TEST[...]
def format_error(exception):
line_number_prefix = f"{exception.lineno} | "
return dedent(f"""\
{exception}
{line_number_prefix}{exception.line}
{' ':{len(line_number_prefix) + exception.col - 1}}^
""")
try:
ast = TEST_FILE.parse_string(sys.stdin.read(), parse_all=True)
except pp.ParseException as exception:
sys.exit(format_error(exception))
except pp.ParseSyntaxException as exception:
sys.exit(format_error(exception))
for test_case in ast:
variables = {
step.variable_name: dedent(''.join(step.content))
for step in test_case.steps
if step.get_name() == 'string_variable'
}
expectations = {'userdoc': {}, 'devdoc': {}}
for step in test_case.steps:
if step.get_name() == 'positive_expectation':
assert step.source_variable_name == 'sourceCode'
expectations[step.contract_name] = expectations.get(step.contract_name, {})
expectation_kind = 'userdoc' if step.userdoc_flag == 'true' else 'devdoc'
assert expectation_kind not in expectations[step.contract_name]
expectations[step.contract_name][expectation_kind] = json.loads(variables[step.natspec_variable_name])
input_file_name = test_case.name + '.sol'
print(f'{input_file_name}')
with open(input_file_name, 'w') as input_file:
input_file.write(
variables['sourceCode']
.strip()
.replace('\t', ' ')
.replace(' ', ' ')
+ '\n\n'
)
input_file.write('// ----\n')
first = True
for contract, expectation_kinds in expectations.items():
for expectation_kind, expectation_json in sorted(expectation_kinds.items()):
if not first:
input_file.write(f"//\n")
first = False
formatted_json = re.sub(
r'^((\s*)".*?":)\s*([\[{])($|[^\]}])', r'\1\n\2\3\4',
json.dumps(expectation_json, indent=4, sort_keys=True),
flags=re.MULTILINE
)
input_file.write(f"// :{contract} {expectation_kind}\n")
input_file.write(indent(formatted_json + '\n', prefix='// ')) |
Currently Natspec is tested by
SolidityNatspecJSON.cpp
, which contains Boost tests. Such tests are harder to maintain than our isoltest test cases. This makes us reluctant to add more of them and leads to insufficient coverage and bugs like #14430.The task here is to create a new isoltest test case for Natspec and covert existing test cases to it.
The text was updated successfully, but these errors were encountered: