Ejemplo n.º 1
0
    def _start_mc(self):
        from mpfmc.core.mc import MpfMc

        # prevent sleep in clock
        Clock._max_fps = 0

        mpf_config = ConfigProcessor.load_config_file(
            os.path.abspath(
                os.path.join(mpfmc.__path__[0], os.pardir,
                             self.get_options()['mcconfigfile'])), 'machine')

        machine_path = self.getAbsoluteMachinePath()

        mpf_config = load_machine_config(
            Util.string_to_list(self.getConfigFile()), machine_path,
            mpf_config['mpf-mc']['paths']['config'], mpf_config)
        self.preprocess_config(mpf_config)

        self.mc = MpfMc(options=self.get_options(),
                        config=mpf_config,
                        machine_path=machine_path)

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()
Ejemplo n.º 2
0
    def _start_mc(self):
        from mpfmc.core.mc import MpfMc

        # prevent sleep in clock
        Clock._max_fps = 0

        machine_path = self.getAbsoluteMachinePath()

        self.mc = MpfMc(options=self.get_options(), machine_path=machine_path)

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()
Ejemplo n.º 3
0
    def run(self, name):
        Clock._events = [[] for i in range(256)]
        self._test_started = time()
        self._test_name = self.id()
        self._test = name
        # This setup is done in run() because we need to give control to the
        # kivy event loop which we can only do by returning from the run()
        # that's called. So we override run() and setup mpf-mc and then call
        # our own run_test() on a callback. Then we can wait until the
        # environment is setup (which can take a few frames), then we call
        # super().run() to get the actual TestCase.run() method to run and
        # we return the results.

        # We have to do this in run() and not setUp() because run actually
        # calls setUp(), so since we were overriding it ours doesn't call it
        # so we just do our setup here since if we manually called setUp() then
        # it would be called again when we call super().run().

        from mpf.core.player import Player
        Player.monitor_enabled = False

        mpf_config = ConfigProcessor.load_config_file(
            os.path.abspath(
                os.path.join(mpfmc.__path__[0], os.pardir,
                             self.get_options()['mcconfigfile'])), 'machine')

        machine_path = os.path.abspath(
            os.path.join(mpfmc.__path__[0], os.pardir, 'mpfmc',
                         self.get_machine_path()))

        mpf_config = load_machine_config(
            Util.string_to_list(self.get_config_file()), machine_path,
            mpf_config['mpf-mc']['paths']['config'], mpf_config)
        self.preprocess_config(mpf_config)

        self.mc = MpfMc(options=self.get_options(),
                        config=mpf_config,
                        machine_path=machine_path)

        self.patch_bcp()

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        Clock.schedule_once(self.run_test, 0)
        self.mc.run()
    def _start_mc(self):
        from mpfmc.core.mc import MpfMc

        # prevent sleep in clock
        Clock._max_fps = 0

        machine_path = self.get_absolute_machine_path()

        config_loader = UnitTestConfigLoader(machine_path, self.machine.options['configfile'], {}, {}, {})

        config = config_loader.load_mc_config()

        self.mc = MpfMc(config=config, options=self.get_options())

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()
Ejemplo n.º 5
0
    def setUp(self):
        # Most of the setup is done in run(). Explanation is there.
        Config._named_configs.pop('app', None)

        self._start_time = time()
        self._current_time = self._start_time
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time

        # prevent sleep in clock
        Clock._max_fps = 0
        # reset clock
        Clock._root_event = None

        from mpf.core.player import Player
        Player.monitor_enabled = False

        machine_path = self.get_absolute_machine_path()

        # load config
        config_loader = UnitTestConfigLoader(machine_path,
                                             [self.get_config_file()], {}, {},
                                             {})

        config = config_loader.load_mc_config()

        try:
            self.mc = MpfMc(options=self.get_options(), config=config)

            self.patch_bcp()

            from kivy.core.window import Window
            Window.create_window()
            Window.canvas.clear()

            self._start_app_as_slave()
        except Exception:
            if hasattr(self, "mc") and self.mc:
                # prevent dead locks with two asset manager threads
                self.mc.stop()
            raise
