Skip to content

Commit 5291ca2

Browse files
committed
isolate_tests: Extract code blocks from documentation using code block header
1 parent f62b805 commit 5291ca2

File tree

6 files changed

+59
-60
lines changed

6 files changed

+59
-60
lines changed

docs/contributing.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ from the documentation or the other tests:
324324
# extract from tests:
325325
path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp
326326
# extract from documentation:
327-
path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs
327+
path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs
328328
329329
The AFL documentation states that the corpus (the initial input files) should not be
330330
too large. The files themselves should not be larger than 1 kB and there should be

scripts/docs_version_pragma_check.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ SOLTMPDIR=$(mktemp -d)
136136
(
137137
set -e
138138
cd "$SOLTMPDIR"
139-
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
139+
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/
140140

141141
getAllAvailableVersions
142142

scripts/isolate_tests.py

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import re
1111
import os
1212
import hashlib
13-
from os.path import join, isfile, split
13+
from os.path import join, isfile, split, basename
14+
from argparse import ArgumentParser
1415

1516
def extract_test_cases(path):
1617
with open(path, encoding="utf8", errors='ignore', mode='r', newline='') as file:
@@ -35,54 +36,50 @@ def extract_test_cases(path):
3536

3637
return tests
3738

38-
# Contract sources are indented by 4 spaces.
39-
# Look for `pragma solidity`, `contract`, `library` or `interface`
40-
# and abort a line not indented properly.
39+
# Extract code examples based on a start marker
40+
# up until we reach EOF or a line that is not empty and doesn't start with 4
41+
# spaces.
4142
def extract_docs_cases(path):
43+
beginMarkers = ['.. code-block:: solidity', '::']
44+
immediatelyAfterMarker = False
4245
insideBlock = False
43-
insideBlockParameters = False
44-
pastBlockParameters = False
45-
extractedLines = []
4646
tests = []
4747

4848
# Collect all snippets of indented blocks
49-
5049
with open(path, mode='r', errors='ignore', encoding='utf8', newline='') as f:
5150
lines = f.read().splitlines()
52-
for l in lines:
53-
if l != '':
54-
if not insideBlock and l.startswith(' '):
55-
# start new test
56-
extractedLines += ['']
57-
insideBlockParameters = False
58-
pastBlockParameters = False
59-
insideBlock = l.startswith(' ')
51+
52+
for line in lines:
6053
if insideBlock:
61-
if not pastBlockParameters:
62-
# NOTE: For simplicity this allows blank lines between block parameters even
63-
# though Sphinx does not. This does not matter since the first non-empty line in
64-
# a Solidity file cannot start with a colon anyway.
65-
if not l.strip().startswith(':') and (l != '' or not insideBlockParameters):
66-
insideBlockParameters = False
67-
pastBlockParameters = True
68-
else:
69-
insideBlockParameters = True
70-
71-
if not insideBlockParameters:
72-
extractedLines[-1] += l + '\n'
54+
if immediatelyAfterMarker:
55+
# Skip Sphinx instructions and empty lines between them
56+
if line == '' or line.lstrip().startswith(":"):
57+
continue
58+
59+
if line == '' or line.startswith(" "):
60+
tests[-1] += line + "\n"
61+
immediatelyAfterMarker = False
62+
else:
63+
insideBlock = False
64+
elif any(map(line.lower().startswith, beginMarkers)):
65+
insideBlock = True
66+
immediatelyAfterMarker = True
67+
tests += ['']
7368

7469
codeStart = "(// SPDX-License-Identifier:|pragma solidity|contract.*{|library.*{|interface.*{)"
7570

76-
# Filter all tests that do not contain Solidity or are indented incorrectly.
77-
for lines in extractedLines:
78-
if re.search(r'^\s{0,3}' + codeStart, lines, re.MULTILINE):
71+
for test in tests:
72+
if re.search(r'^\s{0,3}' + codeStart, test, re.MULTILINE):
7973
print("Indentation error in " + path + ":")
80-
print(lines)
74+
print(test)
8175
exit(1)
82-
if re.search(r'^\s{4}' + codeStart, lines, re.MULTILINE):
83-
tests.append(lines)
8476

85-
return tests
77+
# Filter out tests that are not supposed to be compilable.
78+
return [
79+
test.lstrip("\n")
80+
for test in tests
81+
if re.search(r'^\s{4}' + codeStart, test, re.MULTILINE) is not None
82+
]
8683

8784
def write_cases(f, tests):
8885
cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower()
@@ -94,39 +91,38 @@ def write_cases(f, tests):
9491
with open(sol_filename, mode='w', encoding='utf8', newline='') as fi:
9592
fi.write(remainder)
9693

97-
def extract_and_write(f, path):
98-
if docs:
94+
def extract_and_write(path):
95+
if path.lower().endswith('.rst'):
9996
cases = extract_docs_cases(path)
97+
elif path.endswith('.sol'):
98+
with open(path, mode='r', encoding='utf8', newline='') as f:
99+
cases = [f.read()]
100100
else:
101-
if f.endswith('.sol'):
102-
with open(path, mode='r', encoding='utf8', newline='') as _f:
103-
cases = [_f.read()]
104-
else:
105-
cases = extract_test_cases(path)
106-
write_cases(f, cases)
101+
cases = extract_test_cases(path)
102+
103+
write_cases(basename(path), cases)
107104

108105
if __name__ == '__main__':
109-
if len(sys.argv) == 1:
110-
print("Usage: " + sys.argv[0] + " path-to-file-or-folder-to-extract-code-from [docs]")
111-
exit(1)
106+
script_description = (
107+
"Reads Solidity, C++ or RST source files and extracts compilable solidity and yul code blocks from them. "
108+
"Can be used to generate test cases to validade code examples. "
109+
)
112110

113-
path = sys.argv[1]
114-
docs = False
115-
if len(sys.argv) > 2 and sys.argv[2] == 'docs':
116-
docs = True
111+
parser = ArgumentParser(description=script_description)
112+
parser.add_argument(dest='path', help='Path to file or directory to look for code in.')
113+
options = parser.parse_args()
114+
path = options.path
117115

118116
if isfile(path):
119-
_, tail = split(path)
120-
extract_and_write(tail, path)
117+
extract_and_write(path)
121118
else:
122119
for root, subdirs, files in os.walk(path):
123120
if '_build' in subdirs:
124121
subdirs.remove('_build')
125122
if 'compilationTests' in subdirs:
126123
subdirs.remove('compilationTests')
127124
for f in files:
128-
_, tail = split(f)
129-
if tail == "invalid_utf8_sequence.sol":
125+
if basename(f) == "invalid_utf8_sequence.sol":
130126
continue # ignore the test with broken utf-8 encoding
131127
path = join(root, f)
132-
extract_and_write(f, path)
128+
extract_and_write(path)

scripts/wasm-rebuild/docker-scripts/isolate_tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#!/usr/bin/env python2
2+
#
3+
# Not actively tested or maintained. Exists in case we want to rebuild an
4+
# ancient release.
25

36
import sys
47
import re

test/cmdlineTests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ SOLTMPDIR=$(mktemp -d)
361361
(
362362
set -e
363363
cd "$SOLTMPDIR"
364-
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
364+
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/
365365
developmentVersion=$("$REPO_ROOT/scripts/get_version.sh")
366366

367367
for f in *.sol
@@ -510,7 +510,7 @@ SOLTMPDIR=$(mktemp -d)
510510
set -e
511511
cd "$SOLTMPDIR"
512512
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/
513-
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
513+
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/
514514

515515
echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --quiet --input-files
516516
echo ./*.sol | xargs -P 4 -n 50 "${SOLIDITY_BUILD_DIR}/test/tools/solfuzzer" --without-optimizer --quiet --input-files

test/docsCodeStyle.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ SOLTMPDIR=$(mktemp -d)
2222
(
2323
set -e
2424
cd "$SOLTMPDIR"
25-
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
25+
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/
2626

2727
if npm -v >/dev/null 2>&1; then
2828
if npm list -g | grep solhint >/dev/null 2>&1; then

0 commit comments

Comments
 (0)