Skip to content

Commit 1683cd4

Browse files
geo2arv-auditor
andauthored
Pre-kompile K definitions and specs for integration tests (#2417)
* Towards in-advance kompilation of definitions in tests * Allow a permanent prekompiled directory * Run less tests, for debugging purposes * Add test_kompile_targets * Add file lock * De-dupe targets in a smarter way * Don't prekompile definitions for failing tests * Add __eq__ override to Target * Use target name as cache key, add cache debug msgs * Actually read cache from disk * Revert "Run less tests, for debugging purposes" This reverts commit 5858d0a. * Remove debugging messages * Correct path stuff * Consolidate conftest.py * Add comments * Add pytest marker for prekompilation * Add more comments * Skip prekompiling in the same way as proving * Remove Target.__eq__, improve comments * Set Version: 1.0.546 * Skip kompilation cache test * Skip prekompilation test if no --kompiled-targets-dir is given * Code style * Add smtlib markers to Word operations * Revert "Add smtlib markers to Word operations" This reverts commit 0ab98ac. * Remove redundant parameter * Revert "Remove redundant parameter" This reverts commit 54c5c4a. * Set Version: 1.0.548 --------- Co-authored-by: devops <[email protected]>
1 parent 0a98f60 commit 1683cd4

File tree

5 files changed

+92
-22
lines changed

5 files changed

+92
-22
lines changed

kevm-pyk/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "kevm-pyk"
7-
version = "1.0.547"
7+
version = "1.0.548"
88
description = ""
99
authors = [
1010
"Runtime Verification, Inc. <[email protected]>",

kevm-pyk/src/kevm_pyk/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,4 @@
55
if TYPE_CHECKING:
66
from typing import Final
77

8-
9-
VERSION: Final = '1.0.547'
8+
VERSION: Final = '1.0.548'

kevm-pyk/src/tests/conftest.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
from typing import TYPE_CHECKING
44

55
import pytest
6+
from pyk.cli.utils import dir_path
67

78
if TYPE_CHECKING:
8-
from pytest import FixtureRequest, Parser
9+
from pathlib import Path
10+
11+
from pytest import FixtureRequest, Parser, TempPathFactory
912

1013

1114
def pytest_addoption(parser: Parser) -> None:
@@ -27,6 +30,11 @@ def pytest_addoption(parser: Parser) -> None:
2730
type=str,
2831
help='Run only this specific specification (skip others)',
2932
)
33+
parser.addoption(
34+
'--kompiled-targets-dir',
35+
type=dir_path,
36+
help='Use pre-kompiled definitions for proof tests',
37+
)
3038

3139

3240
@pytest.fixture
@@ -42,3 +50,12 @@ def use_booster(request: FixtureRequest) -> bool:
4250
@pytest.fixture(scope='session')
4351
def spec_name(request: FixtureRequest) -> str | None:
4452
return request.config.getoption('--spec-name')
53+
54+
55+
@pytest.fixture(scope='session')
56+
def kompiled_targets_dir(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:
57+
dir = request.config.getoption('--kompiled-targets-dir')
58+
if dir:
59+
return dir
60+
else:
61+
return tmp_path_factory.mktemp('prekompiled')

kevm-pyk/src/tests/integration/test_prove.py

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import TYPE_CHECKING, NamedTuple
66

77
import pytest
8+
from filelock import FileLock
89
from pyk.prelude.ml import is_top
910
from pyk.proof.reachability import APRProof
1011

@@ -21,7 +22,7 @@
2122
from typing import Any, Final
2223

2324
from pyk.utils import BugReport
24-
from pytest import LogCaptureFixture, TempPathFactory
25+
from pytest import FixtureRequest, LogCaptureFixture
2526

2627

2728
sys.setrecursionlimit(10**8)
@@ -118,31 +119,58 @@ class Target(NamedTuple):
118119
main_file: Path
119120
main_module_name: str
120121

122+
@property
123+
def name(self) -> str:
124+
"""
125+
Target's name is the two trailing path segments and the main module name
126+
"""
127+
return f'{self.main_file.parts[-2]}-{self.main_file.stem}-{self.main_module_name}'
128+
121129
def __call__(self, output_dir: Path) -> Path:
122-
return kevm_kompile(
123-
output_dir=output_dir / 'kompiled',
124-
target=KompileTarget.HASKELL,
125-
main_file=self.main_file,
126-
main_module=self.main_module_name,
127-
syntax_module=self.main_module_name,
128-
debug=True,
129-
)
130+
with FileLock(str(output_dir) + '.lock'):
131+
return kevm_kompile(
132+
output_dir=output_dir / 'kompiled',
133+
target=KompileTarget.HASKELL,
134+
main_file=self.main_file,
135+
main_module=self.main_module_name,
136+
syntax_module=self.main_module_name,
137+
debug=True,
138+
)
139+
140+
141+
@pytest.fixture(scope='module')
142+
def kompiled_target_cache(kompiled_targets_dir: Path) -> tuple[Path, dict[str, Path]]:
143+
"""
144+
Populate the cache of kompiled definitions from an existing file system directory. If the cache is hot, the `kompiled_target_for` fixture will not containt a call to `kompile`, saving an expesive call to the K frontend.
145+
"""
146+
cache_dir = kompiled_targets_dir / 'target'
147+
cache: dict[str, Path] = {}
148+
if cache_dir.exists(): # cache dir exists, populate cache
149+
for file in cache_dir.iterdir():
150+
if file.is_dir():
151+
# the cache key is the name of the target, which is the filename by-construction.
152+
cache[file.stem] = file / 'kompiled'
153+
else:
154+
cache_dir.mkdir(parents=True)
155+
return cache_dir, cache
130156

131157

132158
@pytest.fixture(scope='module')
133-
def kompiled_target_for(tmp_path_factory: TempPathFactory) -> Callable[[Path], Path]:
134-
cache_dir = tmp_path_factory.mktemp('target')
135-
cache: dict[Target, Path] = {}
159+
def kompiled_target_for(kompiled_target_cache: tuple[Path, dict[str, Path]]) -> Callable[[Path], Path]:
160+
"""
161+
Generate a function that returns a path to the kompiled defintion for a given K spec. Invoke `kompile` only if no kompiled directory is cached for the spec.
162+
"""
163+
cache_dir, cache = kompiled_target_cache
136164

137165
def kompile(spec_file: Path) -> Path:
138166
target = _target_for_spec(spec_file)
139167

140-
if target not in cache:
141-
output_dir = cache_dir / f'{target.main_file.stem}-{len(cache)}'
142-
output_dir.mkdir()
143-
cache[target] = target(output_dir)
168+
if target.name not in cache:
169+
output_dir = cache_dir / target.name
170+
output_dir.mkdir(exist_ok=True)
171+
cache[target.name] = target(output_dir)
144172

145-
return cache[target]
173+
return cache[target.name]
146174

147175
return kompile
148176

@@ -156,6 +184,32 @@ def _target_for_spec(spec_file: Path) -> Target:
156184
return Target(main_file, main_module_name)
157185

158186

187+
@pytest.mark.parametrize(
188+
'spec_file',
189+
ALL_TESTS,
190+
ids=[str(spec_file.relative_to(SPEC_DIR)) for spec_file in ALL_TESTS],
191+
)
192+
def test_kompile_targets(spec_file: Path, kompiled_target_for: Callable[[Path], Path], request: FixtureRequest) -> None:
193+
"""
194+
This test function is intended to be used to pre-kompile all definitions,
195+
so that the actual proof tests do not need to do the actual compilation,
196+
which is disturbing performance measurment.
197+
198+
To achieve the desired caching, this test should be run like this:
199+
pytest src/tests/integration/test_prove.py::test_kompile_targets --kompiled-targets-dir ./prekompiled
200+
201+
This test will be skipped if no --kompiled-targets-dir option is given
202+
"""
203+
dir = request.config.getoption('--kompiled-targets-dir')
204+
if not dir:
205+
pytest.skip()
206+
207+
if spec_file in FAILING_BOOSTER_TESTS:
208+
pytest.skip()
209+
210+
kompiled_target_for(spec_file)
211+
212+
159213
# ---------
160214
# Pyk tests
161215
# ---------

package/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.547
1+
1.0.548

0 commit comments

Comments
 (0)