diff --git a/scripts/profile_sync.py b/scripts/profile_sync.py index 4a3eeee6..d7adf80d 100644 --- a/scripts/profile_sync.py +++ b/scripts/profile_sync.py @@ -707,9 +707,27 @@ def verify_at_pin(part: RefPart, pin_lines, tokens) -> PartResult: window = "\n".join(pin_lines[lo:hi]).lower() if any(token in window for token in tokens): return PartResult(part, "ANCHORED", None, None, None, []) + # A ref that cites loading logic never spells the value out, so its absence + # here proves nothing. Only finding the value somewhere else in the file + # shows the ref points at the wrong place. + elsewhere = sorted( + { + index + for index, line in enumerate(pin_lines, 1) + for token in tokens + if token in line.lower() + } + ) + if not elsewhere: + return PartResult(part, "ANCHORED", None, None, None, []) + if len(elsewhere) > 1: + return PartResult( + part, "AMBIGUOUS", None, None, None, elsewhere, + "declared value carried by several lines", + ) return PartResult( - part, "CHANGED", None, None, None, [], - "declared value not found at the pinned revision", + part, "CHANGED", None, elsewhere[0], elsewhere[0], elsewhere, + "declared value carried by another line", ) diff --git a/tests/test_profile_sync.py b/tests/test_profile_sync.py index 6912ada1..7eb5c82e 100644 --- a/tests/test_profile_sync.py +++ b/tests/test_profile_sync.py @@ -329,11 +329,23 @@ class TestVerifyAtPin(unittest.TestCase): "ANCHORED", ) - def test_declared_value_absent(self): + def test_declared_value_absent_from_the_whole_file(self): part = RefPart("a.c", 1, 1, "a.c:1") result = profile_sync.verify_at_pin(part, self.LINES, ["cafebabe"]) + self.assertEqual(result.status, "ANCHORED") + + def test_declared_value_carried_by_another_line(self): + lines = ["load()", "", "", "", "", "", 'rom("cafebabe")'] + part = RefPart("a.c", 1, 1, "a.c:1") + result = profile_sync.verify_at_pin(part, lines, ["cafebabe"]) self.assertEqual(result.status, "CHANGED") - self.assertIn("pinned revision", result.reason) + self.assertEqual(result.start, 7) + + def test_declared_value_on_several_lines_is_ambiguous(self): + lines = ["load()", "", "", "", 'a("cafebabe")', "", 'b("cafebabe")'] + part = RefPart("a.c", 1, 1, "a.c:1") + result = profile_sync.verify_at_pin(part, lines, ["cafebabe"]) + self.assertEqual(result.status, "AMBIGUOUS") def test_missing_file(self): part = RefPart("a.c", 1, 1, "a.c:1") @@ -996,16 +1008,26 @@ class TestBuildReport(unittest.TestCase): return profile def test_a_pin_equal_to_head_is_checked_for_self_consistency(self): - # The cited line does not carry the declared value, which a comparison - # of a revision with itself could never reveal. - self.files[("pinsha", "a.c")] = ["x", "unrelated"] - self.files[("headsha", "a.c")] = ["x", "unrelated"] + # The value sits on another line, so the ref demonstrably points wrong. + self.files[("headsha", "a.c")] = [ + "x", "unrelated", "", "", "", "", 'rom("deadbeef")', + ] profile = self._profile(["a.c:2"]) profile["source_commit"] = "headsha" profile["files"][0]["crc32"] = "deadbeef" report = build_report("test", profile, self.dir) self.assertEqual(report.entries[0].status, "CHANGED") + def test_a_value_absent_from_the_whole_file_is_not_held_against_the_ref(self): + # A ref citing loading logic never spells the value out; its absence + # is not evidence that the ref is stale. + self.files[("headsha", "a.c")] = ["x", "load_bios(path);"] + profile = self._profile(["a.c:2"]) + profile["source_commit"] = "headsha" + profile["files"][0]["crc32"] = "deadbeef" + report = build_report("test", profile, self.dir) + self.assertEqual(report.entries[0].status, "ANCHORED") + def test_a_pin_equal_to_head_accepts_a_ref_on_its_value(self): self.files[("headsha", "a.c")] = ["x", 'rom("deadbeef")'] profile = self._profile(["a.c:2"])