Files
templates/python/tests/test_logic_analyzer_saleae.py

92 lines
4.4 KiB
Python

from dataclasses import replace
from pathlib import Path
import struct
import tempfile
import unittest
from unittest.mock import MagicMock, patch
from logic_analyzer import saleae
from logic_analyzer.files import ImportCancelled
def binary(path, edges=(), initial=0, start=0., end=1., version=0):
Path(path).write_bytes(struct.pack('<8siiIddQ', b'<SALEAE>', version, 0,
initial, start, end, len(edges)) +
struct.pack('<%dd' % len(edges), *edges))
class SaleaeCoreTests(unittest.TestCase):
def test_constant_channel_tail_and_sparse_channel_numbers(self):
with tempfile.TemporaryDirectory() as directory:
binary(Path(directory) / 'digital_2.bin', [.2, .3])
binary(Path(directory) / 'digital_7.bin', initial=1)
capture = saleae.read_binary(directory, (2, 7))
self.assertEqual([c.name for c in capture.channels], ['D2', 'D7'])
self.assertEqual(capture.end, 1.)
self.assertEqual(capture.channels[0].level_at(.25), 1)
self.assertEqual(capture.channels[0].level_at(.9), 0)
self.assertEqual(capture.channels[1].level_at(.9), 1)
def test_reject_bad_binary_and_cancel(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / 'digital_0.bin'
for kwargs in ({'version': 1}, {'edges': [.4, .3]}, {'edges': [float('nan')]},
{'end': -1}, {'initial': 2}):
binary(path, **kwargs)
with self.assertRaises(ValueError):
saleae.read_binary(directory, (0,))
binary(path, [.2])
with self.assertRaises(ImportCancelled):
saleae.read_binary(directory, (0,), lambda: True)
def test_settings_validation(self):
good = saleae.SaleaeSettings('id')
good.validate()
for kwargs in ({'channels': ()}, {'channels': (1, 1)}, {'channels': (16,)},
{'sample_rate': 0}, {'duration': float('nan')}, {'port': 0},
{'buffer_mb': 1}, {'threshold': 5}, {'device_id': ''}):
with self.assertRaises(ValueError):
replace(good, **kwargs).validate()
def fake_api(self):
api = MagicMock()
manager = api.Manager.connect.return_value.__enter__.return_value
capture = manager.start_capture.return_value
def export(directory, **kwargs):
for channel in kwargs['digital_channels']:
binary(Path(directory) / ('digital_%d.bin' % channel), [.25])
capture.export_raw_data_binary.side_effect = export
return api, manager, capture
def test_stop_exports_and_closes_exactly_once(self):
api, manager, capture = self.fake_api()
settings = saleae.SaleaeSettings('abc', channels=(3, 5), threshold=1.8)
with patch.object(saleae, 'automation_api', return_value=api):
result = saleae.acquire(settings, lambda: True, lambda: False)
self.assertEqual(result.sample_rate, 24000000)
self.assertIn('abc', result.source)
self.assertEqual([c.name for c in result.channels], ['D3', 'D5'])
capture.stop.assert_called_once()
capture.wait.assert_not_called()
capture.close.assert_called_once()
api.LogicDeviceConfiguration.assert_called_once_with(
enabled_digital_channels=[3, 5], digital_sample_rate=24000000, digital_threshold_volts=1.8)
self.assertFalse(Path(capture.export_raw_data_binary.call_args[0][0]).exists())
def test_cancel_stops_without_export_and_failure_closes(self):
api, manager, capture = self.fake_api()
with patch.object(saleae, 'automation_api', return_value=api):
calls = iter((False, True, True))
with self.assertRaises(ImportCancelled):
saleae.acquire(saleae.SaleaeSettings('id'), lambda: False, lambda: next(calls))
capture.stop.assert_called_once()
capture.close.assert_called_once()
capture.export_raw_data_binary.assert_not_called()
capture.reset_mock()
capture.stop.side_effect = RuntimeError('USB disconnected')
with patch.object(saleae, 'automation_api', return_value=api):
with self.assertRaisesRegex(RuntimeError, 'USB'):
saleae.acquire(saleae.SaleaeSettings('id'), lambda: True, lambda: False)
capture.stop.assert_called_once()
capture.close.assert_called_once()