Skip to content

Commit a3ecae1

Browse files
committed
Manifest: added proof runtimes
Recorded runtime details for all existing proofs Proofs running longer than 1 minute will not be run in CI Signed-off-by: Andrew Helwer <[email protected]>
1 parent 3c0c83b commit a3ecae1

File tree

40 files changed

+326
-222
lines changed

40 files changed

+326
-222
lines changed

.github/scripts/check_manifest_features.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,19 @@ def check_features(parser, queries, manifest, examples_root):
164164
if parse_err:
165165
success = False
166166
logging.error(f'Module {module["path"]} contains syntax errors')
167-
expected_features = get_tree_features(tree, queries)
167+
module_features = get_tree_features(tree, queries)
168+
expected_features = module_features - {'proof'}
168169
actual_features = set(module['features'])
169170
if expected_features != actual_features:
170171
success = False
171172
logging.error(
172173
f'Module {module["path"]} has incorrect features in manifest; '
173174
+ f'expected {list(expected_features)}, actual {list(actual_features)}'
174175
)
175-
expected_imports = get_community_imports(examples_root, tree, text, dirname(module_path), 'proof' in expected_features, queries)
176+
if 'proof' in module_features and 'proof' not in module:
177+
success = False
178+
logging.error(f'Module {module["path"]} contains proof but no proof runtime details in manifest')
179+
expected_imports = get_community_imports(examples_root, tree, text, dirname(module_path), 'proof' in module_features, queries)
176180
actual_imports = set(module['communityDependencies'])
177181
if expected_imports != actual_imports:
178182
success = False
@@ -192,7 +196,7 @@ def check_features(parser, queries, manifest, examples_root):
192196

193197
if __name__ == '__main__':
194198
parser = ArgumentParser(description='Checks metadata in manifest.json files against module and model files in repository.')
195-
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=True)
199+
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=False, default='.')
196200
args = parser.parse_args()
197201

198202
manifest = tla_utils.load_all_manifests(args.examples_root)

.github/scripts/check_manifest_files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import tla_utils
1313

1414
parser = ArgumentParser(description='Checks manifests against module and model files in repository.')
15-
parser.add_argument('--ci_ignore_path', help='Path to the .ciignore file', required=True)
15+
parser.add_argument('--ci_ignore_path', help='Path to the .ciignore file', required=False, default='./.ciignore')
1616
args = parser.parse_args()
1717

1818
ci_ignore_path = normpath(args.ci_ignore_path)

.github/scripts/check_manifest_schema.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import tla_utils
1616

1717
parser = ArgumentParser(description='Checks tlaplus/examples manifest.json files against JSON schema file.')
18-
parser.add_argument('--schema_path', help='Path to the tlaplus/examples manifest-schema.json file', required=True)
18+
parser.add_argument('--schema_path', help='Path to the tlaplus/examples manifest-schema.json file', required=False, default='manifest-schema.json')
1919
args = parser.parse_args()
2020

2121
examples_root = dirname(args.schema_path)

.github/scripts/check_markdown_table.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,15 @@ def from_json(path, spec):
6767
path,
6868
set(spec['authors']),
6969
'beginner' in spec['tags'],
70-
any([module for module in spec['modules'] if 'proof' in module['features']]),
70+
any([module for module in spec['modules'] if 'proof' in module]),
7171
any([module for module in spec['modules'] if 'pluscal' in module['features']]),
7272
any([model for module in spec['modules'] for model in module['models'] if model['mode'] != 'symbolic']),
7373
any([model for module in spec['modules'] for model in module['models'] if model['mode'] == 'symbolic']),
7474
spec
7575
)
7676

7777
parser = ArgumentParser(description='Validates the spec table in README.md against the manifest.json.')
78-
parser.add_argument('--readme_path', help='Path to the tlaplus/examples README.md file', required=True)
78+
parser.add_argument('--readme_path', help='Path to the tlaplus/examples README.md file', required=False, default='./README.md')
7979
args = parser.parse_args()
8080

8181
manifest = tla_utils.load_all_manifests(dirname(args.readme_path))

