117 lines
5.7 KiB
Python
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()
|