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