Files
templates/python/tests/test_logic_analyzer_files.py

116 lines
4.9 KiB
Python

import csv
import json
from pathlib import Path
import struct
import tempfile
import unittest
import zipfile
from logic_analyzer.files import read_capture, ImportCancelled
def run_bytes(length):
value = length - 1
if value < 64:
return bytes([value])
parts = [value & 127]
value >>= 7
while value >= 64:
parts.append(128 | (value & 127))
value >>= 7
return bytes([64 | value] + list(reversed(parts)))
def sal_binary(version=1, initial=1):
head = b'<SALEAE>' + struct.pack('<IIBdQdBBQ', version, 100, 1, 1000, 0, 0, 0, 0, 2)
for start, end, state, runs in [(0, 100, initial, [2, 98]), (100, 105, initial ^ 1, [3, 2])]:
data = b''.join(run_bytes(n) for n in runs)
if version == 1:
head += struct.pack('<6Q', start, end, end-start, 1000, 1, len(data)) + data
head += struct.pack('<QQQI', 1, 0, 0, state)
else:
head += struct.pack('<QQBBQ', start, end, state, 0, len(data)) + data
return head
class LogicFilesTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
def csv_file(self, text):
path = self.root / 'capture.csv'
path.write_text(text, encoding='utf-8-sig')
return path
def test_csv_preserves_irregular_time_and_channel_names(self):
capture = read_capture(self.csv_file('Time [s],Channel 8,Clock\n127.1,1,0\n127.10000024,0,1\n129,0,1\n'))
self.assertEqual(capture.start, 127.1)
self.assertEqual(capture.end, 129)
self.assertEqual(capture.channels[0].name, 'Channel 8')
self.assertEqual(list(capture.channels[0].edges), [127.10000024])
self.assertEqual(capture.channels[0].level_at(127.1), 1)
self.assertEqual(capture.channels[0].level_at(127.10000024), 0)
def test_dsview_csv_metadata_and_units(self):
capture = read_capture(self.csv_file('; CSV, generated by DSView\n; Sample rate: 100 MHz\nTime(ns), Vin, Vstat\n0,0,1\n240,1,0\n'))
self.assertEqual(capture.format, 'DSLogic CSV')
self.assertAlmostEqual(capture.end, 240e-9)
self.assertEqual(capture.channels[1].name, 'Vstat')
def test_csv_rejects_invalid_data(self):
for rows in ['0,0\n0,1', '1,0\n0,1', 'nan,0', '0,0.5', '0,1,0', '']:
with self.subTest(rows=rows), self.assertRaises(ValueError):
read_capture(self.csv_file('Time [s],D0\n' + rows))
def test_sal_chunks_levels_and_long_runs(self):
for version in (1, 3, 4):
for initial in (0, 1):
path = self.root / 'capture.sal'
with zipfile.ZipFile(path, 'w') as archive:
archive.writestr('meta.json', json.dumps({'data': {'rowsSettings': [
{'channel': {'type': 'Digital', 'deviceChannel': 4}, 'name': 'Clock'}]}}))
archive.writestr('digital-4.bin', sal_binary(version, initial))
capture = read_capture(path)
self.assertEqual(capture.sample_rate, 1000)
self.assertEqual(capture.channels[0].name, 'Clock')
self.assertEqual(capture.channels[0].initial, initial)
self.assertEqual(list(capture.channels[0].edges), [0.002, 0.103])
self.assertEqual(capture.end, 0.105)
def test_sal_rejects_truncated_and_unknown_version(self):
for data in (sal_binary()[:-1], sal_binary(77)):
path = self.root / 'bad.sal'
with zipfile.ZipFile(path, 'w') as archive:
archive.writestr('meta.json', '{}')
archive.writestr('digital-0.bin', data)
with self.assertRaises(ValueError):
read_capture(path)
def test_dsl_sparse_channels_blocks_and_padding(self):
path = self.root / 'capture.dsl'
with zipfile.ZipFile(path, 'w') as archive:
archive.writestr('header', '[version]\nversion=3\n[header]\ndevice mode=0\n'
'samplerate=100 MHz\ntotal samples=10\ntotal blocks=2\n'
'probe3=Vin\nprobe12=Ack\n')
archive.writestr('L-0/0', bytes([0b11110000]))
archive.writestr('L-0/1', bytes([0b11111100]))
archive.writestr('L-1/0', bytes([255]))
archive.writestr('L-1/1', bytes([255]))
capture = read_capture(path)
self.assertEqual([c.name for c in capture.channels], ['Vin', 'Ack'])
self.assertEqual(list(capture.channels[0].edges), [4e-8, 8e-8])
self.assertEqual(capture.channels[1].initial, 1)
self.assertEqual(list(capture.channels[1].edges), [])
self.assertEqual(capture.end, 1e-7)
def test_cancelled_import(self):
with self.assertRaises(ImportCancelled):
read_capture(self.csv_file('Time [s],D0\n0,0'), cancel=lambda: True)
if __name__ == '__main__':
unittest.main()