Update shared Gaia workflows and Sargo development tooling
Fix camera switching and Contacts/SMS activity routing across shared hosts. Integrate Marketplace installation and native standalone recovery tools, Bluetooth runtime packages and trusted certificates. Update English/French READMEs and the shared changelog with tested scope and remaining radio/audio/Bluetooth limitations. Preview17 remains the existing APK; no new native image is released. Validation: shared JavaScript and native Python tests; Android packaging and payload parity from an isolated publication checkout.
This commit is contained in:
parent
d04824ec89
commit
0041cd9660
101 changed files with 3883 additions and 335 deletions
80
tundra/tests/test_standalone_flash.py
Normal file
80
tundra/tests/test_standalone_flash.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# SPDX-License-Identifier: MPL-2.0
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
spec=importlib.util.spec_from_file_location('flash',Path(__file__).resolve().parents[1]/'tools/flash-standalone-sargo.py')
|
||||
flash=importlib.util.module_from_spec(spec);spec.loader.exec_module(flash)
|
||||
|
||||
|
||||
class FlashTests(unittest.TestCase):
|
||||
def plan(self):
|
||||
return {'userdataBytes':8*1024**3,'partitions':{name:{'bytes':4096} for name in flash.PARTITIONS}}
|
||||
|
||||
def values(self,**changes):
|
||||
values={'product':'sargo','unlocked':'yes','current-slot':'b',
|
||||
**{'partition-size:'+name:hex(64*1024**3 if name=='userdata' else 64*1024**2) for name in flash.PARTITIONS}}
|
||||
values.update(changes);return values
|
||||
|
||||
def test_both_slots_accepted_for_full_install(self):
|
||||
for slot in ('a','b'):
|
||||
values=self.values(**{'current-slot':slot})
|
||||
with patch.object(flash,'getvar',side_effect=lambda _,__,name:values[name]):
|
||||
self.assertEqual(flash.preflight('fastboot','serial',self.plan())['current-slot'],slot)
|
||||
|
||||
def test_wrong_model_locked_or_small_partition_rejected(self):
|
||||
for change in ({'product':'bonito'},{'unlocked':'no'},{'partition-size:userdata':'0x1000'}):
|
||||
values=self.values(**change)
|
||||
with patch.object(flash,'getvar',side_effect=lambda _,__,name:values[name]):
|
||||
with self.assertRaises(ValueError):flash.preflight('fastboot','serial',self.plan())
|
||||
|
||||
def test_review_never_contacts_device(self):
|
||||
with patch.object(sys,'argv',['flash']),patch.object(flash,'package',return_value=self.plan()),patch.object(flash.subprocess,'run') as run,patch.object(flash.subprocess,'check_output') as read:
|
||||
flash.main();run.assert_not_called();read.assert_not_called()
|
||||
|
||||
def test_install_requires_explicit_userdata_replacement(self):
|
||||
with patch.object(sys,'argv',['flash','--install']),patch.object(flash,'package',return_value=self.plan()),patch.object(flash.subprocess,'run') as run:
|
||||
with self.assertRaises(SystemExit):flash.main()
|
||||
run.assert_not_called()
|
||||
|
||||
def test_unqualified_package_requires_experimental_flag(self):
|
||||
with patch.object(sys,'argv',['flash','--install','--erase-userdata']),patch.object(flash,'package',return_value=self.plan()),patch.object(flash.subprocess,'run') as run:
|
||||
with self.assertRaises(SystemExit):flash.main()
|
||||
run.assert_not_called()
|
||||
|
||||
def test_sparse_geometry(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
image=Path(directory)/'userdata.img'
|
||||
image.write_bytes(struct.pack('<I4H4I',0xed26ff3a,1,0,28,12,4096,2097152,3,0))
|
||||
self.assertEqual(flash.sparse_size(image),8*1024**3)
|
||||
image.write_bytes(b'bad')
|
||||
with self.assertRaises(ValueError):flash.sparse_size(image)
|
||||
|
||||
def test_incomplete_package_is_rejected_before_manifest_read(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
(Path(directory)/'.incomplete').touch()
|
||||
with self.assertRaisesRegex(ValueError,'construction is incomplete'):
|
||||
flash.package(directory)
|
||||
|
||||
def test_install_writes_only_planned_partitions_after_preflight(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
argv=['flash','--package',directory,'--install','--erase-userdata','--experimental']
|
||||
with patch.object(sys,'argv',argv),patch.object(flash,'package',return_value=self.plan()), \
|
||||
patch.object(flash.shutil,'which',return_value='fastboot'), \
|
||||
patch.object(flash.subprocess,'check_output',return_value='test-device fastboot\n'), \
|
||||
patch.object(flash,'preflight',return_value={'product':'sargo'}) as preflight, \
|
||||
patch.object(flash.subprocess,'run') as run:
|
||||
flash.main()
|
||||
preflight.assert_called_once()
|
||||
commands=[call.args[0][3:] for call in run.call_args_list]
|
||||
self.assertEqual(commands[0],['erase','userdata'])
|
||||
self.assertEqual([command[:2] for command in commands[1:-1]],
|
||||
[['flash',partition] for partition in flash.PARTITIONS])
|
||||
self.assertEqual(commands[-1],['set_active','a'])
|
||||
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
Loading…
Reference in a new issue