Ejemplo n.º 6
0
    def setUp(self):
        # Most of the setup is done in run(). Explanation is there.
        Config._named_configs.pop('app', None)

        self._start_time = time()
        self._current_time = self._start_time
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time

        # prevent sleep in clock
        Clock._max_fps = 0
        Clock._events = [[] for i in range(256)]
        self._test_started = self._start_time

        from mpf.core.player import Player
        Player.monitor_enabled = False

        mpf_config = ConfigProcessor.load_config_file(
            os.path.abspath(
                os.path.join(mpfmc.__path__[0], os.pardir,
                             self.get_options()['mcconfigfile'])), 'machine')

        machine_path = self.getAbsoluteMachinePath()

        mpf_config = load_machine_config(
            Util.string_to_list(self.get_config_file()), machine_path,
            mpf_config['mpf-mc']['paths']['config'], mpf_config)
        self.preprocess_config(mpf_config)

        self.mc = MpfMc(options=self.get_options(),
                        config=mpf_config,
                        machine_path=machine_path)

        self.patch_bcp()

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()
Ejemplo n.º 7
0
    def setUp(self):
        # Most of the setup is done in run(). Explanation is there.
        Config._named_configs.pop('app', None)

        self._start_time = time()
        self._current_time = self._start_time
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time

        # prevent sleep in clock
        Clock._max_fps = 0
        Clock._events = [[] for i in range(256)]
        self._test_started = self._start_time

        from mpf.core.player import Player
        Player.monitor_enabled = False

        machine_path = self.getAbsoluteMachinePath()

        try:
            self.mc = MpfMc(options=self.get_options(),
                            machine_path=machine_path)

            self.patch_bcp()

            from kivy.core.window import Window
            Window.create_window()
            Window.canvas.clear()

            self._start_app_as_slave()
        except Exception:
            if self.mc:
                # prevent dead locks with two asset manager threads
                self.mc.stop()
            raise
Ejemplo n.º 8
0
class TestBcpClient(MockBcpClient):
    def __init__(self, machine, name, bcp):
        super().__init__(machine, name, bcp)
        self.queue = Queue()
        self.exit_on_close = False

        self.fps = 30

        self._start_time = time.time()
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time
        Clock._events = [[] for i in range(256)]
        with patch("mpfmc.core.bcp_processor.BCPServer"):
            self._start_mc()
        self.mc_task = self.machine.clock.schedule_interval(self._run_mc, 1 / self.fps)

        bcp_mc = self.mc.bcp_processor
        bcp_mc.send = self.receive
        self.queue = bcp_mc.receive_queue
        self.mc.bcp_processor.enabled = True
        self.mc.bcp_client_connected = True
        self.mc.events.post("client_connected")

    def getAbsoluteMachinePath(self):
        # creates an absolute path based on machine_path
        return self.machine.machine_path

    def get_options(self):
        return dict(machine_path=self.getAbsoluteMachinePath(),
                    mcconfigfile='mcconfig.yaml',
                    production=False,
                    configfile=self.machine.options['configfile'],
                    no_load_cache=False,
                    create_config_cache=True,
                    bcp=False)

    def preprocess_config(self, config):
        # TODO this method is copied from the mc.py launcher. Prob a better way
        kivy_config = config['kivy_config']

        try:
            kivy_config['graphics'].update(config['displays']['window'])
        except KeyError:
            pass

        try:
            kivy_config['graphics'].update(config['window'])
        except KeyError:
            pass

        if 'top' in kivy_config['graphics'] and 'left' in kivy_config['graphics']:
            kivy_config['graphics']['position'] = 'custom'

        for section, settings in kivy_config.items():
            for k, v in settings.items():
                try:
                    if k in Config[section]:
                        Config.set(section, k, v)
                except KeyError:
                    continue

    def _start_app_as_slave(self):
        # from app::run
        if not self.mc.built:
            self.mc.load_config()
            self.mc.load_kv(filename=self.mc.kv_file)
            root = self.mc.build()
            if root:
                self.mc.root = root
        if self.mc.root:
            if not isinstance(self.mc.root, KivyWidget):
                Logger.critical('App.root must be an _instance_ of Kivy Widget')
                raise Exception('Invalid instance in App.root')
            from kivy.core.window import Window
            Window.add_widget(self.mc.root)

        # Check if the window is already created
        from kivy.base import EventLoop
        window = EventLoop.window
        if window:
            self.mc._app_window = window
            window.set_title(self.mc.get_application_name())
            icon = self.mc.get_application_icon()
            if icon:
                window.set_icon(icon)
            self.mc._install_settings_keys(window)
        else:
            Logger.critical("Application: No window is created."
                            " Terminating application run.")
            return

        self.mc.dispatch('on_start')
        runTouchApp(slave=True)  # change is here

        while not self.mc.is_init_done.is_set():
            EventLoop.idle()

    def _start_mc(self):
        from mpfmc.core.mc import MpfMc

        # prevent sleep in clock
        Clock._max_fps = 0

        machine_path = self.getAbsoluteMachinePath()

        self.mc = MpfMc(options=self.get_options(),
                        machine_path=machine_path)

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()

    def _mc_time(self):
        return self._start_time + self.machine.clock.loop._time

    def _run_mc(self):
        EventLoop.idle()

    def stop(self):
        self.mc.stop()
        self.machine.clock.unschedule(self.mc_task)

    def send(self, bcp_command, kwargs):
        self.queue.put((bcp_command, kwargs))

    def receive(self, bcp_command, callback=None, rawbytes=None, **kwargs):
        if rawbytes:
            kwargs['rawbytes'] = rawbytes
        self.receive_queue.put_nowait((bcp_command, kwargs))

        if callback:
            callback()
