def test_coalesce_widget_states(self):
        old_states = WidgetStates()

        _create_widget("old_set_trigger", old_states).trigger_value = True
        _create_widget("old_unset_trigger", old_states).trigger_value = False
        _create_widget("missing_in_new", old_states).int_value = 123
        _create_widget("shape_changing_trigger",
                       old_states).trigger_value = True

        new_states = WidgetStates()

        _create_widget("old_set_trigger", new_states).trigger_value = False
        _create_widget("new_set_trigger", new_states).trigger_value = True
        _create_widget("added_in_new", new_states).int_value = 456
        _create_widget("shape_changing_trigger", new_states).int_value = 3

        widgets = Widgets()
        widgets.set_state(coalesce_widget_states(old_states, new_states))

        self.assertIsNone(widgets.get_widget_value("old_unset_trigger"))
        self.assertIsNone(widgets.get_widget_value("missing_in_new"))

        self.assertEqual(True, widgets.get_widget_value("old_set_trigger"))
        self.assertEqual(True, widgets.get_widget_value("new_set_trigger"))
        self.assertEqual(456, widgets.get_widget_value("added_in_new"))

        # Widgets that were triggers before, but no longer are, will *not*
        # be coalesced
        self.assertEqual(3, widgets.get_widget_value("shape_changing_trigger"))
    def __init__(self, script_name):
        """Initializes the ScriptRunner for the given script_name"""
        # DeltaGenerator deltas will be enqueued into self.report_queue.
        self.report_queue = ReportQueue()

        def enqueue_fn(msg):
            self.report_queue.enqueue(msg)
            self.maybe_handle_execution_control_request()

        self.script_request_queue = ScriptRequestQueue()
        script_path = os.path.join(os.path.dirname(__file__), "test_data",
                                   script_name)

        super(TestScriptRunner, self).__init__(
            session_id="test session id",
            report=Report(script_path, "test command line"),
            enqueue_forward_msg=enqueue_fn,
            widget_states=WidgetStates(),
            request_queue=self.script_request_queue,
        )

        # Accumulates uncaught exceptions thrown by our run thread.
        self.script_thread_exceptions = []

        # Accumulates all ScriptRunnerEvents emitted by us.
        self.events = []

        def record_event(event, **kwargs):
            self.events.append(event)

        self.on_event.connect(record_event, weak=False)
Exemple #3
0
    def test_widgets(self):
        """Tests that widget values behave as expected."""
        scriptrunner = TestScriptRunner("widgets_script.py")
        scriptrunner.enqueue_rerun()
        scriptrunner.start()

        # Default widget values
        time.sleep(0.1)
        self._assert_text_deltas(
            scriptrunner, ["False", "ahoy!", "0", "False", "loop_forever"])

        # Update widgets
        states = WidgetStates()
        _create_widget("checkbox-checkbox", states).bool_value = True
        _create_widget("text_area-text_area", states).string_value = "matey!"
        _create_widget("radio-radio", states).int_value = 2
        _create_widget("button-button", states).trigger_value = True

        scriptrunner.enqueue_rerun(widget_state=states)
        time.sleep(0.1)
        self._assert_text_deltas(
            scriptrunner, ["True", "matey!", "2", "True", "loop_forever"])

        # Rerun with previous values. Our button should be reset;
        # everything else should be the same.
        scriptrunner.enqueue_rerun()
        time.sleep(0.1)
        self._assert_text_deltas(
            scriptrunner, ["True", "matey!", "2", "False", "loop_forever"])

        scriptrunner.enqueue_shutdown()
        scriptrunner.join()

        self._assert_no_exceptions(scriptrunner)
Exemple #4
0
    def __init__(self, script_name):
        """Initializes the ScriptRunner for the given script_name"""
        # DeltaGenerator deltas will be enqueued into self.report_queue.
        self.report_queue = ReportQueue()

        def enqueue_fn(msg):
            self.report_queue.enqueue(msg)
            self.maybe_handle_execution_control_request()
            return True

        self.main_dg = DeltaGenerator(enqueue_fn, container=BlockPath.MAIN)
        self.sidebar_dg = DeltaGenerator(enqueue_fn,
                                         container=BlockPath.SIDEBAR)
        self.script_request_queue = ScriptRequestQueue()

        script_path = os.path.join(os.path.dirname(__file__), 'test_data',
                                   script_name)

        super(TestScriptRunner,
              self).__init__(report=Report(script_path, []),
                             main_dg=self.main_dg,
                             sidebar_dg=self.sidebar_dg,
                             widget_states=WidgetStates(),
                             request_queue=self.script_request_queue)

        # Accumulates uncaught exceptions thrown by our run thread.
        self.script_thread_exceptions = []

        # Accumulates all ScriptRunnerEvents emitted by us.
        self.events = []

        def record_event(event, **kwargs):
            self.events.append(event)

        self.on_event.connect(record_event, weak=False)
