mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-11-04 12:07:42 +00:00
Qualify Arduino 3.3.12 compatibility hooks
This commit is contained in:
1 parent
f121704a5a
commit
f056ea688d
9 files changed
+81
-44
No files matched your search
@@ -1,4 +1,4 @@
|
||||
"""Restore pinned Arduino 3.3.11 BLE service ownership for reversible shutdown.
|
||||
"""Restore pinned Arduino 3.3.11/3.3.12 BLE ownership for reversible shutdown.
|
||||
|
||||
BLEDevice owns the new wrapper's server, but its implicit destructor leaves
|
||||
raw service pointers behind. BLEService's destructor is private; delete it
|
||||
@@ -11,7 +11,6 @@ import re
|
||||
|
||||
|
||||
FRAMEWORKS_UNCHANGED = {(2, 0, 17), (3, 1, 3)}
|
||||
FRAMEWORK_PATCHED = (3, 3, 11)
|
||||
PINNED_SHA256 = "ec3735051f3263fc0101986fe33602a2593c294c6ccbd9f7b1b416c15529c3c9"
|
||||
PATCHED_SHA256 = "bf8fecfd6367eb0a2c23dd610336fefa17687d1f7bbc38e9d8f8642835763e87"
|
||||
SOURCE_DIRECTORY = ("libraries", "BLE", "src")
|
||||
@@ -43,16 +42,28 @@ SOURCE_PINS = {
|
||||
"2b71699e1962cdc37a54ace429ebdd408b0e40b379d621d427add5bb5a93b252",
|
||||
"8c4d8917ffb768a9eab468b1be06be209b4b8fc065b125c821682b6579a30267", SERVICE_ORIGINAL, SERVICE_REPAIRED),
|
||||
}
|
||||
FRAMEWORK_PINS = {
|
||||
(3, 3, 11): SOURCE_PINS,
|
||||
(3, 3, 12): {
|
||||
# 3.3.12 adds pairing-result logs; retain them while repairing only the
|
||||
# unchanged server/service ownership and array destruction below.
|
||||
"BLEDevice.cpp": (
|
||||
"212c62b3575f4ca8aff3be57a066291e1b1522f0d609b9e727bb547ab600371d",
|
||||
"8790bb0f75b177a3c47bdfb03e436a705f7e1f0b21868039244242cc1f3d08fc",
|
||||
ORIGINAL, REPAIRED),
|
||||
"BLEService.cpp": SOURCE_PINS["BLEService.cpp"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def patched_ble_source(source, framework_version, name="BLEDevice.cpp"):
|
||||
if framework_version in FRAMEWORKS_UNCHANGED:
|
||||
return source
|
||||
if framework_version != FRAMEWORK_PATCHED:
|
||||
if framework_version not in FRAMEWORK_PINS:
|
||||
raise RuntimeError("ESP32 BLE lifecycle fix: unreviewed framework version")
|
||||
source = source.replace("\r\n", "\n")
|
||||
try:
|
||||
original_digest, patched_digest, original, repaired = SOURCE_PINS[name]
|
||||
original_digest, patched_digest, original, repaired = FRAMEWORK_PINS[framework_version][name]
|
||||
except KeyError as error:
|
||||
raise RuntimeError("ESP32 BLE lifecycle fix: unreviewed source") from error
|
||||
digest = hashlib.sha256(source.encode("utf-8")).hexdigest()
|
||||
|
||||
@@ -68,6 +68,10 @@ FRAMEWORK_PINS = {
|
||||
(3, 3, 11): (
|
||||
'3d345bcb3b764f08c6e923dbacc96f27da10edb12beb774de439228057d14a54',
|
||||
'0cfa80cc29a38e854fa17401605dbe9adbd6157195ec5444f0a90d68c344cd0c'),
|
||||
# The FS source and compiler/newlib ABI are unchanged in 3.3.12.
|
||||
(3, 3, 12): (
|
||||
'3d345bcb3b764f08c6e923dbacc96f27da10edb12beb774de439228057d14a54',
|
||||
'0cfa80cc29a38e854fa17401605dbe9adbd6157195ec5444f0a90d68c344cd0c'),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -368,6 +368,8 @@ PATCHED_HWCDC_3311_SHA256 = "7b693bf5d72df97336360969ff429c67f3783d472123aeb79b3
|
||||
HWCDC_STARTUP_PINS = {
|
||||
(3, 1, 3): (PINNED_HWCDC_313_SHA256, PATCHED_HWCDC_313_SHA256),
|
||||
(3, 3, 11): (PINNED_HWCDC_3311_SHA256, PATCHED_HWCDC_3311_SHA256),
|
||||
# The 3.3.12 HWCDC driver is byte-identical to the reviewed 3.3.11 driver.
|
||||
(3, 3, 12): (PINNED_HWCDC_3311_SHA256, PATCHED_HWCDC_3311_SHA256),
|
||||
}
|
||||
HWCDC_STARTUP_INCLUDE = '#include "hal/usb_serial_jtag_ll.h"\n'
|
||||
HWCDC_STARTUP_HOOK = 'extern "C" bool meshEsp32HwcdcShouldReportBusReset() __attribute__((weak));\n'
|
||||
|
||||
@@ -18,7 +18,7 @@ ZEROED_DECLARATION = "wifi_scan_config_t config = {};"
|
||||
PATCHED_VERSION = (2, 0, 17)
|
||||
# These existing recipes keep their own framework sources. This narrowly
|
||||
# qualified Arduino 2 fix does not claim to qualify Arduino 3 scanning.
|
||||
KNOWN_UNCHANGED_VERSIONS = {(3, 1, 3), (3, 3, 11)}
|
||||
KNOWN_UNCHANGED_VERSIONS = {(3, 1, 3), (3, 3, 11), (3, 3, 12)}
|
||||
|
||||
|
||||
def patched_scan_source(source, framework_version=PATCHED_VERSION):
|
||||
|
||||
@@ -145,24 +145,25 @@ class BleLifecycleFixTests(unittest.TestCase):
|
||||
self.assertIn("alloc-dealloc-mismatch", run.stderr)
|
||||
|
||||
def test_hash_guards_idempotence_and_old_sdk_unchanged(self):
|
||||
for name, (_, _, original, repaired) in FIX.SOURCE_PINS.items():
|
||||
with self.subTest(name=name):
|
||||
source = "// reviewed source fixture\n" + original
|
||||
expected = source.replace(original, repaired)
|
||||
pins = {name: (hashlib.sha256(source.encode()).hexdigest(),
|
||||
hashlib.sha256(expected.encode()).hexdigest(), original, repaired)}
|
||||
with patch.object(FIX, "SOURCE_PINS", pins):
|
||||
self.assertEqual(FIX.patched_ble_source(source, (3, 3, 11), name), expected)
|
||||
self.assertEqual(FIX.patched_ble_source(expected, (3, 3, 11), name), expected)
|
||||
self.assertEqual(FIX.patched_ble_source(source.replace("\n", "\r\n"), (3, 3, 11), name), expected)
|
||||
for version in ((2, 0, 17), (3, 1, 3)):
|
||||
self.assertEqual(FIX.patched_ble_source(source, version, name), source)
|
||||
with self.assertRaisesRegex(RuntimeError, "changed pinned source"):
|
||||
FIX.patched_ble_source(source + "// changed\n", (3, 3, 11), name)
|
||||
with self.assertRaisesRegex(RuntimeError, "unreviewed framework"):
|
||||
FIX.patched_ble_source(source, (3, 3, 12), name)
|
||||
with self.assertRaisesRegex(RuntimeError, "unreviewed source"):
|
||||
FIX.patched_ble_source(source, (3, 3, 11), "BLEOther.cpp")
|
||||
for version, sources in FIX.FRAMEWORK_PINS.items():
|
||||
for name, (_, _, original, repaired) in sources.items():
|
||||
with self.subTest(version=version, name=name):
|
||||
source = "// reviewed source fixture\n" + original
|
||||
expected = source.replace(original, repaired)
|
||||
pins = {name: (hashlib.sha256(source.encode()).hexdigest(),
|
||||
hashlib.sha256(expected.encode()).hexdigest(), original, repaired)}
|
||||
with patch.object(FIX, "FRAMEWORK_PINS", {version: pins}):
|
||||
self.assertEqual(FIX.patched_ble_source(source, version, name), expected)
|
||||
self.assertEqual(FIX.patched_ble_source(expected, version, name), expected)
|
||||
self.assertEqual(FIX.patched_ble_source(source.replace("\n", "\r\n"), version, name), expected)
|
||||
for old in ((2, 0, 17), (3, 1, 3)):
|
||||
self.assertEqual(FIX.patched_ble_source(source, old, name), source)
|
||||
with self.assertRaisesRegex(RuntimeError, "changed pinned source"):
|
||||
FIX.patched_ble_source(source + "// changed\n", version, name)
|
||||
with self.assertRaisesRegex(RuntimeError, "unreviewed framework"):
|
||||
FIX.patched_ble_source(source, (3, 3, 13), name)
|
||||
with self.assertRaisesRegex(RuntimeError, "unreviewed source"):
|
||||
FIX.patched_ble_source(source, version, "BLEOther.cpp")
|
||||
|
||||
def test_middleware_copies_only_pinned_sources_without_touching_shared_sdk(self):
|
||||
with tempfile.TemporaryDirectory(prefix="ble-sdk-middleware-") as directory:
|
||||
@@ -179,7 +180,7 @@ class BleLifecycleFixTests(unittest.TestCase):
|
||||
pins = {"BLEDevice.cpp": (hashlib.sha256(original.encode()).hexdigest(),
|
||||
hashlib.sha256(expected.encode()).hexdigest(), FIX.ORIGINAL, FIX.REPAIRED)}
|
||||
node, env = FakeNode(source), FakeEnvironment(root / "build")
|
||||
with patch.object(FIX, "SOURCE_PINS", pins):
|
||||
with patch.object(FIX, "FRAMEWORK_PINS", {(3, 3, 11): pins, (3, 3, 12): pins}):
|
||||
destination = FIX.replace_ble_source(env, node)
|
||||
self.assertEqual(source.read_text(), original)
|
||||
self.assertEqual(destination.read_text(), expected)
|
||||
@@ -188,25 +189,34 @@ class BleLifecycleFixTests(unittest.TestCase):
|
||||
self.assertEqual(FIX.replace_ble_source(env, node), destination)
|
||||
self.assertEqual(destination.stat().st_mtime_ns, timestamp)
|
||||
version.write_text(version.read_text().replace("PATCH 11", "PATCH 12"))
|
||||
self.assertEqual(FIX.replace_ble_source(env, node), destination)
|
||||
version.write_text(version.read_text().replace("PATCH 12", "PATCH 13"))
|
||||
with self.assertRaisesRegex(RuntimeError, "unreviewed framework"):
|
||||
FIX.replace_ble_source(env, node)
|
||||
|
||||
def test_installed_exact_sdk_pins_when_available(self):
|
||||
packages = Path.home() / ".platformio/packages"
|
||||
candidates = list(packages.glob("framework-arduinoespressif32*"))
|
||||
sdk = None
|
||||
core_dir = Path(os.environ.get("PLATFORMIO_CORE_DIR", str(Path.home() / ".platformio")))
|
||||
candidates = list((core_dir / "packages").glob("framework-arduinoespressif32*"))
|
||||
if os.environ.get("MESH_ARDUINO_SDK_TEST_DIR"):
|
||||
candidates.append(Path(os.environ["MESH_ARDUINO_SDK_TEST_DIR"]))
|
||||
tested = 0
|
||||
for candidate in candidates:
|
||||
source = candidate / "libraries/BLE/src/BLEDevice.cpp"
|
||||
if source.exists() and FIX.framework_version(source) == (3, 3, 11):
|
||||
sdk = source.parent
|
||||
break
|
||||
if sdk is None:
|
||||
self.skipTest("Pinned Arduino3.3.11 SDK is not installed")
|
||||
for name in FIX.SOURCE_PINS:
|
||||
source = (sdk / name).read_text()
|
||||
patched = FIX.patched_ble_source(source, (3, 3, 11), name)
|
||||
self.assertNotEqual(source, patched)
|
||||
self.assertEqual(FIX.patched_ble_source(patched, (3, 3, 11), name), patched)
|
||||
if not source.exists():
|
||||
continue
|
||||
version = FIX.framework_version(source)
|
||||
if version not in FIX.FRAMEWORK_PINS:
|
||||
continue
|
||||
for name in FIX.FRAMEWORK_PINS[version]:
|
||||
original = (source.parent / name).read_text(encoding="utf-8")
|
||||
patched = FIX.patched_ble_source(original, version, name)
|
||||
self.assertNotEqual(original, patched)
|
||||
self.assertEqual(FIX.patched_ble_source(patched, version, name), patched)
|
||||
old, new = FIX.FRAMEWORK_PINS[version][name][2:]
|
||||
self.assertEqual(patched.replace(new, old, 1), original)
|
||||
tested += 1
|
||||
if not tested:
|
||||
self.skipTest("Pinned Arduino3.3.11/3.3.12 SDK is not installed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -20,6 +20,8 @@ for version in ((3, 1, 3), (3, 3, 11)):
|
||||
fixture = FIXTURES / ".".join(map(str, version))
|
||||
raw = json.loads((fixture / "vfs_api.json").read_text(encoding="ascii"))["source"]
|
||||
SDK_VARIANTS.append((version, raw, FIX.patched_vfs_source(raw, version), fixture))
|
||||
# The reviewed 3.3.12 FS source is byte-identical; share its source witness.
|
||||
SDK_VARIANTS.append(((3, 3, 12), raw, FIX.patched_vfs_source(raw, (3, 3, 12)), fixture))
|
||||
|
||||
|
||||
def method(source, signature):
|
||||
@@ -276,6 +278,8 @@ class FileBufferFixTest(unittest.TestCase):
|
||||
for version, raw, patched, fixture in SDK_VARIANTS:
|
||||
with self.subTest(version=version):
|
||||
original_header = (fixture / "esp_arduino_version.h").read_bytes()
|
||||
if version == (3, 3, 12):
|
||||
original_header = original_header.replace(b"VERSION_PATCH 11", b"VERSION_PATCH 12")
|
||||
source.write_text(raw, encoding="utf-8")
|
||||
header.write_bytes(original_header)
|
||||
self.assertEqual(FIX.require_pinned_framework(source), version)
|
||||
@@ -296,12 +300,12 @@ class FileBufferFixTest(unittest.TestCase):
|
||||
for changed in (raw + "\n", patched + "\n",
|
||||
raw.replace("return res == 0;", "return true;", 1)):
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(changed, version)
|
||||
for other_version, _, _, _ in SDK_VARIANTS:
|
||||
if other_version != version:
|
||||
for other_version, other_raw, _, _ in SDK_VARIANTS:
|
||||
if other_version != version and other_raw != raw:
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(raw, other_version)
|
||||
header.write_bytes(original_header + b"\n#define ESP_ARDUINO_VERSION_MAJOR 9\n")
|
||||
with self.assertRaises(RuntimeError): FIX.replace_vfs_source(env, node)
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(RAW, (3, 3, 12))
|
||||
with self.assertRaises(RuntimeError): FIX.patched_vfs_source(RAW, (3, 3, 13))
|
||||
|
||||
def test_exact_fingerprints_shape_normalization_and_idempotence(self):
|
||||
self.assertEqual(hashlib.sha256((FIXTURES / "vfs_api.cpp").read_bytes()).hexdigest(), FIX.PINNED_VFS_SHA256)
|
||||
|
||||
@@ -119,7 +119,8 @@ class Esp32UsbSessionFixTests(unittest.TestCase):
|
||||
cores = {}
|
||||
for name, version in (("framework-arduinoespressif32", (3, 1, 3)),
|
||||
("framework-arduinoespressif32@src-old", (2, 0, 17)),
|
||||
("framework-arduinoespressif32@src-new", (3, 3, 11))):
|
||||
("framework-arduinoespressif32@src-new", (3, 3, 11)),
|
||||
("framework-arduinoespressif32@src-experiment", (3, 3, 12))):
|
||||
core = packages / name / "cores/esp32"
|
||||
core.mkdir(parents=True)
|
||||
(core / "USBCDC.cpp").write_text(CDC, encoding="ascii")
|
||||
|
||||
@@ -224,12 +224,12 @@ class WiFiScanFixTest(unittest.TestCase):
|
||||
def version_text(version):
|
||||
return "".join(f"#define ESP_ARDUINO_VERSION_{key} {value}\n"
|
||||
for key, value in zip(("MAJOR", "MINOR", "PATCH"), version))
|
||||
for version in ((3, 1, 3), (3, 3, 11)):
|
||||
for version in ((3, 1, 3), (3, 3, 11), (3, 3, 12)):
|
||||
header.write_text(version_text(version))
|
||||
self.assertEqual(FIX.require_known_framework(source), version)
|
||||
self.assertIs(FIX.replace_scan_source(env, node), node)
|
||||
self.assertFalse(env.build.exists())
|
||||
for changed in ("", version_text((2, 0, 18)), version_text((3, 3, 12)),
|
||||
for changed in ("", version_text((2, 0, 18)), version_text((3, 3, 13)),
|
||||
version_text((2, 0, 17)) + "#define ESP_ARDUINO_VERSION_MAJOR 2\n"):
|
||||
header.write_text(changed)
|
||||
with self.assertRaises(RuntimeError): FIX.replace_scan_source(env, node)
|
||||
|
||||
@@ -167,10 +167,15 @@ class Hwcdc3311StartupSessionTests(startup.HwcdcStartupSessionTests):
|
||||
encoding="ascii")
|
||||
with self.assertRaises(RuntimeError):
|
||||
FIX.replace_hwcdc_source(env, node)
|
||||
# An unreviewed framework version keeps its existing driver.
|
||||
# 3.3.12 retains this exact driver and uses the same capture hook.
|
||||
version.write_text("#define ESP_ARDUINO_VERSION_MAJOR 3\n"
|
||||
"#define ESP_ARDUINO_VERSION_MINOR 3\n"
|
||||
"#define ESP_ARDUINO_VERSION_PATCH 12\n", encoding="ascii")
|
||||
self.assertEqual(Path(FIX.replace_hwcdc_source(env, node)).read_text(), self.patched)
|
||||
# An unreviewed framework version keeps its existing driver.
|
||||
version.write_text("#define ESP_ARDUINO_VERSION_MAJOR 3\n"
|
||||
"#define ESP_ARDUINO_VERSION_MINOR 3\n"
|
||||
"#define ESP_ARDUINO_VERSION_PATCH 13\n", encoding="ascii")
|
||||
self.assertIs(FIX.replace_hwcdc_source(env, node), node)
|
||||
|
||||
def test_shared_predicate_is_exported_without_the_217_tx_backport(self):
|
||||
|
||||
Reference in new issue
Block a user