Files
MeshCore/test/test_nrf52_program_lifetime.py
T

82 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""Keep local cleanup while omitting unreachable nRF52 process-exit callbacks."""
import importlib.util
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location("nrf52_flash_trim", ROOT / "scripts/nrf52_flash_trim.py")
policy = importlib.util.module_from_spec(spec)
spec.loader.exec_module(policy)
class Nrf52ProgramLifetimeTest(unittest.TestCase):
def test_automatic_delete_and_explicit_shutdown_remain_and_exit_callback_is_not_registered(self):
compiler = shutil.which("g++")
if not compiler:
self.skipTest("native G++ unavailable")
source = r'''
#include <cassert>
extern "C" int __aeabi_atexit(void*, void (*)(void*), void*);
extern "C" int __cxa_atexit(void (*)(void*), void*, void*);
extern "C" void __cxa_finalize(void*);
unsigned closed = 0, destroyed = 0, callback = 0;
void cleanup(void* count) { ++*static_cast<unsigned*>(count); }
struct Resource {
void close() { ++closed; }
~Resource() { close(); ++destroyed; }
};
int main() {
{ Resource automatic; }
auto* allocated = new Resource;
delete allocated;
assert(closed == 2 && destroyed == 2);
Resource explicit_close;
explicit_close.close();
assert(closed == 3 && destroyed == 2);
unsigned dso = 0;
assert(__aeabi_atexit(&callback, cleanup, &dso) == 0);
__cxa_finalize(&dso);
assert(callback == 0);
// Positive control: the finalizer really can run a registered callback.
assert(__cxa_atexit(cleanup, &callback, &dso) == 0);
__cxa_finalize(&dso);
assert(callback == 1);
}
'''
with tempfile.TemporaryDirectory(prefix="mesh-nrf52-lifetime-") as directory:
directory = Path(directory)
main, binary = directory / "main.cpp", directory / "checks"
main.write_text(source, encoding="ascii")
subprocess.run([compiler, "-std=c++11", "-Os", "-flto", "-DNRF52_PLATFORM=1",
"-DMESH_NRF52_NO_PROGRAM_EXIT=1", str(main),
str(ROOT / "src/Nrf52ProgramLifetime.cpp"), "-o", str(binary)], check=True)
subprocess.run([str(binary)], check=True)
def test_unknown_and_absent_framework_main_keep_standard_registration(self):
self.assertFalse(policy.supported_program_lifetime(None))
with tempfile.TemporaryDirectory(prefix="mesh-nrf52-lifetime-core-") as directory:
self.assertFalse(policy.supported_program_lifetime(directory))
main = Path(directory) / "cores/nRF5/main.cpp"
main.parent.mkdir(parents=True)
main.write_text("int main() { return 0; }\n", encoding="ascii")
self.assertFalse(policy.supported_program_lifetime(directory))
def test_current_pinned_core_is_the_reviewed_nonreturning_main_when_installed(self):
framework = Path.home() / ".platformio/packages/framework-arduinoadafruitnrf52"
if not framework.is_dir():
self.skipTest("pinned nRF52 framework not installed in this host job")
if not policy.supported_program_lifetime(framework):
self.skipTest("custom or newer core retains ordinary exit registration")
main = (framework / "cores/nRF5/main.cpp").read_text()
self.assertIn("while (1)", main)
self.assertIn("vTaskStartScheduler();", main)
self.assertIn("NVIC_SystemReset();", main)
if __name__ == "__main__":
unittest.main()