NEOS background updater (#1892)
parent
e909fddac0
commit
cb5a2996e7
9 changed files with 353 additions and 139 deletions
@ -0,0 +1,82 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import shutil |
||||
import subprocess |
||||
import tempfile |
||||
import time |
||||
import unittest |
||||
|
||||
from common.basedir import BASEDIR |
||||
|
||||
UPDATER_PATH = os.path.join(BASEDIR, "installer/updater") |
||||
UPDATER = os.path.join(UPDATER_PATH, "updater") |
||||
UPDATE_MANIFEST = os.path.join(UPDATER_PATH, "update.json") |
||||
|
||||
|
||||
class TestUpdater(unittest.TestCase): |
||||
|
||||
@classmethod |
||||
def setUpClass(cls): |
||||
# test that the updater builds |
||||
cls.assertTrue(f"cd {UPDATER_PATH} && make clean && make", "updater failed to build") |
||||
|
||||
# restore the checked-in version, since that's what actually runs on devices |
||||
os.system(f"git reset --hard {UPDATER_PATH}") |
||||
|
||||
def setUp(self): |
||||
self._clear_dir() |
||||
|
||||
def tearDown(self): |
||||
self._clear_dir() |
||||
|
||||
def _clear_dir(self): |
||||
if os.path.isdir("/data/neoupdate"): |
||||
shutil.rmtree("/data/neoupdate") |
||||
|
||||
def _assert_ok(self, cmd, msg=None): |
||||
self.assertTrue(os.system(cmd) == 0, msg) |
||||
|
||||
def _assert_fails(self, cmd): |
||||
self.assertFalse(os.system(cmd) == 0) |
||||
|
||||
def test_background_download(self): |
||||
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||
|
||||
def test_background_download_bad_manifest(self): |
||||
# update with bad manifest should fail |
||||
with tempfile.NamedTemporaryFile(mode="w", suffix=".json") as f: |
||||
f.write("{}") |
||||
self._assert_fails(f"{UPDATER} bgcache 'file://{f.name}'") |
||||
|
||||
def test_cache_resume(self): |
||||
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||
# a full download takes >1m, but resuming from fully cached should only be a few seconds |
||||
start_time = time.monotonic() |
||||
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||
self.assertLess(time.monotonic() - start_time, 10) |
||||
|
||||
# make sure we can recover from corrupt downloads |
||||
def test_recover_from_corrupt(self): |
||||
# download the whole update |
||||
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||
|
||||
# write some random bytes |
||||
for f in os.listdir("/data/neoupdate"): |
||||
with open(os.path.join("/data/neoupdate", f), "ab") as f: |
||||
f.write(b"\xab"*20) |
||||
|
||||
# this attempt should fail, then it unlinks |
||||
self._assert_fails(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||
|
||||
# now it should pass |
||||
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||
|
||||
# simple test that the updater doesn't crash in UI mode |
||||
def test_ui_init(self): |
||||
with subprocess.Popen(UPDATER) as proc: |
||||
time.sleep(5) |
||||
self.assertTrue(proc.poll() is None) |
||||
proc.terminate() |
||||
|
||||
if __name__ == "__main__": |
||||
unittest.main() |
Binary file not shown.
@ -0,0 +1,17 @@ |
||||
#!/usr/bin/bash |
||||
|
||||
export OMP_NUM_THREADS=1 |
||||
export MKL_NUM_THREADS=1 |
||||
export NUMEXPR_NUM_THREADS=1 |
||||
export OPENBLAS_NUM_THREADS=1 |
||||
export VECLIB_MAXIMUM_THREADS=1 |
||||
|
||||
if [ -z "$REQUIRED_NEOS_VERSION" ]; then |
||||
export REQUIRED_NEOS_VERSION="14" |
||||
fi |
||||
|
||||
if [ -z "$PASSIVE" ]; then |
||||
export PASSIVE="1" |
||||
fi |
||||
|
||||
export STAGING_ROOT="/data/safe_staging" |
Loading…
Reference in new issue