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