Ejemplo n.º 9
0
class MpfIntegrationTestCase(MpfTestCase):

    fps = 30

    def get_use_bcp(self):
        return True

    def getAbsoluteMachinePath(self):
        # creates an absolute path based on machine_path
        return os.path.abspath(
            os.path.join(mpfmc.core.__path__[0], os.pardir,
                         self.getMachinePath()))

    def get_options(self):
        return dict(machine_path=self.getAbsoluteMachinePath(),
                    mcconfigfile='mpfmc/mcconfig.yaml',
                    configfile=Util.string_to_list(self.getConfigFile()),
                    bcp=False)

    def preprocess_config(self, config):
        # TODO this method is copied from the mc.py launcher. Prob a better way
        kivy_config = config['kivy_config']

        try:
            kivy_config['graphics'].update(config['displays']['window'])
        except KeyError:
            pass

        try:
            kivy_config['graphics'].update(config['window'])
        except KeyError:
            pass

        if 'top' in kivy_config['graphics'] and 'left' in kivy_config[
                'graphics']:
            kivy_config['graphics']['position'] = 'custom'

        for section, settings in kivy_config.items():
            for k, v in settings.items():
                try:
                    if k in Config[section]:
                        Config.set(section, k, v)
                except KeyError:
                    continue

    def _start_app_as_slave(self):
        # from app::run
        if not self.mc.built:
            self.mc.load_config()
            self.mc.load_kv(filename=self.mc.kv_file)
            root = self.mc.build()
            if root:
                self.mc.root = root
        if self.mc.root:
            if not isinstance(self.mc.root, Widget):
                Logger.critical('App.root must be an _instance_ of Widget')
                raise Exception('Invalid instance in App.root')
            from kivy.core.window import Window
            Window.add_widget(self.mc.root)

        # Check if the window is already created
        from kivy.base import EventLoop
        window = EventLoop.window
        if window:
            self.mc._app_window = window
            window.set_title(self.mc.get_application_name())
            icon = self.mc.get_application_icon()
            if icon:
                window.set_icon(icon)
            self.mc._install_settings_keys(window)
        else:
            Logger.critical("Application: No window is created."
                            " Terminating application run.")
            return

        self.mc.dispatch('on_start')
        runTouchApp(slave=True)  # change is here

        while not self.mc.is_init_done:
            EventLoop.idle()

    def _start_mc(self):
        from mpfmc.core.mc import MpfMc

        # prevent sleep in clock
        Clock._max_fps = 0

        mpf_config = ConfigProcessor.load_config_file(
            os.path.abspath(
                os.path.join(mpfmc.__path__[0], os.pardir,
                             self.get_options()['mcconfigfile'])), 'machine')

        machine_path = self.getAbsoluteMachinePath()

        mpf_config = load_machine_config(
            Util.string_to_list(self.getConfigFile()), machine_path,
            mpf_config['mpf-mc']['paths']['config'], mpf_config)
        self.preprocess_config(mpf_config)

        self.mc = MpfMc(options=self.get_options(),
                        config=mpf_config,
                        machine_path=machine_path)

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        self._start_app_as_slave()

    def _mc_time(self):
        return self._start_time + self.loop._time

    def _run_mc(self, dt):
        del dt
        if self.unittest_verbosity() > 1:
            time.sleep(.05)
        EventLoop.idle()

    def get_enable_plugins(self):
        return True

    def __init__(self, methodName):
        super().__init__(methodName)
        try:
            del self.machine_config_patches['mpf']['plugins']
        except KeyError:
            pass
        self.machine_config_patches['bcp'] = \
            {"connections": {"local_display": {"type": "mpfmc.tests.MpfIntegrationTestCase.TestBcpClient"}}}
        self.machine_config_patches['bcp']['servers'] = []
        self.expected_duration = 60

    def setUp(self):
        super().setUp()
        self._start_time = time.time()
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time
        Clock._events = [[] for i in range(256)]
        with patch("mpfmc.core.bcp_processor.BCPServer"):
            self._start_mc()
        self.mc_task = self.clock.schedule_interval(self._run_mc, 1 / self.fps)

        client = self.machine.bcp.transport.get_named_client("local_display")
        bcp_mc = self.mc.bcp_processor
        bcp_mc.send = client.receive
        self.mc.events.post("client_connected")
        self.advance_time_and_run()
        while not client.queue.empty():
            bcp_mc.receive_queue.put(client.queue.get())
        client.queue = bcp_mc.receive_queue
        self.advance_time_and_run()

    def tearDown(self):
        self.mc.stop()
        self.clock.unschedule(self.mc_task)
        super().tearDown()
        EventLoop.close()