Exemple #5
0
    def test_widgets(self):
        """Tests that widget values behave as expected."""
        scriptrunner = TestScriptRunner('widgets_script.py')
        scriptrunner.enqueue_rerun()
        scriptrunner.start()

        # Default widget values
        time.sleep(0.1)
        self._assert_text_deltas(
            scriptrunner, ['False', 'ahoy!', '0', 'False', 'loop_forever'])

        # Update widgets
        states = WidgetStates()
        _create_widget('checkbox-checkbox', states).bool_value = True
        _create_widget('text_area-text_area', states).string_value = 'matey!'
        _create_widget('radio-radio', states).int_value = 2
        _create_widget('button-button', states).trigger_value = True

        scriptrunner.enqueue_rerun(widget_state=states)
        time.sleep(0.1)
        self._assert_text_deltas(
            scriptrunner, ['True', 'matey!', '2', 'True', 'loop_forever'])

        # Rerun with previous values. Our button should be reset;
        # everything else should be the same.
        scriptrunner.enqueue_rerun()
        time.sleep(0.1)
        self._assert_text_deltas(
            scriptrunner, ['True', 'matey!', '2', 'False', 'loop_forever'])

        scriptrunner.enqueue_shutdown()
        scriptrunner.join()

        self._assert_no_exceptions(scriptrunner)
    def test_multiple_scriptrunners(self):
        """Tests that multiple scriptrunners can run simultaneously."""
        # This scriptrunner will run in parallel to the other 3.
        # It's used to retrieve the widget id before initializing deltas on other runners.
        # Wait a beat to access deltas.
        scriptrunner = TestScriptRunner("widgets_script.py")
        scriptrunner.enqueue_rerun()
        scriptrunner.start()
        time.sleep(0.1)

        # Build several runners. Each will set a different int value for
        # its radio button.
        runners = []
        for ii in range(3):
            runner = TestScriptRunner("widgets_script.py")
            runners.append(runner)

            states = WidgetStates()
            wid = scriptrunner.get_widget_id("radio", "radio")
            _create_widget(wid, states).int_value = ii
            runner.enqueue_rerun(widget_state=states)

        # Start the runners and wait a beat.
        for runner in runners:
            runner.start()

        time.sleep(0.1)

        # Ensure that each runner's radio value is as expected.
        for ii, runner in enumerate(runners):
            self._assert_text_deltas(
                runner, ["False", "ahoy!", "%s" % ii, "False", "loop_forever"]
            )
            runner.enqueue_shutdown()

        time.sleep(0.1)

        # Shut 'em all down!
        for runner in runners:
            runner.join()

        for runner in runners:
            self._assert_no_exceptions(runner)
            self._assert_events(
                runner,
                [
                    ScriptRunnerEvent.SCRIPT_STARTED,
                    ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS,
                    ScriptRunnerEvent.SHUTDOWN,
                ],
            )

        scriptrunner.enqueue_shutdown()
        scriptrunner.join()
        self._assert_no_exceptions(scriptrunner)
Exemple #7
0
    def get_state(self):
        """
        Returns
        -------
        WidgetStates
            A new WidgetStates protobuf containing the contents of
            our widget state dictionary.

        """
        states = WidgetStates()
        states.widgets.extend(self._state.values())
        return states