.github/scripts/check_proofs.py

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
"""
44

55
from argparse import ArgumentParser
6+
from datetime import timedelta
67
from os.path import dirname, join, normpath
78
import logging
89
import subprocess
910
from timeit import default_timer as timer
1011
import tla_utils
1112

1213
parser = ArgumentParser(description='Validate all proofs in all modules with TLAPM.')
13-
parser.add_argument('--tlapm_path', help='Path to TLAPM install dir; should have bin and lib subdirs', required=True)
14-
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=True)
14+
parser.add_argument('--tlapm_path', help='Path to TLAPM install dir; should have bin and lib subdirs', required=False, default = 'deps/tlapm')
15+
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=False, default='.')
16+
parser.add_argument('--runtime_seconds_limit', help='Only run proofs with expected runtime less than this value', required=False, default=60)
1517
parser.add_argument('--skip', nargs='+', help='Space-separated list of .tla modules to skip checking', required=False, default=[])
1618
parser.add_argument('--only', nargs='+', help='If provided, only check proofs in this space-separated list', required=False, default=[])
1719
parser.add_argument('--verbose', help='Set logging output level to debug', action='store_true')
@@ -22,46 +24,68 @@
2224
manifest = tla_utils.load_all_manifests(examples_root)
2325
skip_modules = args.skip
2426
only_modules = args.only
27+
hard_timeout_in_seconds = args.runtime_seconds_limit * 2
2528

2629
logging.basicConfig(level = logging.DEBUG if args.verbose else logging.INFO)
2730

28-
proof_module_paths = [
29-
module['path']
30-
for path, spec in manifest
31-
for module in spec['modules']
32-
if 'proof' in module['features']
33-
and module['path'] not in skip_modules
34-
and (only_modules == [] or module['path'] in only_modules)
35-
]
31+
proof_module_paths = sorted(
32+
[
33+
(manifest_dir, spec, module, runtime)
34+
for manifest_dir, spec in manifest
35+
for module in spec['modules']
36+
if 'proof' in module
37+
and (runtime := tla_utils.parse_timespan(module['proof']['runtime'])) <= timedelta(seconds = args.runtime_seconds_limit)
38+
and module['path'] not in skip_modules
39+
and (only_modules == [] or module['path'] in only_modules)
40+
],
41+
key = lambda m : m[3]
42+
)
3643

3744
for path in skip_modules:
3845
logging.info(f'Skipping {path}')
3946

4047
success = True
4148
tlapm_path = join(tlapm_path, 'bin', 'tlapm')
42-
for module_path in proof_module_paths:
49+
for manifest_dir, spec, module, expected_runtime in proof_module_paths:
50+
module_path = module['path']
4351
logging.info(module_path)
4452
start_time = timer()
45-
module_path = tla_utils.from_cwd(examples_root, module_path)
46-
module_dir = dirname(module_path)
47-
tlapm = subprocess.run(
48-
[
49-
tlapm_path, module_path,
50-
'-I', module_dir,
51-
'--stretch', '5'
52-
],
53-
stdout=subprocess.PIPE,
54-
stderr=subprocess.STDOUT,
55-
text=True
56-
)
57-
end_time = timer()
58-
logging.info(f'Checked proofs in {end_time - start_time:.1f}s')
59-
if tlapm.returncode != 0:
60-
logging.error(f'Proof checking failed in {module_path}:')
61-
logging.error(tlapm.stdout)
53+
full_module_path = tla_utils.from_cwd(examples_root, module_path)
54+
module_dir = dirname(full_module_path)
55+
try:
56+
tlapm_result = subprocess.run(
57+
[
58+
tlapm_path, full_module_path,
59+
'-I', module_dir,
60+
'--stretch', '5'
61+
],
62+
stdout = subprocess.PIPE,
63+
stderr = subprocess.STDOUT,
64+
text = True,
65+
timeout = hard_timeout_in_seconds
66+
)
67+
end_time = timer()
68+
actual_runtime = timedelta(seconds = end_time - start_time)
69+
output = ' '.join(tlapm_result.args) + '\n' + tlapm_result.stdout
70+
logging.info(f'Checked proofs in {tla_utils.format_timespan(actual_runtime)} vs. {tla_utils.format_timespan(expected_runtime)} expected')
71+
if tlapm_result.returncode != 0:
72+
logging.error(f'Proof checking failed for {module_path}:')
73+
logging.error(output)
74+
success = False
75+
else:
76+
if 'proof' not in module or module['proof']['runtime'] == 'unknown':
77+
module['proof'] = { 'runtime' : tla_utils.format_timespan(actual_runtime) }
78+
manifest_path = join(manifest_dir, 'manifest.json')
79+
tla_utils.write_json(spec, manifest_path)
80+
logging.debug(output)
81+
except subprocess.TimeoutExpired as tlapm_result:
82+
# stdout is a string on Windows, byte array everywhere else
83+
stdout = tlapm_result.stdout if type(tlapm_result.stdout) == str else tlapm_result.stdout.decode('utf-8')
84+
args, timeout = tlapm_result.args
85+
logging.error(f'{module_path} hit hard timeout of {timeout} seconds')
86+
output = ' '.join(args) + '\n' + stdout
87+
logging.error(output)
6288
success = False
63-
else:
64-
logging.debug(tlapm.stdout)
6589

6690
exit(0 if success else 1)
6791

.github/scripts/check_small_models.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
import tla_utils
1414

1515
parser = ArgumentParser(description='Checks all small TLA+ models in the tlaplus/examples repo using TLC.')
16-
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=True)
17-
parser.add_argument('--apalache_path', help='Path to the Apalache directory', required=True)
18-
parser.add_argument('--tlapm_lib_path', help='Path to the TLA+ proof manager module directory; .tla files should be in this directory', required=True)
19-
parser.add_argument('--community_modules_jar_path', help='Path to the CommunityModules-deps.jar file', required=True)
20-
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=True)
16+
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=False, default='deps/tools/tla2tools.jar')
17+
parser.add_argument('--apalache_path', help='Path to the Apalache directory', required=False, default='deps/apalache')
18+
parser.add_argument('--tlapm_lib_path', help='Path to the TLA+ proof manager module directory; .tla files should be in this directory', required=False, default='deps/tlapm/library')
19+
parser.add_argument('--community_modules_jar_path', help='Path to the CommunityModules-deps.jar file', required=False, default='deps/community/modules.jar')
20+
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=False, default='.')
2121
parser.add_argument('--skip', nargs='+', help='Space-separated list of models to skip checking', required=False, default=[])
2222
parser.add_argument('--only', nargs='+', help='If provided, only check models in this space-separated list', required=False, default=[])
2323
parser.add_argument('--verbose', help='Set logging output level to debug', action='store_true')
@@ -78,9 +78,11 @@ def check_model(module, model, expected_runtime):
7878
logging.debug(output)
7979
return True
8080
case TimeoutExpired():
81-
args, _ = tlc_result.args
82-
output = ' '.join(args) + '\n' + tlc_result.stdout
83-
logging.error(f'{model_path} hit hard timeout of {hard_timeout_in_seconds} seconds')
81+
args, timeout = tlc_result.args
82+
# stdout is a string on Windows, byte array everywhere else
83+
stdout = tlc_result.stdout if type(tlc_result.stdout) == str else tlc_result.stdout.decode('utf-8')
84+
output = ' '.join(args) + '\n' + stdout
85+
logging.error(f'{model_path} hit hard timeout of {timeout} seconds')
8486
logging.error(output)
8587
return False
8688
case _:

.github/scripts/format_markdown_table.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from mistletoe.markdown_renderer import MarkdownRenderer
1414

1515
parser = ArgumentParser(description='Formats or modifies the spec table in README.md.')
16-
parser.add_argument('--readme_path', help='Path to the tlaplus/examples README.md file', required=True)
16+
parser.add_argument('--readme_path', help='Path to the tlaplus/examples README.md file', required=False, default='README.md')
1717
args = parser.parse_args()
1818

1919
columns = ['name', 'authors', 'beginner', 'proof', 'tlc', 'pcal', 'apalache']

.github/scripts/generate_manifest.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,20 @@ def get_tla_files(examples_root, dir_path):
3535
Gets paths of all .tla files in the given directory, except for error
3636
trace specs.
3737
"""
38-
return [
39-
path for path in glob.glob(f'{dir_path}/**/*.tla', root_dir=examples_root, recursive=True)
38+
return sorted(
39+
path
40+
for path in glob.glob(f'{dir_path}/**/*.tla', root_dir=examples_root, recursive=True)
4041
if '_TTrace_' not in path
42+
)
43+
44+
def get_tla_file_features(examples_root, dir_path, parser, queries):
45+
"""
46+
Gets paths of all .tla files in a given directory, along with their
47+
features.
48+
"""
49+
return [
50+
(path, get_module_features(examples_root, path, parser, queries))
51+
for path in get_tla_files(examples_root, dir_path)
4152
]
4253

4354
def get_cfg_files(examples_root, tla_path):
@@ -73,7 +84,7 @@ def generate_new_manifest(examples_root, spec_path, spec_name, parser, queries):
7384
{
7485
'path': tla_utils.to_posix(tla_path),
7586
'communityDependencies': sorted(list(get_community_module_imports(examples_root, parser, tla_path, queries))),
76-
'features': sorted(list(get_module_features(examples_root, tla_path, parser, queries))),
87+
'features': sorted(list(module_features - {'proof'})),
7788
'models': [
7889
{
7990
'path': tla_utils.to_posix(cfg_path),
@@ -83,8 +94,8 @@ def generate_new_manifest(examples_root, spec_path, spec_name, parser, queries):
8394
}
8495
for cfg_path in sorted(get_cfg_files(examples_root, tla_path))
8596
]
86-
}
87-
for tla_path in sorted(get_tla_files(examples_root, spec_path))
97+
} | ({'proof' : {'runtime': 'unknown'}} if 'proof' in module_features else {})
98+
for tla_path, module_features in get_tla_file_features(examples_root, spec_path, parser, queries)
8899
]
89100
}
90101

