all repos — mgba @ d6f041d5e79659245731e51b30732421be116890

mGBA Game Boy Advance Emulator

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