Ejemplo n.º 10
0
Archivo: mc.py Proyecto: unRARed/mpf-mc
    def __init__(self, mpf_path, machine_path, args):
        """Run MC."""
        p = psutil.Process(os.getpid())
        # increase priority slightly. this will keep MPF responsive when MC lags
        if sys.platform == "win32":
            p.nice(psutil.BELOW_NORMAL_PRIORITY_CLASS)
        else:
            p.nice(10)
        # undo all of Kivy's built-in logging so we can do it our way
        os.environ['KIVY_NO_FILELOG'] = '1'
        os.environ['KIVY_NO_CONSOLELOG'] = '1'
        from kivy.logger import Logger

        for handler in Logger.handlers:
            Logger.removeHandler(handler)
        sys.stderr = sys.__stderr__

        # Need to have these in here because we don't want them to load when
        # the module is loaded as an mpf.command
        from mpf.core.utility_functions import Util

        del mpf_path

        parser = argparse.ArgumentParser(
            description='Starts the MPF Media Controller')

        parser.add_argument("-b",
                            action="store_false",
                            dest="bcp",
                            default=True,
                            help="Do not set up the BCP server threads")

        parser.add_argument(
            "-c",
            action="store",
            dest="configfile",
            default="config",
            metavar='config_file(s)',
            help="The name of a config file to load. Default is "
            "config.yaml. Multiple files can be used via a comma-"
            "separated list (no spaces between)")

        parser.add_argument(
            "-C",
            action="store",
            dest="mcconfigfile",
            default="mcconfig.yaml",
            metavar='config_file',
            help="The MPF framework default config file. Default is "
            "<mpf-mc install folder>/mcconfig.yaml")

        parser.add_argument("-f",
                            action="store_true",
                            dest="force_assets_load",
                            default=False,
                            help="Load all assets upon startup. Useful for "
                            "ensuring all assets are set up properly "
                            "during development.")

        parser.add_argument(
            "-l",
            action="store",
            dest="logfile",
            metavar='file_name',
            default=os.path.join(
                "logs",
                datetime.now().strftime("%Y-%m-%d-%H-%M-%S-mc-" +
                                        socket.gethostname() + ".log")),
            help="The name (and path) of the log file")

        parser.add_argument("-L",
                            action="store",
                            dest="mc_logfile",
                            metavar='file_name',
                            default=None,
                            help="The name (and path) of the log file")

        parser.add_argument("-p",
                            action="store_true",
                            dest="pause",
                            default=False,
                            help="Pause the terminal window on exit. Useful "
                            "when launching in a separate window so you can "
                            "see any errors before the window closes.")

        parser.add_argument(
            "-P",
            action="store_true",
            dest="production",
            default=False,
            help=
            "Production mode. Will suppress errors, wait for hardware on start and "
            "try to exit when startup fails. Run this inside a loop.")

        parser.add_argument("-t",
                            action="store_false",
                            dest='text_ui',
                            default=True,
                            help="Use the ASCII text-based UI")

        parser.add_argument("-v",
                            action="store_const",
                            dest="loglevel",
                            const=logging.DEBUG,
                            default=logging.INFO,
                            help="Enables verbose logging to the"
                            " log file")

        parser.add_argument(
            "-V",
            action="store_true",
            dest="consoleloglevel",
            default=logging.INFO,
            help="Enables verbose logging to the console. Do NOT on "
            "Windows platforms")

        parser.add_argument("-a",
                            action="store_true",
                            dest="no_load_cache",
                            help="Forces the config to be loaded from files "
                            "and not cache")

        parser.add_argument("-A",
                            action="store_false",
                            dest="create_config_cache",
                            help="Does not create the cache config files")

        # The following are just included for full compatibility with mpf.py
        # which is needed when using "mpf both".

        parser.add_argument("-x",
                            action="store_const",
                            dest="force_platform",
                            const='virtual',
                            help=argparse.SUPPRESS)

        parser.add_argument("-X",
                            action="store_const",
                            dest="force_platform",
                            const='smart_virtual',
                            help=argparse.SUPPRESS)

        args = parser.parse_args(args)

        args.configfile = Util.string_to_list(args.configfile)

        # Configure logging. Creates a logfile and logs to the console.
        # Formatting options are documented here:
        # https://docs.python.org/2.7/library/logging.html#logrecord-attributes

        try:
            os.makedirs(os.path.join(machine_path, 'logs'))
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

        if args.mc_logfile:
            args.logfile = args.mc_logfile

        full_logfile_path = os.path.join(machine_path, args.logfile)

        try:
            os.remove(full_logfile_path)
        except OSError:
            pass

        logging.basicConfig(level=args.loglevel,
                            format='%(asctime)s : %(name)s : %(message)s',
                            filename=full_logfile_path,
                            filemode='a')

        # define a Handler which writes INFO messages or higher to the
        # sys.stderr

        if args.text_ui:
            console = logging.NullHandler()
            console.setLevel(logging.ERROR)
        else:
            console = logging.StreamHandler()
            console.setLevel(args.consoleloglevel)

        # set a format which is simpler for console use
        formatter = logging.Formatter('%(name)s: %(message)s')

        # tell the handler to use this format
        console.setFormatter(formatter)

        # add the handler to the root logger
        logging.getLogger('').addHandler(console)

        from mpfmc.core.mc import MpfMc

        logging.info("Loading MPF-MC controller")

        thread_stopper = threading.Event()

        try:
            MpfMc(options=vars(args),
                  machine_path=machine_path,
                  thread_stopper=thread_stopper).run()
            logging.info("MC run loop ended.")
        except Exception as e:  # noqa
            logging.exception(str(e))

        logging.info("Stopping child threads... (%s remaining)",
                     len(threading.enumerate()) - 1)

        thread_stopper.set()

        while len(threading.enumerate()) > 1:
            time.sleep(.1)

        logging.info("All child threads stopped.")
        logging.shutdown()

        if args.pause:
            input('Press ENTER to continue...')

        sys.exit()
