Restore Gaia settings and media workflows, extend native network adapters, and align the APK payload. Generate French/English changelogs and website entries from the simplified JSON source. Validation: shared JS and Tundra unit tests; desktop workflows and persistence; signed APK payload parity; Sargo RAM and installed startup with boot readback. Android runtime qualification is recorded separately.
65 lines
3.3 KiB
Python
65 lines
3.3 KiB
Python
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
from types import SimpleNamespace
|
|
import unittest
|
|
|
|
spec = importlib.util.spec_from_file_location('diagnostic', Path(__file__).resolve().parents[1]/'tools/diagnose-telephony.py')
|
|
diagnostic = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(diagnostic)
|
|
|
|
|
|
class DiagnosticTests(unittest.TestCase):
|
|
def test_qualcomm_attach_crash_is_visible_without_raw_backtrace(self):
|
|
report = diagnostic.modem_crashes(
|
|
'F libc: Fatal signal 7 (SIGBUS), fault addr 0x123456 in tid 101 (qcrild)\n'
|
|
'F DEBUG: qcril_qmi_nas_initial_attach_timeout_handler(void*)\n'
|
|
'private subscriber text\n')
|
|
self.assertEqual(report, {'signals':[{'signal':7, 'name':'SIGBUS'}],
|
|
'initialAttachTimeoutInBacktrace':True})
|
|
self.assertNotIn('0x123456', json.dumps(report))
|
|
self.assertNotIn('subscriber', json.dumps(report))
|
|
|
|
def test_crash_time_distinguishes_old_events_after_a_radio_restart(self):
|
|
report = diagnostic.modem_crashes(
|
|
'10-08 12:37:33.483 50 101 F libc: Fatal signal 7 (SIGBUS) in tid 101 (qcrild)\n')
|
|
self.assertEqual(report['signals'][0]['logTime'], '10-08 12:37:33.483')
|
|
|
|
def test_subscriber_and_cell_identifiers_never_leave_report(self):
|
|
props = {'Present': {'data': True}, 'SubscriberIdentity': {'data': 'private-imsi'},
|
|
'SubscriberNumbers': {'data': ['private-number']}, 'CardIdentifier': {'data': 'private-card'}}
|
|
self.assertEqual(diagnostic.select_properties('SimManager', props), {'Present': True})
|
|
line = 'src/binder_network.c:binder_network_poll_voice_state_cb() slot1 denied,lte,14,12345,987654321,11'
|
|
events = diagnostic.registration_events(line+'\n'+line)
|
|
self.assertEqual(len(events), 1)
|
|
self.assertEqual(events[0]['denialReason'], 11)
|
|
self.assertNotIn('12345', json.dumps(events))
|
|
self.assertNotIn('987654321', json.dumps(events))
|
|
|
|
def test_unavailable_services_are_not_reported_as_healthy(self):
|
|
def unavailable(command):
|
|
raise subprocess.TimeoutExpired(command, 12)
|
|
with tempfile.TemporaryDirectory() as folder:
|
|
report = diagnostic.collect(unavailable, Path(folder))
|
|
self.assertEqual(len(report['errors']), len(diagnostic.FIELDS))
|
|
self.assertFalse(report['registrationLogAvailable'])
|
|
self.assertEqual(report['audio'], {'available': False})
|
|
|
|
def test_diagnostic_only_reads_fixed_interfaces(self):
|
|
calls = []
|
|
def command(args):
|
|
calls.append(args)
|
|
if args[0] == 'busctl':
|
|
return SimpleNamespace(returncode=0, stdout='{"data":[{"Status":{"data":"denied"}}]}')
|
|
return SimpleNamespace(returncode=0, stdout='active\n' if args[0] == 'systemctl' else '')
|
|
with tempfile.TemporaryDirectory() as folder:
|
|
report = diagnostic.collect(command, Path(folder))
|
|
self.assertEqual(report['properties']['NetworkRegistration']['Status'], 'denied')
|
|
for args in calls:
|
|
if args[0] == 'busctl': self.assertEqual(args[-1], 'GetProperties')
|
|
if args[0] == 'systemctl': self.assertEqual(args[1], 'is-active')
|
|
|
|
|
|
if __name__ == '__main__': unittest.main()
|