Files
templates/tools/dsview/tests/test_install_decoders.py

117 lines
5.7 KiB
Python

"""Exercise the standalone PowerShell installer without modifying installed DSView."""
import os
from pathlib import Path
import shutil
import subprocess
import sys
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
SHELL = shutil.which('powershell.exe') or shutil.which('pwsh')
@unittest.skipUnless(os.name == 'nt' and SHELL, 'Windows PowerShell required')
class InstallerTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory(prefix='dsview installer ')
self.addCleanup(self.temp.cleanup)
self.app = Path(self.temp.name)/'DSView test'
self.app.mkdir()
(self.app/'DSView.exe').write_bytes(b'fixture, not executable')
(self.app/'decoders/common').mkdir(parents=True)
(self.app/'decoders/common/__init__.py').write_text('# existing common package\n')
(self.app/'decoders/common/keep.txt').write_text('unrelated')
def run_installer(self, *extra, script=None):
return subprocess.run([SHELL, '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File',
str(script or ROOT/'install_decoders.ps1'),
'-DsViewPath', str(self.app), *extra], capture_output=True, text=True)
def test_install_import_and_update_backup(self):
result = self.run_installer()
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
code = '''
import importlib, pathlib, sys, types
root = pathlib.Path(sys.argv[1])
sys.path.insert(0, str(root))
stub = types.ModuleType('sigrokdecode')
stub.Decoder = type('Decoder', (), {})
sys.modules['sigrokdecode'] = stub
for name in ('gate_driver_timing', 'transistor_pair', 'set_uart', 'set_can', 'pm35_uart'):
module = importlib.import_module(name + '.pd')
assert module.Decoder.api_version == 3
core = importlib.import_module('common.setgui_decoders.gate_timing')
pathlib.Path(core.__file__).resolve().relative_to(root)
assert '1SP0635' in core.PROFILES
'''
check = subprocess.run([sys.executable, '-I', '-c', code, str(self.app/'decoders')],
cwd=self.app, capture_output=True, text=True)
self.assertEqual(check.returncode, 0, check.stderr)
target = self.app/'decoders/set_uart/pd.py'
target.write_text('# old decoder\n')
result = self.run_installer()
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
backups = list((self.app/'set-decoder-backups').glob('*/set_uart/pd.py'))
self.assertEqual(len(backups), 1)
self.assertEqual(backups[0].read_text(), '# old decoder\n')
self.assertEqual(target.read_bytes(), (ROOT/'decoders/set_uart/pd.py').read_bytes())
self.assertEqual((self.app/'decoders/common/keep.txt').read_text(), 'unrelated')
self.assertEqual((self.app/'decoders/common/__init__.py').read_text(), '# existing common package\n')
for name in ('gate_timing', 'transistor_pair', 'set_uart', 'set_can', 'pm35_uart'):
self.assertEqual((self.app/('decoders/common/setgui_decoders/'+name+'.py')).read_bytes(),
(ROOT.parents[1]/('python/logic_analyzer/decoders/'+name+'.py')).read_bytes())
def test_check_only_does_not_install(self):
result = self.run_installer('-CheckOnly')
self.assertEqual(result.returncode, 0, result.stderr)
self.assertFalse((self.app/'decoders/set_uart').exists())
self.assertFalse((self.app/'set-decoder-backups').exists())
def test_copy_failure_restores_previous_decoders(self):
old = self.app/'decoders/gate_driver_timing/pd.py'
old.parent.mkdir()
old.write_text('# original\n')
runner = Path(self.temp.name)/'inject-failure.ps1'
runner.write_text('''
param([string]$Installer, [string]$Target)
$ErrorActionPreference = 'Stop'
$global:installerTestFailed = $false
function Copy-Item {
param([string]$LiteralPath, [string]$Destination, [switch]$Force)
if (-not $global:installerTestFailed -and $Destination.Replace('\\', '/').EndsWith('/decoders/set_uart/pd.py')) {
$global:installerTestFailed = $true
throw 'Injected copy failure'
}
Microsoft.PowerShell.Management\\Copy-Item -LiteralPath $LiteralPath -Destination $Destination -Force:$Force
}
& $Installer -DsViewPath $Target
''', encoding='utf-8')
result = subprocess.run([SHELL, '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File',
str(runner), '-Installer', str(ROOT/'install_decoders.ps1'),
'-Target', str(self.app)], capture_output=True)
self.assertNotEqual(result.returncode, 0)
self.assertEqual(old.read_text(), '# original\n')
self.assertFalse((old.parent/'__init__.py').exists())
self.assertFalse((self.app/'decoders/set_uart/__init__.py').exists())
self.assertEqual(len(list((self.app/'set-decoder-backups').glob('*/gate_driver_timing/pd.py'))), 1, result.stderr)
def test_incomplete_target_is_untouched(self):
(self.app/'DSView.exe').unlink()
result = self.run_installer()
self.assertNotEqual(result.returncode, 0)
self.assertFalse((self.app/'decoders/set_uart').exists())
def test_missing_source_is_detected_before_changes(self):
source = Path(self.temp.name)/'empty-template/tools/dsview'
source.mkdir(parents=True)
shutil.copy2(ROOT/'install_decoders.ps1', source/'install_decoders.ps1')
result = self.run_installer(script=source/'install_decoders.ps1')
self.assertNotEqual(result.returncode, 0)
self.assertFalse((self.app/'decoders/gate_driver_timing').exists())
self.assertFalse((self.app/'set-decoder-backups').exists())
if __name__ == '__main__':
unittest.main()