test: switch the stroke tests to use pytest

We're already running the meson test through pytest anyway and pytest is more
powerful than unittest. So let's switch, it's just a search/replace away.

Plus, this way the approach to dynamically create the tests based on the test
logs in the user's home directory is a lot saner.

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
pull/248/head
Peter Hutterer 2020-01-08 09:58:27 +10:00 committed by Benjamin Tissoires
parent fcdacb7187
commit 7a881de9bb
1 changed files with 140 additions and 161 deletions

View File

@ -18,8 +18,8 @@
# #
import os import os
import pytest
import sys import sys
import unittest
import xdg.BaseDirectory import xdg.BaseDirectory
from pathlib import Path from pathlib import Path
import yaml import yaml
@ -34,32 +34,47 @@ logger = logging.getLogger('tuhi') # piggyback the debug messages
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
class TestLogFiles(unittest.TestCase): def pytest_generate_tests(metafunc):
''' # for any test function that takes a "logfile" argument return the list
Special test class that loads a yaml file created by tuhi compiles # of all current logfiles in XDG_DATA_HOME/tuhi
the StrokeData from it. This class autogenerates its own tests, see # This means the test gets better the more logfiles are present on the
the main() handling. # user's machine.
''' if 'logfile' in metafunc.fixturenames:
def load_pen_data(self, filename): basedir = Path(xdg.BaseDirectory.xdg_data_home) / 'tuhi'
def loads_and_has_data(filename):
with open(filename) as fd:
try:
yml = yaml.load(fd, Loader=yaml.Loader)
return yml is not None
except Exception as e:
logger.error(f'Exception triggered by file {filename}')
raise e
logfiles = [f for f in basedir.glob('**/raw/log-*.yaml') if loads_and_has_data(f)]
metafunc.parametrize('logfile', logfiles)
def test_log_files(logfile):
def load_pen_data(filename):
with open(filename) as fd: with open(filename) as fd:
yml = yaml.load(fd, Loader=yaml.Loader) yml = yaml.load(fd, Loader=yaml.Loader)
# all recv lists that have source PEN # all recv lists that have source PEN
pendata = [d['recv'] for d in yml['data'] if 'recv' in d and 'source' in d and d['source'] == 'PEN'] pendata = [d['recv'] for d in yml['data'] if 'recv' in d and 'source' in d and d['source'] == 'PEN']
return list(flatten(pendata)) return list(flatten(pendata))
def _test_file(self, fname): data = load_pen_data(logfile)
data = self.load_pen_data(fname) if not data: # Recordings without Pen data can be skipped
if not data: # Recordings without Pen data can be skipped pytest.skip('Recording without pen data')
raise unittest.SkipTest() StrokeFile(data)
StrokeFile(data)
class TestStrokeParsers(unittest.TestCase): class TestStrokeParsers(object):
def test_identify_file_header(self): def test_identify_file_header(self):
data = [0x67, 0x82, 0x69, 0x65] data = [0x67, 0x82, 0x69, 0x65]
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.FILE_HEADER) assert StrokeDataType.identify(data) == StrokeDataType.FILE_HEADER
data = [0x62, 0x38, 0x62, 0x74] data = [0x62, 0x38, 0x62, 0x74]
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.FILE_HEADER) assert StrokeDataType.identify(data) == StrokeDataType.FILE_HEADER
others = [ others = [
# with header # with header
@ -77,54 +92,54 @@ class TestStrokeParsers(unittest.TestCase):
[0x62, 0x38, 0x62, 0x73], [0x62, 0x38, 0x62, 0x73],
] ]
for data in others: for data in others:
self.assertNotEqual(StrokeDataType.identify(data), StrokeDataType.FILE_HEADER, msg=data) assert StrokeDataType.identify(data) != StrokeDataType.FILE_HEADER, data
def test_identify_stroke_header(self): def test_identify_stroke_header(self):
data = [0xff, 0xfa] # two bytes are enough to identify data = [0xff, 0xfa] # two bytes are enough to identify
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.STROKE_HEADER) assert StrokeDataType.identify(data) == StrokeDataType.STROKE_HEADER
data = [0x3, 0xfa] # lowest bits set, not a correct packet but identify doesn't care data = [0x3, 0xfa] # lowest bits set, not a correct packet but identify doesn't care
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.STROKE_HEADER) assert StrokeDataType.identify(data) == StrokeDataType.STROKE_HEADER
data = [0xfc, 0xfa] # lowest bits unset, must be something else data = [0xfc, 0xfa] # lowest bits unset, must be something else
self.assertNotEqual(StrokeDataType.identify(data), StrokeDataType.STROKE_HEADER) assert StrokeDataType.identify(data) != StrokeDataType.STROKE_HEADER
def test_identify_stroke_point(self): def test_identify_stroke_point(self):
data = [0xff, 0xff, 0xff] # three bytes are enough to identify data = [0xff, 0xff, 0xff] # three bytes are enough to identify
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.POINT) assert StrokeDataType.identify(data) == StrokeDataType.POINT
data = [0xff, 0xff, 0xff, 1, 2, 3, 4, 5, 6] data = [0xff, 0xff, 0xff, 1, 2, 3, 4, 5, 6]
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.POINT) assert StrokeDataType.identify(data) == StrokeDataType.POINT
# wrong header, but observed in the wild # wrong header, but observed in the wild
data = [0xbf, 0xff, 0xff, 1, 2, 3, 4, 5, 6] data = [0xbf, 0xff, 0xff, 1, 2, 3, 4, 5, 6]
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.POINT) assert StrokeDataType.identify(data) == StrokeDataType.POINT
data = [0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] # stroke end data = [0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] # stroke end
self.assertNotEqual(StrokeDataType.identify(data), StrokeDataType.POINT) assert StrokeDataType.identify(data) != StrokeDataType.POINT
data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] # EOF data = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] # EOF
self.assertNotEqual(StrokeDataType.identify(data), StrokeDataType.POINT) assert StrokeDataType.identify(data) != StrokeDataType.POINT
def test_identify_stroke_lost_point(self): def test_identify_stroke_lost_point(self):
data = [0xff, 0xdd, 0xdd] data = [0xff, 0xdd, 0xdd]
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.LOST_POINT) assert StrokeDataType.identify(data) == StrokeDataType.LOST_POINT
def test_identify_eof(self): def test_identify_eof(self):
data = [0xff] * 9 data = [0xff] * 9
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.EOF) assert StrokeDataType.identify(data) == StrokeDataType.EOF
def test_identify_stroke_end(self): def test_identify_stroke_end(self):
data = [0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff] data = [0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.STROKE_END) assert StrokeDataType.identify(data) == StrokeDataType.STROKE_END
def test_identify_delta(self): def test_identify_delta(self):
for i in range(256): for i in range(256):
data = [i, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6] data = [i, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]
if i & 0x3 == 0: if i & 0x3 == 0:
self.assertEqual(StrokeDataType.identify(data), StrokeDataType.DELTA, f'packet: {data}') assert StrokeDataType.identify(data), StrokeDataType.DELTA == f'packet: {data}'
else: else:
self.assertNotEqual(StrokeDataType.identify(data), StrokeDataType.DELTA, f'packet: {data}') assert StrokeDataType.identify(data), StrokeDataType.DELTA != f'packet: {data}'
def test_parse_stroke_header(self): def test_parse_stroke_header(self):
F_NEW_LAYER = 0x40 F_NEW_LAYER = 0x40
@ -134,81 +149,81 @@ class TestStrokeParsers(unittest.TestCase):
data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01] data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01]
packet = StrokeHeader(data) packet = StrokeHeader(data)
self.assertEqual(packet.size, 9) assert packet.size == 9
self.assertEqual(packet.is_new_layer, 1) assert packet.is_new_layer == 1
self.assertEqual(packet.pen_id, 0) assert packet.pen_id == 0
self.assertEqual(packet.pen_type, pen_type) assert packet.pen_type == pen_type
self.assertEqual(packet.timestamp, 1565750047) assert packet.timestamp == 1565750047
# new layer off # new layer off
flags = pen_type flags = pen_type
data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01] data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01]
packet = StrokeHeader(data) packet = StrokeHeader(data)
self.assertEqual(packet.size, 9) assert packet.size == 9
self.assertEqual(packet.is_new_layer, 0) assert packet.is_new_layer == 0
self.assertEqual(packet.pen_id, 0) assert packet.pen_id == 0
self.assertEqual(packet.pen_type, pen_type) assert packet.pen_type == pen_type
self.assertEqual(packet.timestamp, 1565750047) assert packet.timestamp == 1565750047
# pen type change # pen type change
pen_type = 1 pen_type = 1
flags = F_NEW_LAYER | pen_type flags = F_NEW_LAYER | pen_type
data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01] data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01]
packet = StrokeHeader(data) packet = StrokeHeader(data)
self.assertEqual(packet.size, 9) assert packet.size == 9
self.assertEqual(packet.is_new_layer, 1) assert packet.is_new_layer == 1
self.assertEqual(packet.pen_id, 0) assert packet.pen_id == 0
self.assertEqual(packet.pen_type, pen_type) assert packet.pen_type == pen_type
self.assertEqual(packet.timestamp, 1565750047) assert packet.timestamp == 1565750047
# with pen id # with pen id
flags = F_NEW_LAYER | F_PEN_ID | pen_type flags = F_NEW_LAYER | F_PEN_ID | pen_type
pen_id = [0xff, 0x0a, 0x87, 0x75, 0x80, 0x28, 0x42, 0x00, 0x10] pen_id = [0xff, 0x0a, 0x87, 0x75, 0x80, 0x28, 0x42, 0x00, 0x10]
data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01] + pen_id data = [0xff, 0xfa, flags, 0x1f, 0x73, 0x53, 0x5d, 0x2e, 0x01] + pen_id
packet = StrokeHeader(data) packet = StrokeHeader(data)
self.assertEqual(packet.size, 18) assert packet.size == 18
self.assertEqual(packet.is_new_layer, 1) assert packet.is_new_layer == 1
self.assertEqual(packet.pen_id, 0x100042288075870a) assert packet.pen_id == 0x100042288075870a
self.assertEqual(packet.pen_type, pen_type) assert packet.pen_type == pen_type
self.assertEqual(packet.timestamp, 1565750047) assert packet.timestamp == 1565750047
def test_parse_stroke_point(self): def test_parse_stroke_point(self):
# 0xff means 2 bytes each for abs coords # 0xff means 2 bytes each for abs coords
data = [0xff, 0xff, 0xff, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6] data = [0xff, 0xff, 0xff, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]
packet = StrokePoint(data) packet = StrokePoint(data)
self.assertEqual(packet.size, 9) assert packet.size == 9
self.assertEqual(packet.x, 0x0201) assert packet.x == 0x0201
self.assertEqual(packet.y, 0x0403) assert packet.y == 0x0403
self.assertEqual(packet.p, 0x0605) assert packet.p == 0x0605
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertIsNone(packet.dp) assert packet.dp is None
# 0xbf means: 1 byte for pressure delta, i.e. the 0x6 is skipped # 0xbf means: 1 byte for pressure delta, i.e. the 0x6 is skipped
data = [0xbf, 0xff, 0xff, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6] data = [0xbf, 0xff, 0xff, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]
packet = StrokePoint(data) packet = StrokePoint(data)
self.assertEqual(packet.size, 8) assert packet.size == 8
self.assertEqual(packet.x, 0x0201) assert packet.x == 0x0201
self.assertEqual(packet.y, 0x0403) assert packet.y == 0x0403
self.assertIsNone(packet.p) assert packet.p is None
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertEqual(packet.dp, 0x5) assert packet.dp == 0x5
def test_parse_lost_point(self): def test_parse_lost_point(self):
data = [0xff, 0xdd, 0xdd, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6] data = [0xff, 0xdd, 0xdd, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]
packet = StrokeLostPoint(data) packet = StrokeLostPoint(data)
self.assertEqual(packet.size, 9) assert packet.size == 9
self.assertEqual(packet.nlost, 0x0201) assert packet.nlost == 0x0201
def test_parse_eof(self): def test_parse_eof(self):
data = [0xff] * 9 data = [0xff] * 9
packet = StrokeEOF(data) packet = StrokeEOF(data)
self.assertEqual(packet.size, 9) assert packet.size == 9
data = [0xfc] + [0xff] * 6 data = [0xfc] + [0xff] * 6
packet = StrokeEOF(data) packet = StrokeEOF(data)
self.assertEqual(packet.size, 7) assert packet.size == 7
def test_parse_delta(self): def test_parse_delta(self):
x_delta = 0b00001000 # noqa x_delta = 0b00001000 # noqa
@ -221,93 +236,93 @@ class TestStrokeParsers(unittest.TestCase):
flags = x_delta flags = x_delta
data = [flags, 1] data = [flags, 1]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertEqual(packet.dx, 1) assert packet.dx == 1
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertIsNone(packet.dp) assert packet.dp is None
self.assertIsNone(packet.x) assert packet.x is None
self.assertIsNone(packet.y) assert packet.y is None
self.assertIsNone(packet.p) assert packet.p is None
flags = y_delta flags = y_delta
data = [flags, 2] data = [flags, 2]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertEqual(packet.dy, 2) assert packet.dy == 2
self.assertIsNone(packet.dp) assert packet.dp is None
self.assertIsNone(packet.x) assert packet.x is None
self.assertIsNone(packet.y) assert packet.y is None
self.assertIsNone(packet.p) assert packet.p is None
flags = p_delta flags = p_delta
data = [flags, 3] data = [flags, 3]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertEqual(packet.dp, 3) assert packet.dp == 3
self.assertIsNone(packet.x) assert packet.x is None
self.assertIsNone(packet.y) assert packet.y is None
self.assertIsNone(packet.p) assert packet.p is None
flags = x_delta | p_delta flags = x_delta | p_delta
data = [flags, 3, 5] data = [flags, 3, 5]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertEqual(packet.dx, 3) assert packet.dx == 3
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertEqual(packet.dp, 5) assert packet.dp == 5
self.assertIsNone(packet.x) assert packet.x is None
self.assertIsNone(packet.y) assert packet.y is None
self.assertIsNone(packet.p) assert packet.p is None
flags = x_delta | y_delta | p_delta flags = x_delta | y_delta | p_delta
data = [flags, 3, 5, 7] data = [flags, 3, 5, 7]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertEqual(packet.dx, 3) assert packet.dx == 3
self.assertEqual(packet.dy, 5) assert packet.dy == 5
self.assertEqual(packet.dp, 7) assert packet.dp == 7
self.assertIsNone(packet.x) assert packet.x is None
self.assertIsNone(packet.y) assert packet.y is None
self.assertIsNone(packet.p) assert packet.p is None
flags = x_abs | y_abs | p_abs flags = x_abs | y_abs | p_abs
data = [flags, 1, 2, 3, 4, 5, 6] data = [flags, 1, 2, 3, 4, 5, 6]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertEqual(packet.x, 0x0201) assert packet.x == 0x0201
self.assertEqual(packet.y, 0x0403) assert packet.y == 0x0403
self.assertEqual(packet.p, 0x0605) assert packet.p == 0x0605
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertIsNone(packet.dp) assert packet.dp is None
flags = y_abs flags = y_abs
data = [flags, 2, 3] data = [flags, 2, 3]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertIsNone(packet.x) assert packet.x is None
self.assertEqual(packet.y, 0x0302) assert packet.y == 0x0302
self.assertIsNone(packet.p) assert packet.p is None
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertIsNone(packet.dy) assert packet.dy is None
self.assertIsNone(packet.dp) assert packet.dp is None
flags = x_abs | y_delta | p_delta flags = x_abs | y_delta | p_delta
data = [flags, 2, 3, 4, 5] data = [flags, 2, 3, 4, 5]
packet = StrokeDelta(data) packet = StrokeDelta(data)
self.assertEqual(packet.size, len(data)) assert packet.size == len(data)
self.assertEqual(packet.x, 0x0302) assert packet.x == 0x0302
self.assertIsNone(packet.y) assert packet.y is None
self.assertIsNone(packet.p) assert packet.p is None
self.assertIsNone(packet.dx) assert packet.dx is None
self.assertEqual(packet.dy, 4) assert packet.dy == 4
self.assertEqual(packet.dp, 5) assert packet.dp == 5
class TestStrokes(unittest.TestCase): class TestStrokes(object):
def test_single_stroke(self): def test_single_stroke(self):
data = ''' data = '''
67 82 69 65 22 73 53 5d 00 00 02 00 00 00 00 00 ff fa c3 1f 67 82 69 65 22 73 53 5d 00 00 02 00 00 00 00 00 ff fa c3 1f
@ -428,39 +443,3 @@ class TestStrokes(unittest.TestCase):
p = Protocol(ProtocolVersion.INTUOS_PRO, None, None) p = Protocol(ProtocolVersion.INTUOS_PRO, None, None)
p.parse_pen_data(b) p.parse_pen_data(b)
# How does this work?
# The test generater spits out a simple test function that just calls the
# real test function
#
# Then we search for all yaml files with logs we have and generate a unique
# test name (based on the timestamp) for that file. Result: we have
# a unittests function for each log file found in the directory.
def generator(logfile):
def test(self):
self._test_file(logfile)
return test
def search_for_tests():
basedir = Path(xdg.BaseDirectory.xdg_data_home) / 'tuhi'
for logfile in basedir.glob('**/raw/log-*.yaml'):
with open(logfile) as fd:
try:
yml = yaml.load(fd, Loader=yaml.Loader)
if not yml:
continue
timestamp = yml['time']
test_name = f'test_log_{timestamp}'
test = generator(logfile)
setattr(TestLogFiles, test_name, test)
except Exception as e:
logger.error(f'Exception triggered by file {logfile}')
raise e
search_for_tests()
if __name__ == '__main__':
unittest.main()