all repos — mgba @ 11b8167708a23ca36bc7591235ee3fcb94556257

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 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