92 lines
4.4 KiB
Python
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()
|