src/platform/python/cinema/test.py (view raw)
1import os
2import os.path
3import mgba.core
4import mgba.image
5import cinema.movie
6import itertools
7import glob
8import re
9import yaml
10from copy import deepcopy
11from cinema import VideoFrame
12from cinema.util import dict_merge
13
14
15class CinemaTest(object):
16 TEST = 'test.(mvl|gb|gba|nds)'
17
18 def __init__(self, path, root, settings={}):
19 self.full_path = path or []
20 self.path = os.path.abspath(os.path.join(root, *self.full_path))
21 self.root = root
22 self.name = '.'.join(path)
23 self.settings = settings
24 try:
25 with open(os.path.join(self.path, 'manifest.yml'), 'r') as f:
26 dict_merge(self.settings, yaml.safe_load(f))
27 except IOError:
28 pass
29 self.tests = {}
30
31 def __repr__(self):
32 return '<%s %s>' % (self.__class__.__name__, self.name)
33
34 def setup(self):
35 results = [f for f in glob.glob(os.path.join(self.path, 'test.*')) if re.search(self.TEST, f)]
36 self.core = mgba.core.load_path(results[0])
37 if 'config' in self.settings:
38 self.config = mgba.core.Config(defaults=self.settings['config'])
39 self.core.load_config(self.config)
40 self.core.reset()
41
42 def add_test(self, name, cls=None, settings={}):
43 cls = cls or self.__class__
44 new_settings = deepcopy(self.settings)
45 dict_merge(new_settings, settings)
46 self.tests[name] = cls(self.full_path + [name], self.root, new_settings)
47 return self.tests[name]
48
49 def output_settings(self):
50 output_settings = {}
51 if 'frames' in self.settings:
52 output_settings['limit'] = self.settings['frames']
53 if 'skip' in self.settings:
54 output_settings['skip'] = self.settings['skip']
55 return output_settings
56
57 def __lt__(self, other):
58 return self.path < other.path
59
60
61class VideoTest(CinemaTest):
62 BASELINE = 'baseline_%04u.png'
63
64 def setup(self):
65 super(VideoTest, self).setup()
66 self.tracer = cinema.movie.Tracer(self.core)
67
68 def generate_frames(self):
69 for i, frame in zip(itertools.count(), self.tracer.video(**self.output_settings())):
70 try:
71 baseline = VideoFrame.load(os.path.join(self.path, self.BASELINE % i))
72 yield baseline, frame, VideoFrame.diff(baseline, frame)
73 except IOError:
74 yield None, frame, (None, None)
75
76 def test(self):
77 self.baseline, self.frames, self.diffs = zip(*self.generate_frames())
78 assert not any(any(diffs[0].image.convert("L").point(bool).getdata()) for diffs in self.diffs)
79
80 def generate_baseline(self):
81 for i, frame in zip(itertools.count(), self.tracer.video(**self.output_settings())):
82 frame.save(os.path.join(self.path, self.BASELINE % i))
83
84
85def gather_tests(root=os.getcwd()):
86 tests = CinemaTest([], root)
87 for path, _, files in os.walk(root):
88 test = [f for f in files if re.match(CinemaTest.TEST, f)]
89 if not test:
90 continue
91 prefix = os.path.commonprefix([path, root])
92 suffix = path[len(prefix)+1:]
93 test_path = suffix.split(os.sep)
94 test_root = tests
95 for component in test_path[:-1]:
96 new_test = test_root.tests.get(component)
97 if not new_test:
98 new_test = test_root.add_test(component)
99 test_root = new_test
100 test_root.add_test(test_path[-1], VideoTest)
101 return tests