Exemple #8
0
    def __init__(
        self, is_preheat, ioloop, script_path, command_line, uploaded_file_manager
    ):
        """Initialize the ReportSession.

        Parameters
        ----------
        is_preheat : bool
            True if this is the "preheated" session.

        ioloop : tornado.ioloop.IOLoop
            The Tornado IOLoop that we're running within.

        script_path : str
            Path of the Python file from which this report is generated.

        command_line : str
            Command line as input by the user.

        uploaded_file_manager : UploadedFileManager
            The server's UploadedFileManager.

        """
        # Each ReportSession has a unique string ID.
        if is_preheat:
            self.id = PREHEATED_ID
        else:
            self.id = str(uuid.uuid4())

        self._ioloop = ioloop
        self._report = Report(script_path, command_line)
        self._uploaded_file_mgr = uploaded_file_manager

        self._state = ReportSessionState.REPORT_NOT_RUNNING

        self._widget_states = WidgetStates()
        self._local_sources_watcher = LocalSourcesWatcher(
            self._report, self._on_source_file_changed
        )
        self._sent_initialize_message = False
        self._storage = None
        self._maybe_reuse_previous_run = False
        self._run_on_save = config.get_option("server.runOnSave")

        # The ScriptRequestQueue is the means by which we communicate
        # with the active ScriptRunner.
        self._script_request_queue = ScriptRequestQueue()

        self._scriptrunner = None

        LOGGER.debug("ReportSession initialized (id=%s)", self.id)
    def test_reset_triggers(self):
        states = WidgetStates()
        widgets = Widgets()

        _create_widget("trigger", states).trigger_value = True
        _create_widget("int", states).int_value = 123
        widgets.set_state(states)

        self.assertEqual(True, widgets.get_widget_value("trigger"))
        self.assertEqual(123, widgets.get_widget_value("int"))

        widgets.reset_triggers()

        self.assertEqual(None, widgets.get_widget_value("trigger"))
        self.assertEqual(123, widgets.get_widget_value("int"))
    def __init__(self, ioloop, script_path, command_line):
        """Initialize the ReportSession.

        Parameters
        ----------
        ioloop : tornado.ioloop.IOLoop
            The Tornado IOLoop that we're running within.

        script_path : str
            Path of the Python file from which this report is generated.

        command_line : str
            Command line as input by the user.

        """
        # Each ReportSession gets a unique ID
        self.id = ReportSession._next_id
        ReportSession._next_id += 1

        self._ioloop = ioloop
        self._report = Report(script_path, command_line)

        self._state = ReportSessionState.REPORT_NOT_RUNNING

        self._uploaded_file_mgr = UploadedFileManager()

        self._main_dg = DeltaGenerator(enqueue=self.enqueue, container=BlockPath.MAIN)
        self._sidebar_dg = DeltaGenerator(
            enqueue=self.enqueue, container=BlockPath.SIDEBAR
        )

        self._widget_states = WidgetStates()
        self._local_sources_watcher = LocalSourcesWatcher(
            self._report, self._on_source_file_changed
        )
        self._sent_initialize_message = False
        self._storage = None
        self._maybe_reuse_previous_run = False
        self._run_on_save = config.get_option("server.runOnSave")

        # The ScriptRequestQueue is the means by which we communicate
        # with the active ScriptRunner.
        self._script_request_queue = ScriptRequestQueue()

        self._scriptrunner = None

        LOGGER.debug("ReportSession initialized (id=%s)", self.id)
    def test_values(self):
        states = WidgetStates()

        _create_widget("trigger", states).trigger_value = True
        _create_widget("bool", states).bool_value = True
        _create_widget("float", states).float_value = 0.5
        _create_widget("int", states).int_value = 123
        _create_widget("string", states).string_value = "howdy!"

        widgets = Widgets()
        widgets.set_state(states)

        self.assertEqual(True, widgets.get_widget_value("trigger"))
        self.assertEqual(True, widgets.get_widget_value("bool"))
        self.assertAlmostEqual(0.5, widgets.get_widget_value("float"))
        self.assertEqual(123, widgets.get_widget_value("int"))
        self.assertEqual("howdy!", widgets.get_widget_value("string"))
Exemple #12
0
    def test_values(self):
        states = WidgetStates()

        _create_widget('trigger', states).trigger_value = True
        _create_widget('bool', states).bool_value = True
        _create_widget('float', states).float_value = 0.5
        _create_widget('int', states).int_value = 123
        _create_widget('string', states).string_value = 'howdy!'

        widgets = Widgets()
        widgets.set_state(states)

        self.assertEqual(True, widgets.get_widget_value('trigger'))
        self.assertEqual(True, widgets.get_widget_value('bool'))
        self.assertAlmostEqual(0.5, widgets.get_widget_value('float'))
        self.assertEqual(123, widgets.get_widget_value('int'))
        self.assertEqual('howdy!', widgets.get_widget_value('string'))