@@ -107,9 +118,13 @@ def find_corresponding_module(old_module, new_spec):
107118
return modules[0] if any(modules) else None
108119

109120
def integrate_module_info(old_module, new_module):
110-
fields = []
111-
for field in fields:
121+
required_fields = []
122+
for field in required_fields:
112123
new_module[field] = old_module[field]
124+
optional_fields = ['proof']
125+
for field in optional_fields:
126+
if field in old_module:
127+
new_module[field] = old_module[field]
113128

114129
def find_corresponding_model(old_model, new_module):
115130
models = [

.github/scripts/parse_modules.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
import tla_utils
1212

1313
parser = ArgumentParser(description='Parses all TLA+ modules in the tlaplus/examples repo using SANY.')
14-
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=True)
15-
parser.add_argument('--apalache_path', help='Path to the Apalache directory', required=True)
16-
parser.add_argument('--tlapm_lib_path', help='Path to the TLA+ proof manager module directory; .tla files should be in this directory', required=True)
17-
parser.add_argument('--community_modules_jar_path', help='Path to the CommunityModules-deps.jar file', required=True)
18-
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=True)
14+
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=False, default='deps/tools/tla2tools.jar')
15+
parser.add_argument('--apalache_path', help='Path to the Apalache directory', required=False, default='deps/apalache')
16+
parser.add_argument('--tlapm_lib_path', help='Path to the TLA+ proof manager module directory; .tla files should be in this directory', required=False, default='deps/tlapm/library')
17+
parser.add_argument('--community_modules_jar_path', help='Path to the CommunityModules-deps.jar file', required=False, default='deps/community/modules.jar')
18+
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=False, default='.')
1919
parser.add_argument('--skip', nargs='+', help='Space-separated list of .tla modules to skip parsing', required=False, default=[])
2020
parser.add_argument('--only', nargs='+', help='If provided, only parse models in this space-separated list', required=False, default=[])
2121
parser.add_argument('--verbose', help='Set logging output level to debug', action='store_true')

