Skip to content

Commit 85543ee

Browse files
committed
[green] make RP2 runtime closure tests hermetic
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent 0b3eee1 commit 85543ee

1 file changed

Lines changed: 101 additions & 7 deletions

File tree

‎tests/firmware_tests/host/test_rp2_arm_runtime_closure.py‎

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,14 @@
88
import copy
99
import hashlib
1010
import importlib.util
11+
import io
1112
import json
1213
from pathlib import Path
14+
import tarfile
15+
import tempfile
1316
import tomllib
1417
import unittest
18+
from unittest import mock
1519

1620

1721
ROOT = Path(__file__).resolve().parents[3]
@@ -118,11 +122,30 @@
118122
),
119123
}
120124

125+
FIXTURE_SOURCE_ARCHIVE_NAME = "arm-gnu-toolchain-source-fixture.tar.xz"
126+
FIXTURE_SOURCE_MANIFEST = b"fixture-source.c\tfixture source archive member\n"
127+
FIXTURE_SOURCE = b"int pyble_arm_runtime_fixture(void) { return 0; }\n"
128+
121129

122130
def sha256(path: Path) -> str:
123131
return hashlib.sha256(path.read_bytes()).hexdigest()
124132

125133

134+
def sha256_bytes(value: bytes) -> str:
135+
return hashlib.sha256(value).hexdigest()
136+
137+
138+
def source_archive_fixture() -> bytes:
139+
output = io.BytesIO()
140+
with tarfile.open(fileobj=output, mode="w:xz") as archive:
141+
info = tarfile.TarInfo("arm-gnu-source-fixture/fixture-source.c")
142+
info.size = len(FIXTURE_SOURCE)
143+
info.mode = 0o644
144+
info.mtime = 0
145+
archive.addfile(info, io.BytesIO(FIXTURE_SOURCE))
146+
return output.getvalue()
147+
148+
126149
def load_release_module():
127150
spec = importlib.util.spec_from_file_location(
128151
"release_bundle_arm_runtime_red", RELEASE_BUNDLE
@@ -140,6 +163,72 @@ class RP2ArmRuntimeClosureTests(unittest.TestCase):
140163
@classmethod
141164
def setUpClass(cls) -> None:
142165
cls.attribution = json.loads(ATTRIBUTION.read_text(encoding="utf-8"))
166+
cls._temporary = tempfile.TemporaryDirectory(
167+
prefix="pyble-rp2-arm-runtime-closure-"
168+
)
169+
cls.addClassCleanup(cls._temporary.cleanup)
170+
cls.validation_root = Path(cls._temporary.name)
171+
172+
archive_raw = source_archive_fixture()
173+
archive_path = (
174+
cls.validation_root
175+
/ "firmware/.arm-gnu/.pyble-dist"
176+
/ FIXTURE_SOURCE_ARCHIVE_NAME
177+
)
178+
archive_path.parent.mkdir(parents=True)
179+
archive_path.write_bytes(archive_raw)
180+
181+
cls.validation_attribution = copy.deepcopy(cls.attribution)
182+
identity = cls.validation_attribution["identity"]
183+
identity["source_archive"] = {
184+
"bytes": len(archive_raw),
185+
"filename": FIXTURE_SOURCE_ARCHIVE_NAME,
186+
"sha256": sha256_bytes(archive_raw),
187+
}
188+
manifest_relative = identity["source_manifest"]["path"]
189+
identity["source_manifest"]["sha256"] = sha256_bytes(
190+
FIXTURE_SOURCE_MANIFEST
191+
)
192+
193+
evidence_path = (
194+
cls.validation_root
195+
/ "firmware/licenses/evidence/rp2/arm-gnu-toolchain/14.2.rel1"
196+
)
197+
evidence_path.mkdir(parents=True)
198+
(cls.validation_root / manifest_relative).write_bytes(
199+
FIXTURE_SOURCE_MANIFEST
200+
)
201+
fixture_attribution = evidence_path / "runtime-attribution-v1.json"
202+
fixture_attribution.write_text(
203+
json.dumps(cls.validation_attribution, indent=2) + "\n",
204+
encoding="utf-8",
205+
)
206+
cls.validation_attribution_sha256 = sha256(fixture_attribution)
207+
208+
lock_path = cls.validation_root / "firmware/versions.lock"
209+
lock_path.write_text(
210+
"# SPDX-License-Identifier: MIT\n"
211+
"[micropython]\n"
212+
'repo = "https://example.invalid/micropython.git"\n'
213+
'ref = "fixture"\n'
214+
f'commit = "{"1" * 40}"\n\n'
215+
"[esp_idf]\n"
216+
'repo = "https://example.invalid/esp-idf.git"\n'
217+
'ref = "fixture"\n'
218+
f'commit = "{"2" * 40}"\n\n'
219+
"[pyble]\n"
220+
'agent_version = "0.6.0"\n'
221+
'protocol_version = "PBLE/1"\n\n'
222+
"[arm_gnu_toolchain]\n"
223+
f'source_archive_filename = "{FIXTURE_SOURCE_ARCHIVE_NAME}"\n'
224+
f"source_archive_bytes = {len(archive_raw)}\n"
225+
'source_archive_format = "tar.xz"\n'
226+
f'source_archive_sha256 = "{sha256_bytes(archive_raw)}"\n'
227+
f'source_manifest_path = "{manifest_relative}"\n'
228+
"source_manifest_sha256 = "
229+
f'"{sha256_bytes(FIXTURE_SOURCE_MANIFEST)}"\n',
230+
encoding="utf-8",
231+
)
143232

144233
def test_spec_freezes_build_input_vs_shipped_contribution(self) -> None:
145234
value = SPEC.read_text(encoding="utf-8")
@@ -365,7 +454,7 @@ def test_versions_lock_pins_the_replayable_official_source_archive(self) -> None
365454
self.assertEqual(arm.get(key), expected)
366455

367456
def expected_observation(self) -> dict[str, object]:
368-
attribution = self.attribution
457+
attribution = self.validation_attribution
369458
return {
370459
"identity": copy.deepcopy(attribution["identity"]),
371460
"build_tools": copy.deepcopy(attribution["build_tools"]),
@@ -408,7 +497,7 @@ def expected_observation(self) -> dict[str, object]:
408497
attribution["specs_inputs"]
409498
),
410499
"source_archive_sha256": (
411-
SOURCE_LOCK_CONTRACT["source_archive_sha256"]
500+
attribution["identity"]["source_archive"]["sha256"]
412501
),
413502
},
414503
}
@@ -422,11 +511,16 @@ def validator(self):
422511
return value
423512

424513
def validate(self, observed: dict[str, object]) -> dict[str, object]:
425-
return self.validator()(
426-
copy.deepcopy(self.attribution),
427-
observed,
428-
repo_root=ROOT,
429-
)
514+
with mock.patch.object(
515+
RELEASE,
516+
"_RP2_ARM_RUNTIME_ATTRIBUTION_SHA256",
517+
self.validation_attribution_sha256,
518+
):
519+
return self.validator()(
520+
copy.deepcopy(self.validation_attribution),
521+
observed,
522+
repo_root=self.validation_root,
523+
)
430524

431525
def test_validator_derives_notice_scope_from_contribution_not_load(self) -> None:
432526
result = self.validate(self.expected_observation())

0 commit comments

Comments
 (0)