Exemple #13
0
def coalesce_widget_states(old_states, new_states):
    """Coalesces an older WidgetStates into a newer one,
    and returns a new WidgetState containing the result.

    For most widget values, we just take the latest version.

    However, any trigger_values (the state set by buttons)
    that are True in the older WidgetStates will be set to
    True in the coalesced result, so that button presses
    don't go missing.


    Parameters
    ----------
    old_states : WidgetStates
        The older WidgetStates protobuf
    new_states : WidgetStates
        The newer WidgetStates protobuf

    Returns
    -------
    WidgetStates
        The resulting coalesced protobuf

    """
    states_by_id = {}
    for new_state in new_states.widgets:
        states_by_id[new_state.id] = new_state

    for old_state in old_states.widgets:
        if old_state.WhichOneof(
                "value") == "trigger_value" and old_state.trigger_value:

            # Ensure the corresponding new_state is also a trigger;
            # otherwise, a widget that was previously a button but no longer is
            # could get a bad value.
            new_state = states_by_id.get(old_state.id)
            if new_state and new_state.WhichOneof("value") == "trigger_value":
                states_by_id[old_state.id] = old_state

    coalesced = WidgetStates()
    coalesced.widgets.extend(states_by_id.values())

    return coalesced
Exemple #14
0
    def test_multiple_scriptrunners(self):
        """Tests that multiple scriptrunners can run simultaneously."""

        # Build several runners. Each will set a different int value for
        # its radio button.
        runners = []
        for ii in range(3):
            runner = TestScriptRunner('widgets_script.py')
            runners.append(runner)

            states = WidgetStates()
            _create_widget('radio-radio', states).int_value = ii
            runner.enqueue_rerun(widget_state=states)

        # Start the runners and wait a beat.
        for runner in runners:
            runner.start()

        time.sleep(0.1)

        # Ensure that each runner's radio value is as expected.
        for ii, runner in enumerate(runners):
            self._assert_text_deltas(
                runner, ['False', 'ahoy!',
                         '%s' % ii, 'False', 'loop_forever'])

            runner.enqueue_shutdown()

        time.sleep(0.1)

        # Shut 'em all down!
        for runner in runners:
            runner.join()

        for runner in runners:
            self._assert_no_exceptions(runner)
            self._assert_events(runner, [
                ScriptRunnerEvent.SCRIPT_STARTED,
                ScriptRunnerEvent.SCRIPT_STOPPED_WITH_SUCCESS,
                ScriptRunnerEvent.SHUTDOWN
            ])
    def test_rerun_data_coalescing(self):
        """Test that multiple RERUN requests get coalesced with
        expected values.

        (This is similar to widgets_test.test_coalesce_widget_states -
        it's testing the same thing, but through the ScriptEventQueue
        interface.)
        """
        queue = ScriptRequestQueue()

        states = WidgetStates()
        _create_widget('trigger', states).trigger_value = True
        _create_widget('int', states).int_value = 123

        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=states))

        states = WidgetStates()
        _create_widget('trigger', states).trigger_value = False
        _create_widget('int', states).int_value = 456

        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=states))

        event, data = queue.dequeue()
        self.assertEqual(event, ScriptRequest.RERUN)

        widgets = Widgets()
        widgets.set_state(data.widget_state)

        # Coalesced triggers should be True if either the old or
        # new value was True
        self.assertEqual(True, widgets.get_widget_value('trigger'))

        # Other widgets should have their newest value
        self.assertEqual(456, widgets.get_widget_value('int'))

        # We should have no more events
        self.assertEqual((None, None), queue.dequeue(),
                         'Expected empty event queue')

        # Test that we can coalesce if previous widget state is None
        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=None))
        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=None))

        states = WidgetStates()
        _create_widget('int', states).int_value = 789

        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=states))

        event, data = queue.dequeue()
        widgets = Widgets()
        widgets.set_state(data.widget_state)

        self.assertEqual(event, ScriptRequest.RERUN)
        self.assertEqual(789, widgets.get_widget_value('int'))

        # We should have no more events
        self.assertEqual((None, None), queue.dequeue(),
                         'Expected empty event queue')

        # Test that we can coalesce if our *new* widget state is None
        states = WidgetStates()
        _create_widget('int', states).int_value = 101112

        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=states))

        queue.enqueue(ScriptRequest.RERUN,
                      RerunData(argv=None, widget_state=None))

        event, data = queue.dequeue()
        widgets = Widgets()
        widgets.set_state(data.widget_state)

        self.assertEqual(event, ScriptRequest.RERUN)
        self.assertEqual(101112, widgets.get_widget_value('int'))

        # We should have no more events
        self.assertEqual((None, None), queue.dequeue(),
                         'Expected empty event queue')