You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					83 lines
				
				2.5 KiB
			
		
		
			
		
	
	
					83 lines
				
				2.5 KiB
			| 
											5 years ago
										 | #!/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()
 |