Add DSLogic capture, signal conversion and DSView decoders

This commit is contained in:
2026-09-27 01:45:08 +03:00
parent cc22c803d1
commit 513e79b127
44 changed files with 5982 additions and 36 deletions

View File

@@ -0,0 +1,116 @@
"""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()