all repos — mgba @ aa9c9c40f7b0851da74710cac4a2bc1226a27535

mGBA Game Boy Advance Emulator

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