Ejemplo n.º 11
0
    def __init__(self, mpf_path, machine_path, args):

        # undo all of Kivy's built-in logging so we can do it our way
        os.environ['KIVY_NO_FILELOG'] = '1'
        os.environ['KIVY_NO_CONSOLELOG'] = '1'
        from kivy.logger import Logger

        for handler in Logger.handlers:
            Logger.removeHandler(handler)
        sys.stderr = sys.__stderr__

        # Need to have these in here because we don't want them to load when
        # the module is loaded as an mpf.command
        import mpfmc
        from mpf.core.utility_functions import Util
        from mpfmc.core.config_processor import ConfigProcessor
        from mpfmc.core.utils import set_machine_path, load_machine_config

        del mpf_path

        parser = argparse.ArgumentParser(description='Starts the MPF Media Controller')

        parser.add_argument("-b",
                            action="store_false", dest="bcp", default=True,
                            help="Do not set up the BCP server threads")

        parser.add_argument("-c",
                            action="store", dest="configfile",
                            default="config", metavar='config_file(s)',
                            help="The name of a config file to load. Default is "
                                 "config.yaml. Multiple files can be used via a comma-"
                                 "separated list (no spaces between)")

        parser.add_argument("-C",
                            action="store", dest="mcconfigfile",
                            default="mcconfig.yaml",
                            metavar='config_file',
                            help="The MPF framework default config file. Default is "
                                 "<mpf-mc install folder>/mcconfig.yaml")

        parser.add_argument("-f",
                            action="store_true", dest="force_assets_load", default=False,
                            help="Load all assets upon startup. Useful for "
                            "ensuring all assets are set up properly "
                            "during development.")

        parser.add_argument("-l",
                            action="store", dest="logfile",
                            metavar='file_name',
                            default=os.path.join("logs", datetime.now().strftime(
                                "%Y-%m-%d-%H-%M-%S-mc-" + socket.gethostname() +
                                ".log")),
                            help="The name (and path) of the log file")

        parser.add_argument("-p",
                            action="store_true", dest="pause", default=False,
                            help="Pause the terminal window on exit. Useful "
                            "when launching in a separate window so you can "
                            "see any errors before the window closes.")

        parser.add_argument("-v",
                            action="store_const", dest="loglevel", const=logging.DEBUG,
                            default=logging.INFO, help="Enables verbose logging to the"
                                                       " log file")

        parser.add_argument("-V",
                            action="store_true", dest="consoleloglevel",
                            default=logging.INFO,
                            help="Enables verbose logging to the console. Do NOT on "
                                 "Windows platforms")

        # The following are just included for full compatibility with mpf.py
        # which is needed when using "mpf both".

        parser.add_argument("-a",
                            action="store_const", dest="force_platform",
                            const='no_load_cache', help=argparse.SUPPRESS)

        parser.add_argument("-A",
                            action="store_const", dest="force_platform",
                            const='create_config_cache', help=argparse.SUPPRESS)

        parser.add_argument("-x",
                            action="store_const", dest="force_platform",
                            const='virtual', help=argparse.SUPPRESS)

        parser.add_argument("-X",
                            action="store_const", dest="force_platform",
                            const='smart_virtual', help=argparse.SUPPRESS)

        args = parser.parse_args(args)

        args.configfile = Util.string_to_list(args.configfile)

        # Configure logging. Creates a logfile and logs to the console.
        # Formatting options are documented here:
        # https://docs.python.org/2.7/library/logging.html#logrecord-attributes

        try:
            os.makedirs(os.path.join(machine_path, 'logs'))
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

        logging.basicConfig(level=args.loglevel,
                            format='%(asctime)s : %(levelname)s : %(name)s : '
                                   '%(message)s',
                            filename=os.path.join(machine_path, args.logfile),
                            filemode='w')

        # define a Handler which writes INFO messages or higher to the
        # sys.stderr
        console = logging.StreamHandler()
        console.setLevel(args.consoleloglevel)

        # set a format which is simpler for console use
        formatter = logging.Formatter('%(levelname)s : %(name)s : %(message)s')

        # tell the handler to use this format
        console.setFormatter(formatter)

        # add the handler to the root logger
        logging.getLogger('').addHandler(console)

        mpf_config = ConfigProcessor.load_config_file(os.path.join(
            mpfmc.__path__[0], args.mcconfigfile), 'machine')

        machine_path = set_machine_path(machine_path,
                                        mpf_config['mpf-mc']['paths'][
                                            'machine_files'])

        mpf_config = load_machine_config(args.configfile, machine_path,
                                         mpf_config['mpf-mc']['paths'][
                                             'config'], mpf_config)

        self.preprocess_config(mpf_config)

        from mpfmc.core.mc import MpfMc

        logging.info("Loading MPF-MC controller")

        thread_stopper = threading.Event()

        try:
            MpfMc(options=vars(args), config=mpf_config,
                  machine_path=machine_path,
                  thread_stopper=thread_stopper).run()
            logging.info("MC run loop ended.")
        except Exception as e:
            logging.exception(str(e))

        logging.info("Stopping child threads... (%s remaining)", len(threading.enumerate()) - 1)

        thread_stopper.set()

        while len(threading.enumerate()) > 1:
            time.sleep(.1)

        logging.info("All child threads stopped.")

        if args.pause:
            input('Press ENTER to continue...')

        sys.exit()