.github/scripts/record_model_state_space.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
import tla_utils
1212

1313
parser = ArgumentParser(description='Updates manifest.json with unique & total model states for each small model.')
14-
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=True)
15-
parser.add_argument('--tlapm_lib_path', help='Path to the TLA+ proof manager module directory; .tla files should be in this directory', required=True)
16-
parser.add_argument('--community_modules_jar_path', help='Path to the CommunityModules-deps.jar file', required=True)
17-
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=True)
14+
parser.add_argument('--tools_jar_path', help='Path to the tla2tools.jar file', required=False, default='deps/tools/tla2tools.jar')
15+
parser.add_argument('--tlapm_lib_path', help='Path to the TLA+ proof manager module directory; .tla files should be in this directory', required=False, default='deps/tlapm/library')
16+
parser.add_argument('--community_modules_jar_path', help='Path to the CommunityModules-deps.jar file', required=False, default='deps/community/modules.jar')
17+
parser.add_argument('--examples_root', help='Root directory of the tlaplus/examples repository', required=False, default='.')
1818
parser.add_argument('--skip', nargs='+', help='Space-separated list of models to skip checking', required=False, default=[])
1919
parser.add_argument('--only', nargs='+', help='If provided, only check models in this space-separated list', required=False, default=[])
2020
parser.add_argument('--enable_assertions', help='Enable Java assertions (pass -enableassertions to JVM)', action='store_true')

0 commit comments

Comments
 (0)