updated: basic e2e update tests (#31742)
* e2e update test
* that too
* fix
* fix
* fix running in docker
* don't think GHA will work
* also test switching branches
* it's a test
* lets not delete that yet
* comment
* space
old-commit-hash: ac77129041
chrysler-long2
parent
e991495530
commit
4166525f03
2 changed files with 173 additions and 0 deletions
@ -0,0 +1,172 @@ |
||||
#!/usr/bin/env python3 |
||||
import os |
||||
import pathlib |
||||
import shutil |
||||
import signal |
||||
import subprocess |
||||
import tempfile |
||||
import time |
||||
import unittest |
||||
from unittest import mock |
||||
|
||||
import pytest |
||||
from openpilot.selfdrive.manager.process import ManagerProcess |
||||
|
||||
|
||||
from openpilot.selfdrive.test.helpers import processes_context |
||||
from openpilot.common.params import Params |
||||
|
||||
|
||||
def run(args, **kwargs): |
||||
return subprocess.run(args, **kwargs, check=True) |
||||
|
||||
|
||||
def update_release(directory, name, version, release_notes): |
||||
with open(directory / "RELEASES.md", "w") as f: |
||||
f.write(release_notes) |
||||
|
||||
(directory / "common").mkdir(exist_ok=True) |
||||
|
||||
with open(directory / "common" / "version.h", "w") as f: |
||||
f.write(f'#define COMMA_VERSION "{version}"') |
||||
|
||||
run(["git", "add", "."], cwd=directory) |
||||
run(["git", "commit", "-m", f"openpilot release {version}"], cwd=directory) |
||||
|
||||
|
||||
@pytest.mark.slow # TODO: can we test overlayfs in GHA? |
||||
class TestUpdateD(unittest.TestCase): |
||||
def setUp(self): |
||||
self.tmpdir = tempfile.mkdtemp() |
||||
|
||||
run(["sudo", "mount", "-t", "tmpfs", "tmpfs", self.tmpdir]) # overlayfs doesn't work inside of docker unless this is a tmpfs |
||||
|
||||
self.mock_update_path = pathlib.Path(self.tmpdir) |
||||
|
||||
self.params = Params() |
||||
|
||||
self.basedir = self.mock_update_path / "openpilot" |
||||
self.basedir.mkdir() |
||||
|
||||
self.staging_root = self.mock_update_path / "safe_staging" |
||||
self.staging_root.mkdir() |
||||
|
||||
self.remote_dir = self.mock_update_path / "remote" |
||||
self.remote_dir.mkdir() |
||||
|
||||
mock.patch("openpilot.common.basedir.BASEDIR", self.basedir).start() |
||||
|
||||
os.environ["UPDATER_STAGING_ROOT"] = str(self.staging_root) |
||||
os.environ["UPDATER_LOCK_FILE"] = str(self.mock_update_path / "safe_staging_overlay.lock") |
||||
|
||||
self.MOCK_RELEASES = { |
||||
"release3": ("0.1.2", "0.1.2 release notes"), |
||||
"master": ("0.1.3", "0.1.3 release notes"), |
||||
} |
||||
|
||||
def set_target_branch(self, branch): |
||||
self.params.put("UpdaterTargetBranch", branch) |
||||
|
||||
def setup_basedir_release(self, release): |
||||
self.params = Params() |
||||
self.set_target_branch(release) |
||||
run(["git", "clone", "-b", release, self.remote_dir, self.basedir]) |
||||
|
||||
def update_remote_release(self, release): |
||||
update_release(self.remote_dir, release, *self.MOCK_RELEASES[release]) |
||||
|
||||
def setup_remote_release(self, release): |
||||
run(["git", "init"], cwd=self.remote_dir) |
||||
run(["git", "checkout", "-b", release], cwd=self.remote_dir) |
||||
self.update_remote_release(release) |
||||
|
||||
def tearDown(self): |
||||
mock.patch.stopall() |
||||
run(["sudo", "umount", "-l", str(self.staging_root / "merged")]) |
||||
run(["sudo", "umount", "-l", self.tmpdir]) |
||||
shutil.rmtree(self.tmpdir) |
||||
|
||||
def send_check_for_updates_signal(self, updated: ManagerProcess): |
||||
updated.signal(signal.SIGUSR1.value) |
||||
|
||||
def send_download_signal(self, updated: ManagerProcess): |
||||
updated.signal(signal.SIGHUP.value) |
||||
|
||||
def _test_params(self, branch, fetch_available, update_available): |
||||
self.assertEqual(self.params.get("UpdaterTargetBranch", encoding="utf-8"), branch) |
||||
self.assertEqual(self.params.get_bool("UpdaterFetchAvailable"), fetch_available) |
||||
self.assertEqual(self.params.get_bool("UpdateAvailable"), update_available) |
||||
|
||||
def _test_update_params(self, branch, version, release_notes): |
||||
self.assertTrue(self.params.get("UpdaterNewDescription", encoding="utf-8").startswith(f"{version} / {branch}")) |
||||
self.assertEqual(self.params.get("UpdaterNewReleaseNotes", encoding="utf-8"), f"<p>{release_notes}</p>\n") |
||||
|
||||
def wait_for_idle(self, timeout=5, min_wait_time=2): |
||||
start = time.monotonic() |
||||
time.sleep(min_wait_time) |
||||
|
||||
while True: |
||||
waited = time.monotonic() - start |
||||
if self.params.get("UpdaterState", encoding="utf-8") == "idle": |
||||
print(f"waited {waited}s for idle") |
||||
break |
||||
|
||||
if waited > timeout: |
||||
raise TimeoutError("timed out waiting for idle") |
||||
|
||||
time.sleep(1) |
||||
|
||||
def test_new_release(self): |
||||
# Start on release3, simulate a release3 commit, ensure we fetch that update properly |
||||
self.setup_remote_release("release3") |
||||
self.setup_basedir_release("release3") |
||||
|
||||
with processes_context(["updated"]) as [updated]: |
||||
self._test_params("release3", False, False) |
||||
time.sleep(1) |
||||
self._test_params("release3", False, False) |
||||
|
||||
self.MOCK_RELEASES["release3"] = ("0.1.3", "0.1.3 release notes") |
||||
self.update_remote_release("release3") |
||||
|
||||
self.send_check_for_updates_signal(updated) |
||||
|
||||
self.wait_for_idle() |
||||
|
||||
self._test_params("release3", True, False) |
||||
|
||||
self.send_download_signal(updated) |
||||
|
||||
self.wait_for_idle() |
||||
|
||||
self._test_params("release3", False, True) |
||||
self._test_update_params("release3", *self.MOCK_RELEASES["release3"]) |
||||
|
||||
def test_switch_branches(self): |
||||
# Start on release3, request to switch to master manually, ensure we switched |
||||
self.setup_remote_release("release3") |
||||
self.setup_remote_release("master") |
||||
self.setup_basedir_release("release3") |
||||
|
||||
with processes_context(["updated"]) as [updated]: |
||||
self._test_params("release3", False, False) |
||||
self.wait_for_idle() |
||||
self._test_params("release3", False, False) |
||||
|
||||
self.set_target_branch("master") |
||||
self.send_check_for_updates_signal(updated) |
||||
|
||||
self.wait_for_idle() |
||||
|
||||
self._test_params("master", True, False) |
||||
|
||||
self.send_download_signal(updated) |
||||
|
||||
self.wait_for_idle() |
||||
|
||||
self._test_params("master", False, True) |
||||
self._test_update_params("master", *self.MOCK_RELEASES["master"]) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
unittest.main() |
Loading…
Reference in new issue