Ejemplo n.º 12
0
class MpfMcTestCase(unittest.TestCase):
    def __init__(self, *args):
        self.sent_bcp_commands = list()
        super().__init__(*args)

        self._events = dict()
        self._last_event_kwargs = dict()
        self.max_test_setup_secs = 30

        self._fps = 30

    def _mc_time(self):
        return self._current_time

    def get_options(self):
        return dict(machine_path=self.get_machine_path(),
                    mcconfigfile='mcconfig.yaml',
                    production=False,
                    configfile=Util.string_to_list(self.get_config_file()),
                    no_load_cache=False,
                    create_config_cache=True,
                    no_sound=False,
                    bcp=False)

    def getAbsoluteMachinePath(self):
        return os.path.abspath(
            os.path.join(mpfmc.__path__[0], os.pardir, 'mpfmc',
                         self.get_machine_path()))

    def get_machine_path(self):
        raise NotImplementedError

    def get_config_file(self):
        raise NotImplementedError

    def get_abs_path(self, path):
        return os.path.join(os.path.abspath(os.curdir), path)

    def advance_time(self, secs=.1):
        start = self._current_time
        while self._current_time < start + secs:
            EventLoop.idle()
            self._current_time += 1 / self._fps

    def advance_real_time(self, secs=.1):
        start = self._current_time
        while self._current_time < start + secs:
            EventLoop.idle()
            sleep(1 / self._fps)
            self._current_time += 1 / self._fps

        EventLoop.idle()

    def get_pixel_color(self, x, y):
        """Returns a binary string of the RGB bytes that make up the slide
        pixel at the passed x,y coordinates. 2 bytes per pixel, 6 bytes total.
        This method *does* compensate for different window sizes.

        Note: This method does not work yet.

        """
        raise NotImplementedError  # remove when we fix it. :)

        # do the Window import here because we don't want to import it at the
        # top or else we won't be able to set window properties
        from kivy.core.window import Window

        # convert the passed x/y to the actual x/y of the Window since it's
        # possible for the mpf-mc display size to be different than the Window
        # size
        x *= Window.width / Window.children[0].width
        y *= Window.height / Window.children[0].height

        return glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE)

    def tearDown(self):
        self.mc.stop()

    def patch_bcp(self):
        # used internally
        self.orig_bcp_send = self.mc.bcp_processor.send
        self.mc.bcp_processor.send = self._bcp_send

        # this is used to send BCP commands to mpf-mc
        self.send = self.mc.bcp_processor._process_command

        self.mc.bcp_client_connected = True

    def _bcp_send(self, bcp_command, callback=None, **kwargs):
        # used for commands sent from the MC to the PC
        # print((bcp_command, callback, kwargs))
        self.sent_bcp_commands.append((bcp_command, callback, kwargs))
        self.orig_bcp_send(bcp_command=bcp_command,
                           callback=callback,
                           **kwargs)

    def setUp(self):
        # Most of the setup is done in run(). Explanation is there.
        Config._named_configs.pop('app', None)

        self._start_time = time()
        self._current_time = self._start_time
        Clock._start_tick = self._start_time
        Clock._last_tick = self._start_time
        Clock.time = self._mc_time

        # prevent sleep in clock
        Clock._max_fps = 0
        # reset clock
        Clock._root_event = None

        from mpf.core.player import Player
        Player.monitor_enabled = False

        machine_path = self.getAbsoluteMachinePath()

        try:
            self.mc = MpfMc(options=self.get_options(),
                            machine_path=machine_path)

            self.patch_bcp()

            from kivy.core.window import Window
            Window.create_window()
            Window.canvas.clear()

            self._start_app_as_slave()
        except Exception:
            if self.mc:
                # prevent dead locks with two asset manager threads
                self.mc.stop()
            raise

    def _start_app_as_slave(self):
        # from app::run
        if not self.mc.built:
            self.mc.load_config()
            self.mc.load_kv(filename=self.mc.kv_file)
            root = self.mc.build()
            if root:
                self.mc.root = root
        if self.mc.root:
            if not isinstance(self.mc.root, KivyWidget):
                Logger.critical(
                    'App.root must be an _instance_ of Kivy Widget')
                raise Exception('Invalid instance in App.root')
            from kivy.core.window import Window
            Window.add_widget(self.mc.root)

        # Check if the window is already created
        from kivy.base import EventLoop
        window = EventLoop.window
        if window:
            self.mc._app_window = window
            #window.set_title(self.mc.get_application_name() + self._testMethodName)
            icon = self.mc.get_application_icon()
            if icon:
                window.set_icon(icon)
            self.mc._install_settings_keys(window)
        else:
            Logger.critical("Application: No window is created."
                            " Terminating application run.")
            return

        self.mc.dispatch('on_start')
        runTouchApp(slave=True)  # change is here

        # Perform init process
        tries = 0
        while not self.mc.is_init_done.is_set(
        ) and not self.mc.thread_stopper.is_set():
            self.advance_time()
            sleep(.001)
            tries += 1
            if tries > 1000:
                self.fail("Test init took too long")

        # set a nice title
        window.set_title(self.__class__.__name__ + "::" + self._testMethodName)

    def dump_clock(self):
        print("---------")
        events = []
        event = Clock._root_event
        while event:
            events.append(event)
            event = event.next

        events.sort(key=lambda x: str(x.get_callback()))

        for event in events:
            print(event.get_callback(), event.timeout)

    def _mock_event_handler(self, event_name, **kwargs):
        self._last_event_kwargs[event_name] = kwargs
        self._events[event_name] += 1

    def mock_event(self, event_name):
        self._events[event_name] = 0
        self.mc.events.remove_handler_by_event(
            event=event_name, handler=self._mock_event_handler)
        self.mc.events.add_handler(event=event_name,
                                   handler=self._mock_event_handler,
                                   event_name=event_name)

    def assertEventNotCalled(self, event_name):
        """Assert that event was not called."""
        if event_name not in self._events:
            raise AssertionError("Event {} not mocked.".format(event_name))

        if self._events[event_name] != 0:
            raise AssertionError("Event {} was called {} times.".format(
                event_name, self._events[event_name]))

    def assertEventCalled(self, event_name, times=None):
        """Assert that event was called."""
        if event_name not in self._events:
            raise AssertionError("Event {} not mocked.".format(event_name))

        if self._events[event_name] == 0:
            raise AssertionError("Event {} was not called.".format(event_name))

        if times is not None and self._events[event_name] != times:
            raise AssertionError(
                "Event {} was called {} instead of {}.".format(
                    event_name, self._events[event_name], times))

    def assertEventCalledWith(self, event_name, **kwargs):
        """Assert that event was called with kwargs."""
        self.assertEventCalled(event_name)
        self.assertEqual(kwargs, self._last_event_kwargs[event_name],
                         "Args for {} differ.".format(event_name))

    def reset_mock_events(self):
        for event in self._events.keys():
            self._events[event] = 0
