diff --git a/scripts/exporter/misterfpga_exporter.py b/scripts/exporter/misterfpga_exporter.py index 1567df50e..c84fc7ad4 100644 --- a/scripts/exporter/misterfpga_exporter.py +++ b/scripts/exporter/misterfpga_exporter.py @@ -86,8 +86,14 @@ class Exporter(BaseExporter): return super().states(fe, field_name) def written(self, fe: NativeFile, field_name: str) -> str: - """An entry is checked against one md5, the first accepted.""" - return fe.hash(field_name) + """An entry is one record: its md5, size and url describe one dump. + + The first value the truth accepts replaced MiSTer's own accepted + md5 while the url still served MiSTer's dump, which the Downloader + then refused (fetch_file_worker.py:127), and the report said + nothing. record_hash keeps the platform's value when it is accepted. + """ + return fe.record_hash(field_name) def _by_path(self, systems: dict[str, NativeSystem]) -> dict[str, object]: indexed: dict[str, object] = {} @@ -119,7 +125,7 @@ class Exporter(BaseExporter): fe = indexed.get(path) if fe is None: continue - md5 = fe.hash("md5") + md5 = self.written(fe, "md5") if not md5: # The record is checked by its md5: where the truth takes the # entry without one, a new size beside the old hash describes @@ -185,7 +191,7 @@ class Exporter(BaseExporter): indexed = self._by_path(systems) for path, fe in indexed.items(): declared = files.get(path) - md5 = fe.hash("md5") + md5 = self.written(fe, "md5") if ( declared is not None and md5 and declared.get("hash") != md5 and path not in getattr(self, "_unsized", []) diff --git a/tests/test_exporters.py b/tests/test_exporters.py index 108da1187..05cbf7555 100644 --- a/tests/test_exporters.py +++ b/tests/test_exporters.py @@ -1849,6 +1849,27 @@ class MisterDatabaseComesBackAsWritten(unittest.TestCase): self.assertEqual(len(exporter.withheld()), 1) self.assertEqual(exporter.validate(systems, produced), []) + def test_an_accepted_hash_is_not_swapped_for_another(self): + """The url serves MiSTer's dump, and the truth accepts it among two. + The first accepted md5 went in its place with no change reported, + and the Downloader refused every install of the file.""" + database = {"db_id": "bios_db", "files": { + "games/X/boot.rom": {"hash": "b" * 32, "size": 10, "url": "u"}, + }} + original = json.dumps(database) + scraped = {"systems": {"x": {"files": [{ + "name": "boot.rom", "destination": "X/boot.rom", "md5": "b" * 32, "size": 10, + }]}}} + truth = {"systems": {"x": {"files": [{ + "name": "boot.rom", "path": "X/boot.rom", "md5": ["a" * 32, "b" * 32], + "size": 10, "_cores": ["x"], + }]}}} + exporter = discover_exporters()["misterfpga"]() + systems, report = build_native_model(truth, scraped) + produced = exporter.render(systems, report, {"bios_db.json": original}, scraped) + self.assertEqual(produced["bios_db.json"], original) + self.assertEqual(exporter.validate(systems, produced), []) + def test_an_empty_truth_hands_back_the_database(self): archive = UPSTREAM_CACHE / "misterfpga" / "bios_db.json.zip" if not archive.exists():