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
9from copy import deepcopy
10from cinema import VideoFrame
11from cinema.util import dict_merge
12from configparser import ConfigParser
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, 'config.ini'), 'r') as f:
26 cfg = ConfigParser()
27 cfg.read_file(f)
28 settings = {}
29 if 'testinfo' in cfg:
30 settings = dict(cfg['testinfo'])
31 if 'ports.cinema' in cfg:
32 settings['config'] = dict(cfg['ports.cinema'])
33 dict_merge(self.settings, settings)
34 except IOError:
35 pass
36 self.tests = {}
37
38 def __repr__(self):
39 return '<%s %s>' % (self.__class__.__name__, self.name)
40
41 def setup(self):
42 results = [f for f in glob.glob(os.path.join(self.path, 'test.*')) if re.search(self.TEST, f)]
43 self.core = mgba.core.load_path(results[0])
44 if 'config' in self.settings:
45 self.config = mgba.core.Config(defaults=self.settings['config'])
46 self.core.load_config(self.config)
47 self.core.reset()
48
49 def add_test(self, name, cls=None, settings={}):
50 cls = cls or self.__class__
51 new_settings = deepcopy(self.settings)
52 dict_merge(new_settings, settings)
53 self.tests[name] = cls(self.full_path + [name], self.root, new_settings)
54 return self.tests[name]
55
56 def output_settings(self):
57 output_settings = {}
58 if 'frames' in self.settings:
59 output_settings['limit'] = int(self.settings['frames'])
60 if 'skip' in self.settings:
61 output_settings['skip'] = int(self.settings['skip'])
62 return output_settings
63
64 def __lt__(self, other):
65 return self.path < other.path
66
67
68class VideoTest(CinemaTest):
69 BASELINE = 'baseline_%04u.png'
70
71 def setup(self):
72 super(VideoTest, self).setup()
73 self.tracer = cinema.movie.Tracer(self.core)
74
75 def generate_frames(self):
76 for i, frame in zip(itertools.count(), self.tracer.video(**self.output_settings())):
77 try:
78 baseline = VideoFrame.load(os.path.join(self.path, self.BASELINE % i))
79 yield baseline, frame, VideoFrame.diff(baseline, frame)
80 except IOError:
81 yield None, frame, (None, None)
82
83 def test(self):
84 self.baseline, self.frames, self.diffs = zip(*self.generate_frames())
85 assert not any(any(diffs[0].image.convert("L").point(bool).getdata()) for diffs in self.diffs)
86
87 def generate_baseline(self):
88 for i, frame in zip(itertools.count(), self.tracer.video(**self.output_settings())):
89 frame.save(os.path.join(self.path, self.BASELINE % i))
90
91
92def gather_tests(root=os.getcwd()):
93 tests = CinemaTest([], root)
94 for path, _, files in os.walk(root):
95 test = [f for f in files if re.match(CinemaTest.TEST, f)]
96 if not test:
97 continue
98 prefix = os.path.commonprefix([path, root])
99 suffix = path[len(prefix)+1:]
100 test_path = suffix.split(os.sep)
101 test_root = tests
102 for component in test_path[:-1]:
103 new_test = test_root.tests.get(component)
104 if not new_test:
105 new_test = test_root.add_test(component)
106 test_root = new_test
107 test_root.add_test(test_path[-1], VideoTest)
108 return tests