Ejemplo n.º 13
0
class MpfMcTestCase(unittest.TestCase):
    def __init__(self, *args):
        self.sent_bcp_commands = list()
        super().__init__(*args)

        self._events = dict()
        self._last_event_kwargs = dict()
        self.max_test_setup_secs = 30

    def get_options(self):
        return dict(machine_path=self.get_machine_path(),
                    mcconfigfile='mpfmc/mcconfig.yaml',
                    configfile=Util.string_to_list(self.get_config_file()),
                    bcp=False)

    def getAbsoluteMachinePath(self):
        return os.path.abspath(
            os.path.join(mpfmc.__path__[0], os.pardir, 'mpfmc',
                         self.get_machine_path()))

    def get_machine_path(self):
        raise NotImplementedError

    def get_config_file(self):
        raise NotImplementedError

    def get_abs_path(self, path):
        return os.path.join(os.path.abspath(os.curdir), path)

    def preprocess_config(self, config):
        # TODO this method is copied from the mc.py launcher. Prob a better way
        kivy_config = config['kivy_config']

        try:
            kivy_config['graphics'].update(config['displays']['window'])
        except KeyError:
            pass

        try:
            kivy_config['graphics'].update(config['window'])
        except KeyError:
            pass

        if 'top' in kivy_config['graphics'] and 'left' in kivy_config[
                'graphics']:
            kivy_config['graphics']['position'] = 'custom'

        for section, settings in kivy_config.items():
            for k, v in settings.items():
                try:
                    if k in Config[section]:
                        Config.set(section, k, v)
                except KeyError:
                    continue

    def advance_time(self, secs=.1):
        start = time()
        self.mc.events.process_event_queue()
        while time() < start + secs:
            sleep(.01)
            self.mc.events.process_event_queue()
            EventLoop.idle()

    def get_pixel_color(self, x, y):
        """Returns a binary string of the RGB bytes that make up the slide
        pixel at the passed x,y coordinates. 2 bytes per pixel, 6 bytes total.
        This method *does* compensate for different window sizes.

        Note: This method does not work yet.

        """
        raise NotImplementedError  # remove when we fix it. :)

        # do the Window import here because we don't want to import it at the
        # top or else we won't be able to set window properties
        from kivy.core.window import Window

        # convert the passed x/y to the actual x/y of the Window since it's
        # possible for the mpf-mc display size to be different than the Window
        # size
        x *= Window.width / Window.children[0].width
        y *= Window.height / Window.children[0].height

        return glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE)

    def setUp(self):
        # Most of the setup is done in run(). Explanation is there.
        Config._named_configs.pop('app', None)

    def tearDown(self):
        stopTouchApp()

    def patch_bcp(self):
        # used internally
        self.orig_bcp_send = self.mc.bcp_processor.send
        self.mc.bcp_processor.send = self._bcp_send

        # this is used to send BCP commands to mpf-mc
        self.send = self.mc.bcp_processor._process_command

        self.mc.bcp_client_connected = True

    def _bcp_send(self, bcp_command, callback=None, **kwargs):
        # used for commands sent from the MC to the PC
        # print((bcp_command, callback, kwargs))
        self.sent_bcp_commands.append((bcp_command, callback, kwargs))
        self.orig_bcp_send(bcp_command=bcp_command,
                           callback=callback,
                           **kwargs)

    def run(self, name):
        Clock._events = [[] for i in range(256)]
        self._test_started = time()
        self._test_name = self.id()
        self._test = name
        # This setup is done in run() because we need to give control to the
        # kivy event loop which we can only do by returning from the run()
        # that's called. So we override run() and setup mpf-mc and then call
        # our own run_test() on a callback. Then we can wait until the
        # environment is setup (which can take a few frames), then we call
        # super().run() to get the actual TestCase.run() method to run and
        # we return the results.

        # We have to do this in run() and not setUp() because run actually
        # calls setUp(), so since we were overriding it ours doesn't call it
        # so we just do our setup here since if we manually called setUp() then
        # it would be called again when we call super().run().

        from mpf.core.player import Player
        Player.monitor_enabled = False

        mpf_config = ConfigProcessor.load_config_file(
            os.path.abspath(
                os.path.join(mpfmc.__path__[0], os.pardir,
                             self.get_options()['mcconfigfile'])), 'machine')

        machine_path = self.getAbsoluteMachinePath()

        mpf_config = load_machine_config(
            Util.string_to_list(self.get_config_file()), machine_path,
            mpf_config['mpf-mc']['paths']['config'], mpf_config)
        self.preprocess_config(mpf_config)

        self.mc = MpfMc(options=self.get_options(),
                        config=mpf_config,
                        machine_path=machine_path)

        self.patch_bcp()

        from kivy.core.window import Window
        Window.create_window()
        Window.canvas.clear()

        Clock.schedule_once(self.run_test, 0)
        self.mc.run()

    def dump_clock(self):
        print("---------")
        events = []
        for slot in Clock._events:
            for event in slot:
                events.append(event)

        events.sort(key=lambda x: str(x.get_callback()))

        for event in events:
            print(event.get_callback(), event.timeout)

    def run_test(self, dt):
        # set the title bar, just for fun. :)
        self.mc.title = str(self._test_name)

        if not self.mc.is_init_done:
            if self._test_started + self.max_test_setup_secs < time():
                self.dump_clock()
                self.fail("Test setup took more than {} seconds.".format(
                    self.max_test_setup_secs))
            Clock.schedule_once(self.run_test, 0)
            return

        return super().run(self._test)

    def _mock_event_handler(self, event_name, **kwargs):
        self._last_event_kwargs[event_name] = kwargs
        self._events[event_name] += 1

    def mock_event(self, event_name):
        self._events[event_name] = 0
        self.mc.events.remove_handler_by_event(
            event=event_name, handler=self._mock_event_handler)
        self.mc.events.add_handler(event=event_name,
                                   handler=self._mock_event_handler,
                                   event_name=event_name)

    def assertEventNotCalled(self, event_name):
        """Assert that event was not called."""
        if event_name not in self._events:
            raise AssertionError("Event {} not mocked.".format(event_name))

        if self._events[event_name] != 0:
            raise AssertionError("Event {} was called {} times.".format(
                event_name, self._events[event_name]))

    def assertEventCalled(self, event_name, times=None):
        """Assert that event was called."""
        if event_name not in self._events:
            raise AssertionError("Event {} not mocked.".format(event_name))

        if self._events[event_name] == 0:
            raise AssertionError("Event {} was not called.".format(event_name))

        if times is not None and self._events[event_name] != times:
            raise AssertionError(
                "Event {} was called {} instead of {}.".format(
                    event_name, self._events[event_name], times))

    def assertEventCalledWith(self, event_name, **kwargs):
        """Assert that event was called with kwargs."""
        self.assertEventCalled(event_name)
        self.assertEqual(kwargs, self._last_event_kwargs[event_name],
                         "Args for {} differ.".format(event_name))

    def reset_mock_events(self):
        for event in self._events.keys():
            self._events[event] = 0