Skip to content
File

Blob: scripts/test_flash_backup.py

python69 lines
1"""Backup integrity and failure behavior without serial I/O."""
2import json
3from pathlib import Path
4import subprocess
5import tempfile
6import unittest
7from unittest.mock import patch
8 
9import flash_backup as backup
10 
11 
12class BackupTests(unittest.TestCase):
13 def setUp(self):
14 self.temporary = tempfile.TemporaryDirectory()
15 self.addCleanup(self.temporary.cleanup)
16 self.root = Path(self.temporary.name)
17 self.size = patch.object(backup, "FLASH_BYTES", 64)
18 self.size.start()
19 self.addCleanup(self.size.stop)
20 
21 def run_tool(self, command, **kwargs):
22 self.assertTrue(kwargs["check"])
23 self.assertNotIn("write-flash", command)
24 self.assertNotIn("erase-flash", command)
25 if "read-flash" in command:
26 Path(command[-1]).write_bytes(b"x" * 64)
27 
28 def test_verified_image_is_preserved_and_rechecked(self):
29 image = backup.create_backup(self.root, "test-port", self.run_tool)
30 self.assertEqual(backup.require_backup(self.root), image)
31 self.assertEqual(image.stat().st_mode & 0o777, 0o600)
32 record = self.root / backup.RECORD_NAME
33 self.assertEqual(record.stat().st_mode & 0o777, 0o600)
34 self.assertTrue(json.loads(record.read_text())["verified_against_device_flash"])
35 with self.assertRaisesRegex(ValueError, "already exists"):
36 backup.create_backup(self.root, "another-port", lambda *a, **k: self.fail("Must not contact hardware"))
37 image.write_bytes(b"y" * 64)
38 with self.assertRaisesRegex(ValueError, "No valid saved backup"):
39 backup.require_backup(self.root)
40 
41 def test_failed_verification_publishes_no_backup(self):
42 def failing(command, **kwargs):
43 self.run_tool(command, **kwargs)
44 if "verify-flash" in command:
45 raise subprocess.CalledProcessError(1, command)
46 with self.assertRaises(subprocess.CalledProcessError):
47 backup.create_backup(self.root, "test-port", failing)
48 self.assertFalse((self.root / backup.IMAGE_NAME).exists())
49 self.assertFalse((self.root / backup.RECORD_NAME).exists())
50 
51 def test_partial_read_is_not_verified(self):
52 def partial(command, **kwargs):
53 self.assertIn("read-flash", command)
54 Path(command[-1]).write_bytes(b"short")
55 with self.assertRaisesRegex(ValueError, "incomplete"):
56 backup.create_backup(self.root, "test-port", partial)
57 self.assertFalse((self.root / backup.RECORD_NAME).exists())
58 
59 def test_missing_or_malformed_record_is_rejected(self):
60 for contents in [None, "not json", "[]", '{"verified_against_device_flash": true}']:
61 if contents is not None:
62 (self.root / backup.RECORD_NAME).write_text(contents)
63 with self.assertRaisesRegex(ValueError, "No valid saved backup"):
64 backup.require_backup(self.root)
65 
66 
67if __name__ == "__main__":
68 unittest.main()