mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-16 16:33:27 -05:00
fix: keep one-value md5 blank when dumps differ
This commit is contained in:
1 parent
7cb98f7c92
commit
3a0ae494d3
4 files changed
+74
-16
No files matched your search
@@ -133,6 +133,19 @@ class BaseExporter(ABC):
|
||||
require = require or cls.requires()
|
||||
return not require or bool(fe.hash(require))
|
||||
|
||||
@staticmethod
|
||||
def single_value(fe: NativeFile, field_name: str) -> str:
|
||||
"""The one hash a single-value field is checked against.
|
||||
|
||||
A platform that leaves the field empty checks the file by existence.
|
||||
Filling it turns that into a content check on one value: right when
|
||||
the truth accepts exactly one image, wrong when the core takes several
|
||||
and the field can hold only the first. Such a fill stays empty.
|
||||
"""
|
||||
if field_name in fe.filled and len(set(fe.hashes(field_name))) != 1:
|
||||
return ""
|
||||
return fe.hash(field_name)
|
||||
|
||||
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||
"""Whether the written file carries this entry's corrected field.
|
||||
|
||||
|
||||
@@ -53,6 +53,11 @@ class Exporter(BaseExporter):
|
||||
return False
|
||||
return super().writable(fe, require)
|
||||
|
||||
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||
if field_name == "md5" and "md5" in fe.filled:
|
||||
return bool(self.single_value(fe, "md5"))
|
||||
return super().states(fe, field_name)
|
||||
|
||||
@staticmethod
|
||||
def native_sources() -> dict[str, str]:
|
||||
return {"batocera-systems": SOURCE_URL}
|
||||
@@ -89,7 +94,7 @@ class Exporter(BaseExporter):
|
||||
for fe in files:
|
||||
# The platform states an unhashed file as an empty md5 rather
|
||||
# than leaving it out, and so do we.
|
||||
item = [f'"md5": "{fe.hash("md5")}"']
|
||||
item = [f'"md5": "{self.single_value(fe, "md5")}"']
|
||||
alt = fe.native("alt_md5", "")
|
||||
if alt:
|
||||
item.append(f'"altmd5": "{alt}"')
|
||||
|
||||
@@ -41,22 +41,9 @@ class Exporter(BaseExporter):
|
||||
def native_sources() -> dict[str, str]:
|
||||
return {"batocera-systems.json": SOURCE_URL}
|
||||
|
||||
@staticmethod
|
||||
def _md5(fe: NativeFile) -> str:
|
||||
"""The one md5 the entry is checked against, empty for existence.
|
||||
|
||||
RetroBat writes an empty md5 to check a file by existence. Filling it
|
||||
turns that into a content check on a single value: right when the
|
||||
truth accepts exactly one image, wrong when the core takes several
|
||||
and the field can hold only the first.
|
||||
"""
|
||||
if "md5" in fe.filled and len(fe.hashes("md5")) != 1:
|
||||
return ""
|
||||
return fe.hash("md5")
|
||||
|
||||
def states(self, fe: NativeFile, field_name: str) -> bool:
|
||||
if field_name == "md5" and "md5" in fe.filled:
|
||||
return bool(self._md5(fe))
|
||||
return bool(self.single_value(fe, "md5"))
|
||||
return super().states(fe, field_name)
|
||||
|
||||
def render(
|
||||
@@ -94,7 +81,7 @@ class Exporter(BaseExporter):
|
||||
ours: OrderedDict[str, OrderedDict[str, str]] = OrderedDict()
|
||||
for fe in files:
|
||||
entry: OrderedDict[str, str] = OrderedDict()
|
||||
entry["md5"] = self._md5(fe)
|
||||
entry["md5"] = self.single_value(fe, "md5")
|
||||
declared = fe.native("native_path", "")
|
||||
entry["file"] = str(declared) if declared else f"bios/{fe.destination}"
|
||||
ours.setdefault(entry["file"], entry)
|
||||
|
||||
@@ -663,6 +663,59 @@ class BatoceraExport(unittest.TestCase):
|
||||
)
|
||||
self.assertIn('"md5": ""', produced["batocera-systems"])
|
||||
|
||||
def test_a_blank_md5_is_not_filled_with_one_of_several_dumps(self):
|
||||
"""checkBios compares one md5: the first of two refuses the other.
|
||||
|
||||
The empty field checks by existence, so a user holding the second
|
||||
dump the core accepts would turn from OK to UNTESTED.
|
||||
"""
|
||||
first, second = "1" * 32, "2" * 32
|
||||
truth = {
|
||||
"systems": {
|
||||
"nintendo-ds": {
|
||||
"files": [
|
||||
{
|
||||
"name": "firmware.bin",
|
||||
"path": "firmware.bin",
|
||||
"md5": [first, second],
|
||||
"required": True,
|
||||
"_cores": ["melonds"],
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
scraped = {
|
||||
"systems": {
|
||||
"nintendo-ds": {
|
||||
"native_id": "nds",
|
||||
"files": [
|
||||
{
|
||||
"name": "firmware.bin",
|
||||
"destination": "firmware.bin",
|
||||
"native_system": "nds",
|
||||
"md5": "",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
for platform, filename in (
|
||||
("batocera", "batocera-systems"),
|
||||
("rocknix", "rocknix-systems"),
|
||||
):
|
||||
with self.subTest(platform=platform):
|
||||
systems, report = build_native_model(truth, scraped)
|
||||
(entry,) = systems["nds"].files
|
||||
self.assertIn("md5", entry.filled)
|
||||
exporter = discover_exporters()[platform]()
|
||||
produced = exporter.render(
|
||||
systems, report, {filename: self.SKELETON}, None
|
||||
)
|
||||
self.assertIn('"md5": ""', produced[filename])
|
||||
self.assertNotIn(first, produced[filename])
|
||||
self.assertFalse(exporter.states(entry, "md5"))
|
||||
|
||||
def test_writing_without_the_platform_file_is_refused(self):
|
||||
"""A systems dict alone is not the script Batocera runs."""
|
||||
systems, report = model()
|
||||
|
||||
Reference in new issue
Block a user