Exemplo n.º 1
0
def main(argv):
    parser = _stbt.core.argparser()
    parser.prog = 'stbt run'
    parser.description = 'Run an stb-tester test script'
    parser.add_argument(
        '--cache', default=imgproc_cache.default_filename,
        help="Path for image-processing cache (default: %(default)s")
    parser.add_argument(
        '--save-screenshot', default='on-failure',
        choices=['always', 'on-failure', 'never'],
        help="Save a screenshot at the end of the test to screenshot.png")
    parser.add_argument(
        '--save-thumbnail', default='never',
        choices=['always', 'on-failure', 'never'],
        help="Save a thumbnail at the end of the test to thumbnail.jpg")
    parser.add_argument(
        'script', metavar='FILE[::TESTCASE]', help=(
            "The python test script to run. Optionally specify a python "
            "function name to run that function; otherwise only the script's "
            "top-level will be executed."))
    parser.add_argument(
        'args', nargs=argparse.REMAINDER, metavar='ARG',
        help='Additional arguments passed on to the test script (in sys.argv)')

    args = parser.parse_args(argv[1:])
    debug("Arguments:\n" + "\n".join([
        "%s: %s" % (k, v) for k, v in args.__dict__.items()]))

    dut = _stbt.core.new_device_under_test_from_config(args)
    with sane_unicode_and_exception_handling(args.script), \
            video(args, dut), \
            imgproc_cache.setup_cache(filename=args.cache):
        test_function = load_test_function(args.script, args.args)
        test_function.call()
Exemplo n.º 2
0
    def swipe(self, start_position, end_position):
        """Swipe from one point to another point.

        :param start_position:
            A `stbt.Region` or (x, y) tuple of coordinates at which to start.
        :param end_position:
            A `stbt.Region` or (x, y) tuple of coordinates at which to stop.

        Example::

          d.swipe((100, 100), (100, 400))

        """
        x1, y1 = _centre_point(start_position)
        x2, y2 = _centre_point(end_position)
        debug("AdbDevice.swipe((%d,%d), (%d,%d))" % (x1, y1, x2, y2))

        x1, y1 = self._to_native_coordinates(x1, y1)
        x2, y2 = self._to_native_coordinates(x2, y2)
        command = [
            "shell", "input", "swipe",
            str(x1),
            str(y1),
            str(x2),
            str(y2)
        ]
        self.adb(command, timeout_secs=10)
Exemplo n.º 3
0
    def frames(self, timeout_secs=None):
        if timeout_secs is not None:
            end_time = self._time.time() + timeout_secs
        timestamp = None
        first = True

        while True:
            if self._use_old_threading_behaviour:
                timestamp = self._last_grabbed_frame_time

            ddebug("user thread: Getting sample at %s" % self._time.time())
            frame = self._display.get_frame(max(10, timeout_secs),
                                            since=timestamp)
            ddebug("user thread: Got sample at %s" % self._time.time())
            timestamp = frame.time

            if self._use_old_threading_behaviour:
                self._last_grabbed_frame_time = timestamp

            if not first and timeout_secs is not None and timestamp > end_time:
                debug("timed out: %.3f > %.3f" % (timestamp, end_time))
                return

            yield frame
            first = False
Exemplo n.º 4
0
    def __exit__(self, _1, _2, _3):
        # Drain the frame queue
        while self._frames:
            self._push_sample(self._frames.pop())

        if self._sample_count > 0:
            state = self.sink_pipeline.get_state(0)
            if (state[0] != Gst.StateChangeReturn.SUCCESS
                    or state[1] != Gst.State.PLAYING):
                debug("teardown: Sink pipeline not in state PLAYING: %r" %
                      (state, ))
            debug("teardown: Sending eos on sink pipeline")
            if self.appsrc.emit("end-of-stream") == Gst.FlowReturn.OK:
                self.sink_pipeline.send_event(Gst.Event.new_eos())
                if not self.received_eos.wait(10):
                    debug("Timeout waiting for sink EOS")
            else:
                debug("Sending EOS to sink pipeline failed")
        else:
            debug("SinkPipeline teardown: Not sending EOS, no samples sent")

        self.sink_pipeline.set_state(Gst.State.NULL)

        # Don't want to cause the Display object to hang around on our account,
        # we won't be raising any errors from now on anyway:
        self._raise_in_user_thread = None
Exemplo n.º 5
0
    def __exit__(self, _1, _2, _3):
        # Drain the frame queue
        while self._frames:
            self._push_sample(self._frames.pop())

        if self._sample_count > 0:
            state = self.sink_pipeline.get_state(0)
            if (state[0] != Gst.StateChangeReturn.SUCCESS or
                    state[1] != Gst.State.PLAYING):
                debug("teardown: Sink pipeline not in state PLAYING: %r"
                      % (state,))
            debug("teardown: Sending eos on sink pipeline")
            if self.appsrc.emit("end-of-stream") == Gst.FlowReturn.OK:
                self.sink_pipeline.send_event(Gst.Event.new_eos())
                if not self.received_eos.wait(10):
                    debug("Timeout waiting for sink EOS")
            else:
                debug("Sending EOS to sink pipeline failed")
        else:
            debug("SinkPipeline teardown: Not sending EOS, no samples sent")

        self.sink_pipeline.set_state(Gst.State.NULL)

        # Don't want to cause the Display object to hang around on our account,
        # we won't be raising any errors from now on anyway:
        self._raise_in_user_thread = None
Exemplo n.º 6
0
    def __init__(self, user_source_pipeline, sink_pipeline):

        import time

        self._condition = threading.Condition()  # Protects last_frame
        self.last_frame = None
        self.last_used_frame = None
        self.source_pipeline = None
        self.init_time = time.time()
        self.tearing_down = False

        appsink = ("appsink name=appsink max-buffers=1 drop=false sync=true "
                   "emit-signals=true "
                   "caps=video/x-raw,format=BGR")
        # Notes on the source pipeline:
        # * _stbt_raw_frames_queue is kept small to reduce the amount of slack
        #   (and thus the latency) of the pipeline.
        # * _stbt_user_data_queue before the decodebin is large.  We don't want
        #   to drop encoded packets as this will cause significant image
        #   artifacts in the decoded buffers.  We make the assumption that we
        #   have enough horse-power to decode the incoming stream and any delays
        #   will be transient otherwise it could start filling up causing
        #   increased latency.
        self.source_pipeline_description = " ! ".join([
            user_source_pipeline,
            'queue name=_stbt_user_data_queue max-size-buffers=0 '
            '    max-size-bytes=0 max-size-time=10000000000', "decodebin",
            'queue name=_stbt_raw_frames_queue max-size-buffers=2',
            'videoconvert', 'video/x-raw,format=BGR', appsink
        ])
        self.create_source_pipeline()

        self._sink_pipeline = sink_pipeline

        debug("source pipeline: %s" % self.source_pipeline_description)
Exemplo n.º 7
0
def draw_text(text, duration_secs=3):
    """Write the specified text to the output video.

    :param str text: The text to write.

    :param duration_secs: The number of seconds to display the text.
    :type duration_secs: int or float
    """
    debug(text)
    return _dut.draw_text(text, duration_secs)
Exemplo n.º 8
0
    def __init__(self,
                 user_sink_pipeline,
                 raise_in_user_thread,
                 save_video=""):
        import time as _time

        self.annotations_lock = threading.Lock()
        self.text_annotations = []
        self.annotations = []
        self._raise_in_user_thread = raise_in_user_thread
        self.received_eos = threading.Event()
        self._frames = deque(maxlen=35)
        self._time = _time
        self._sample_count = 0

        # The test script can draw on the video, but this happens in a different
        # thread.  We don't know when they're finished drawing so we just give
        # them 0.5s instead.
        self._sink_latency_secs = 0.5

        sink_pipeline_description = (
            "appsrc name=appsrc format=time is-live=true "
            "caps=video/x-raw,format=(string)BGR ")

        if save_video and user_sink_pipeline:
            sink_pipeline_description += "! tee name=t "
            src = "t. ! queue leaky=downstream"
        else:
            src = "appsrc."

        if save_video:
            if not save_video.endswith(".webm"):
                save_video += ".webm"
            debug("Saving video to '%s'" % save_video)
            sink_pipeline_description += (
                "{src} ! videoconvert ! "
                "vp8enc cpu-used=6 min_quantizer=32 max_quantizer=32 ! "
                "webmmux ! filesink location={save_video} ").format(
                    src=src, save_video=save_video)

        if user_sink_pipeline:
            sink_pipeline_description += (
                "{src} ! videoconvert ! {user_sink_pipeline}").format(
                    src=src, user_sink_pipeline=user_sink_pipeline)

        self.sink_pipeline = Gst.parse_launch(sink_pipeline_description)
        sink_bus = self.sink_pipeline.get_bus()
        sink_bus.connect("message::error", self._on_error)
        sink_bus.connect("message::warning", self._on_warning)
        sink_bus.connect("message::eos", self._on_eos_from_sink_pipeline)
        sink_bus.add_signal_watch()
        self.appsrc = self.sink_pipeline.get_by_name("appsrc")

        debug("sink pipeline: %s" % sink_pipeline_description)
Exemplo n.º 9
0
def as_precondition(message):
    """Context manager that replaces test failures with test errors.

    Stb-tester's reports show test failures (that is, `UITestFailure` or
    `AssertionError` exceptions) as red results, and test errors (that is,
    unhandled exceptions of any other type) as yellow results. Note that
    `wait_for_match`, `wait_for_motion`, and similar functions raise a
    `UITestFailure` when they detect a failure. By running such functions
    inside an `as_precondition` context, any `UITestFailure` or
    `AssertionError` exceptions they raise will be caught, and a
    `PreconditionError` will be raised instead.

    When running a single testcase hundreds or thousands of times to reproduce
    an intermittent defect, it is helpful to mark unrelated failures as test
    errors (yellow) rather than test failures (red), so that you can focus on
    diagnosing the failures that are most likely to be the particular defect
    you are looking for. For more details see `Test failures vs. errors
    <http://stb-tester.com/preconditions>`__.

    :param str message:
        A description of the precondition. Word this positively: "Channels
        tuned", not "Failed to tune channels".

    :raises:
        `PreconditionError` if the wrapped code block raises a `UITestFailure`
        or `AssertionError`.

    Example::

        def test_that_the_on_screen_id_is_shown_after_booting():
            channel = 100

            with stbt.as_precondition("Tuned to channel %s" % channel):
                mainmenu.close_any_open_menu()
                channels.goto_channel(channel)
                power.cold_reboot()
                assert channels.is_on_channel(channel)

            stbt.wait_for_match("on-screen-id.png")

    """
    try:
        yield
    except (UITestFailure, AssertionError) as e:
        debug(
            "stbt.as_precondition caught a %s exception and will "
            "re-raise it as PreconditionError.\nOriginal exception was:\n%s" %
            (type(e).__name__, traceback.format_exc(e)))
        exc = PreconditionError(message, e)
        if hasattr(e, 'screenshot'):
            exc.screenshot = e.screenshot  # pylint: disable=attribute-defined-outside-init,no-member
        raise exc
Exemplo n.º 10
0
 def __exit__(self, _1, _2, _3):
     self.tearing_down = True
     self.source_pipeline, source = None, self.source_pipeline
     if source:
         if self.source_teardown_eos:
             debug("teardown: Sending eos on source pipeline")
             for elem in gst_iterate(source.iterate_sources()):
                 elem.send_event(Gst.Event.new_eos())
             if not self.appsink_await_eos(source.get_by_name('appsink'),
                                           timeout=10):
                 debug("Source pipeline did not teardown gracefully")
         source.set_state(Gst.State.NULL)
         source = None
Exemplo n.º 11
0
 def __exit__(self, _1, _2, _3):
     self.tearing_down = True
     self.source_pipeline, source = None, self.source_pipeline
     if source:
         if self.source_teardown_eos:
             debug("teardown: Sending eos on source pipeline")
             for elem in gst_iterate(source.iterate_sources()):
                 elem.send_event(Gst.Event.new_eos())
             if not self.appsink_await_eos(
                     source.get_by_name('appsink'), timeout=10):
                 debug("Source pipeline did not teardown gracefully")
         source.set_state(Gst.State.NULL)
         source = None
Exemplo n.º 12
0
def as_precondition(message):
    """Context manager that replaces test failures with test errors.

    Stb-tester's reports show test failures (that is, `UITestFailure` or
    `AssertionError` exceptions) as red results, and test errors (that is,
    unhandled exceptions of any other type) as yellow results. Note that
    `wait_for_match`, `wait_for_motion`, and similar functions raise a
    `UITestFailure` when they detect a failure. By running such functions
    inside an `as_precondition` context, any `UITestFailure` or
    `AssertionError` exceptions they raise will be caught, and a
    `PreconditionError` will be raised instead.

    When running a single testcase hundreds or thousands of times to reproduce
    an intermittent defect, it is helpful to mark unrelated failures as test
    errors (yellow) rather than test failures (red), so that you can focus on
    diagnosing the failures that are most likely to be the particular defect
    you are looking for. For more details see `Test failures vs. errors
    <http://stb-tester.com/preconditions>`__.

    :param str message:
        A description of the precondition. Word this positively: "Channels
        tuned", not "Failed to tune channels".

    :raises:
        `PreconditionError` if the wrapped code block raises a `UITestFailure`
        or `AssertionError`.

    Example::

        def test_that_the_on_screen_id_is_shown_after_booting():
            channel = 100

            with stbt.as_precondition("Tuned to channel %s" % channel):
                mainmenu.close_any_open_menu()
                channels.goto_channel(channel)
                power.cold_reboot()
                assert channels.is_on_channel(channel)

            stbt.wait_for_match("on-screen-id.png")

    """
    try:
        yield
    except (UITestFailure, AssertionError) as e:
        debug("stbt.as_precondition caught a %s exception and will "
              "re-raise it as PreconditionError.\nOriginal exception was:\n%s"
              % (type(e).__name__, traceback.format_exc(e)))
        exc = PreconditionError(message, e)
        if hasattr(e, 'screenshot'):
            exc.screenshot = e.screenshot  # pylint: disable=attribute-defined-outside-init,no-member
        raise exc
Exemplo n.º 13
0
    def __init__(self, user_sink_pipeline, raise_in_user_thread, save_video=""):
        import time as _time

        self.annotations_lock = threading.Lock()
        self.text_annotations = []
        self.annotations = []
        self._raise_in_user_thread = raise_in_user_thread
        self.received_eos = threading.Event()
        self._frames = deque(maxlen=35)
        self._time = _time
        self._sample_count = 0

        # The test script can draw on the video, but this happens in a different
        # thread.  We don't know when they're finished drawing so we just give
        # them 0.5s instead.
        self._sink_latency_secs = 0.5

        sink_pipeline_description = (
            "appsrc name=appsrc format=time is-live=true "
            "caps=video/x-raw,format=(string)BGR ")

        if save_video and user_sink_pipeline:
            sink_pipeline_description += "! tee name=t "
            src = "t. ! queue leaky=downstream"
        else:
            src = "appsrc."

        if save_video:
            if not save_video.endswith(".webm"):
                save_video += ".webm"
            debug("Saving video to '%s'" % save_video)
            sink_pipeline_description += (
                "{src} ! videoconvert ! "
                "vp8enc cpu-used=6 min_quantizer=32 max_quantizer=32 ! "
                "webmmux ! filesink location={save_video} ").format(
                src=src, save_video=save_video)

        if user_sink_pipeline:
            sink_pipeline_description += (
                "{src} ! videoconvert ! {user_sink_pipeline}").format(
                src=src, user_sink_pipeline=user_sink_pipeline)

        self.sink_pipeline = Gst.parse_launch(sink_pipeline_description)
        sink_bus = self.sink_pipeline.get_bus()
        sink_bus.connect("message::error", self._on_error)
        sink_bus.connect("message::warning", self._on_warning)
        sink_bus.connect("message::eos", self._on_eos_from_sink_pipeline)
        sink_bus.add_signal_watch()
        self.appsrc = self.sink_pipeline.get_by_name("appsrc")

        debug("sink pipeline: %s" % sink_pipeline_description)
Exemplo n.º 14
0
 def _adb(self, command, timeout_secs=None, **kwargs):
     _command = []
     if timeout_secs is not None:
         _command += ["timeout", "%fs" % timeout_secs]
     _command += [self.adb_binary]
     if self.adb_server:
         _command += ["-H", self.adb_server]
     if self.adb_device:
         _command += ["-s", self.adb_device]
     _command += command
     debug("AdbDevice.adb: About to run command: %r\n" % _command)
     output = subprocess.check_output(
         _command, stderr=subprocess.STDOUT, **kwargs)
     return output
Exemplo n.º 15
0
def _mainloop():
    mainloop = GLib.MainLoop.new(context=None, is_running=False)

    thread = threading.Thread(target=mainloop.run)
    thread.daemon = True
    thread.start()

    try:
        yield
    finally:
        mainloop.quit()
        thread.join(10)
        debug("teardown: Exiting (GLib mainloop %s)" %
              ("is still alive!" if thread.isAlive() else "ok"))
Exemplo n.º 16
0
 def _adb(self, command, timeout_secs=None, **kwargs):
     _command = []
     if timeout_secs is not None:
         _command += ["timeout", "%fs" % timeout_secs]
     _command += [self.adb_binary]
     if self.adb_server:
         _command += ["-H", self.adb_server]
     if self.adb_device:
         _command += ["-s", self.adb_device]
     _command += command
     debug("AdbDevice.adb: About to run command: %r\n" % _command)
     output = subprocess.check_output(
         _command, stderr=subprocess.STDOUT, **kwargs).decode("utf-8")
     return output
Exemplo n.º 17
0
def _mainloop():
    mainloop = GLib.MainLoop.new(context=None, is_running=False)

    thread = threading.Thread(target=mainloop.run)
    thread.daemon = True
    thread.start()

    try:
        yield
    finally:
        mainloop.quit()
        thread.join(10)
        debug("teardown: Exiting (GLib mainloop %s)" % (
              "is still alive!" if thread.isAlive() else "ok"))
Exemplo n.º 18
0
    def _push_sample(self, sample):
        # Calculate whether we need to draw any annotations on the output video.
        now = sample.time
        annotations = []
        with self.annotations_lock:
            # Remove expired annotations
            self.text_annotations = [x for x in self.text_annotations
                                     if now < x.end_time]
            current_texts = [x for x in self.text_annotations if x.time <= now]
            for annotation in list(self.annotations):
                if annotation.time == now:
                    annotations.append(annotation)
                if now >= annotation.time:
                    self.annotations.remove(annotation)

        sample = gst_sample_make_writable(sample)
        img = array_from_sample(sample, readwrite=True)
        # Text:
        _draw_text(
            img,
            datetime.datetime.fromtimestamp(now).strftime("%H:%M:%S.%f")[:-4],
            (10, 30), (255, 255, 255))
        for i, x in enumerate(reversed(current_texts)):
            origin = (10, (i + 2) * 30)
            age = float(now - x.time) / 3
            color = (native(int(255 * max([1 - age, 0.5]))).__int__(),) * 3
            _draw_text(img, x.text, origin, color)

        # Regions:
        for annotation in annotations:
            _draw_annotation(img, annotation)

        APPSRC_LIMIT_BYTES = 100 * 1024 * 1024  # 100MB
        if self.appsrc.props.current_level_bytes > APPSRC_LIMIT_BYTES:
            # appsrc is backed-up, perhaps something's gone wrong.  We don't
            # want to use up all RAM, so let's drop the buffer on the floor.
            if not self._appsrc_was_full:
                warn("sink pipeline appsrc is full, dropping buffers from now "
                     "on")
                self._appsrc_was_full = True
            return
        elif self._appsrc_was_full:
            debug("sink pipeline appsrc no longer full, pushing buffers again")
            self._appsrc_was_full = False

        self.appsrc.props.caps = sample.get_caps()
        self.appsrc.emit("push-buffer", sample.get_buffer())
        self._sample_count += 1
Exemplo n.º 19
0
    def tap(self, position):
        """Tap on a particular location.

        :param position: A `stbt.Region`, or an (x,y) tuple.

        Example::

            d.tap((100, 20))
            d.tap(stbt.match(...).region)

        """
        x, y = _centre_point(position)
        debug("AdbDevice.tap((%d,%d))" % (x, y))

        x, y = self._to_native_coordinates(x, y)
        self.adb(["shell", "input", "tap", str(x), str(y)], timeout_secs=10)
Exemplo n.º 20
0
    def tap(self, position):
        """Tap on a particular location.

        :param position: A `stbt.Region`, or an (x,y) tuple.

        Example::

            d.tap((100, 20))
            d.tap(stbt.match(...).region)

        """
        x, y = _centre_point(position)
        debug("AdbDevice.tap((%d,%d))" % (x, y))

        x, y = self._to_native_coordinates(x, y)
        self.adb(["shell", "input", "tap", str(x), str(y)], timeout_secs=10)
Exemplo n.º 21
0
    def press(self, key):
        """Send a keypress.

        :param str key: An Android keycode as listed in
            <https://developer.android.com/reference/android/view/KeyEvent.html>.
            Particularly useful key codes are "KEYCODE_HOME" and
            "KEYCODE_BACK", which are physical buttons on some phones so you
            can't hit them with `AdbDevice.tap`. Also accepts standard
            Stb-tester key names like "KEY_HOME" and "KEY_BACK".
        """
        # "adb shell input keyevent xxx" always returns success, so we need to
        # validate key names.
        if key in _KEYCODE_MAPPINGS:
            key = _KEYCODE_MAPPINGS[key]  # Map Stb-tester names to Android ones
        if key not in _ANDROID_KEYCODES:
            raise ValueError("Unknown key code %r" % (key,))
        debug("AdbDevice.press(%r)" % key)
        self.adb(["shell", "input", "keyevent", key], timeout_secs=10)
Exemplo n.º 22
0
    def press(self, key):
        """Send a keypress.

        :param str key: An Android keycode as listed in
            <https://developer.android.com/reference/android/view/KeyEvent.html>.
            Particularly useful key codes are "KEYCODE_HOME" and
            "KEYCODE_BACK", which are physical buttons on some phones so you
            can't hit them with `AdbDevice.tap`. Also accepts standard
            Stb-tester key names like "KEY_HOME" and "KEY_BACK".
        """
        # "adb shell input keyevent xxx" always returns success, so we need to
        # validate key names.
        if key in _KEYCODE_MAPPINGS:
            key = _KEYCODE_MAPPINGS[key]  # Map Stb-tester names to Android ones
        if key not in _ANDROID_KEYCODES:
            raise ValueError("Unknown key code %r" % (key,))
        debug("AdbDevice.press(%r)" % key)
        self.adb(["shell", "input", "keyevent", key], timeout_secs=10)
Exemplo n.º 23
0
    def frames(self, timeout_secs=None):
        if timeout_secs is not None:
            end_time = self._time.time() + timeout_secs
        timestamp = None
        first = True

        while True:
            ddebug("user thread: Getting sample at %s" % self._time.time())
            frame = self._display.get_frame(
                max(10, timeout_secs), since=timestamp)
            ddebug("user thread: Got sample at %s" % self._time.time())
            timestamp = frame.time

            if not first and timeout_secs is not None and timestamp > end_time:
                debug("timed out: %.3f > %.3f" % (timestamp, end_time))
                return

            yield frame
            first = False
Exemplo n.º 24
0
    def is_screen_black(self,
                        frame=None,
                        mask=None,
                        threshold=None,
                        region=Region.ALL):
        if threshold is None:
            threshold = get_config('is_screen_black', 'threshold', type_=int)

        if frame is None:
            frame = self.get_frame()

        if mask is None:
            mask = _ImageFromUser(None, None, None)
        else:
            mask = _load_image(mask, cv2.IMREAD_GRAYSCALE)

        _region = Region.intersect(_image_region(frame), region)
        greyframe = cv2.cvtColor(crop(frame, _region), cv2.COLOR_BGR2GRAY)
        if mask.image is not None:
            cv2.bitwise_and(greyframe, mask.image, dst=greyframe)
        maxVal = greyframe.max()

        if logging.get_debug_level() > 1:
            imglog = logging.ImageLogger("is_screen_black")
            imglog.imwrite("source", frame)
            if mask.image is not None:
                imglog.imwrite('mask', mask.image)
            _, thresholded = cv2.threshold(greyframe, threshold, 255,
                                           cv2.THRESH_BINARY)
            imglog.imwrite('non-black-regions-after-masking', thresholded)

        result = _IsScreenBlackResult(bool(maxVal <= threshold), frame)
        debug("is_screen_black: {found} black screen using mask={mask}, "
              "threshold={threshold}, region={region}: "
              "{result}, maximum_intensity={maxVal}".format(
                  found="Found" if result.black else "Didn't find",
                  mask=mask.friendly_name,
                  threshold=threshold,
                  region=region,
                  result=result,
                  maxVal=maxVal))
        return result
Exemplo n.º 25
0
    def __init__(self, user_source_pipeline, sink_pipeline,
                 restart_source=False, source_teardown_eos=False):

        import time

        self._condition = threading.Condition()  # Protects last_frame
        self.last_frame = None
        self.last_used_frame = None
        self.source_pipeline = None
        self.init_time = time.time()
        self.underrun_timeout = None
        self.tearing_down = False
        self.restart_source_enabled = restart_source
        self.source_teardown_eos = source_teardown_eos

        appsink = (
            "appsink name=appsink max-buffers=1 drop=false sync=true "
            "emit-signals=true "
            "caps=video/x-raw,format=BGR")
        # Notes on the source pipeline:
        # * _stbt_raw_frames_queue is kept small to reduce the amount of slack
        #   (and thus the latency) of the pipeline.
        # * _stbt_user_data_queue before the decodebin is large.  We don't want
        #   to drop encoded packets as this will cause significant image
        #   artifacts in the decoded buffers.  We make the assumption that we
        #   have enough horse-power to decode the incoming stream and any delays
        #   will be transient otherwise it could start filling up causing
        #   increased latency.
        self.source_pipeline_description = " ! ".join([
            user_source_pipeline,
            'queue name=_stbt_user_data_queue max-size-buffers=0 '
            '    max-size-bytes=0 max-size-time=10000000000',
            "decodebin",
            'queue name=_stbt_raw_frames_queue max-size-buffers=2',
            'videoconvert',
            'video/x-raw,format=BGR',
            appsink])
        self.create_source_pipeline()

        self._sink_pipeline = sink_pipeline

        debug("source pipeline: %s" % self.source_pipeline_description)
Exemplo n.º 26
0
def main(argv):
    parser = argparse.ArgumentParser(
        epilog=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument(
        "--input", default="lircd", help="""The source of remote control
        presses. Values are the same as stbt record's --control-recorder.""")
    parser.add_argument("output", nargs="+", help="""One or more remote control
        configurations. Values are the same as stbt run's --control.""")
    argparser_add_verbose_argument(parser)
    args = parser.parse_args(argv[1:])

    signal.signal(signal.SIGTERM, lambda _signo, _stack_frame: sys.exit(0))

    r = MultiRemote(uri_to_remote(x) for x in args.output)
    listener = uri_to_remote_recorder(args.input)
    for key in listener:
        debug("Received %s" % key)
        try:
            r.press(key)
        except Exception as e:  # pylint: disable=broad-except
            sys.stderr.write("Error pressing key %r: %s\n" % (key, e))
Exemplo n.º 27
0
    def swipe(self, start_position, end_position):
        """Swipe from one point to another point.

        :param start_position:
            A `stbt.Region` or (x, y) tuple of coordinates at which to start.
        :param end_position:
            A `stbt.Region` or (x, y) tuple of coordinates at which to stop.

        Example::

          d.swipe((100, 100), (100, 400))

        """
        x1, y1 = _centre_point(start_position)
        x2, y2 = _centre_point(end_position)
        debug("AdbDevice.swipe((%d,%d), (%d,%d))" % (x1, y1, x2, y2))

        x1, y1 = self._to_native_coordinates(x1, y1)
        x2, y2 = self._to_native_coordinates(x2, y2)
        command = ["shell", "input", "swipe",
                   str(x1), str(y1), str(x2), str(y2)]
        self.adb(command, timeout_secs=10)
Exemplo n.º 28
0
def main(argv):
    parser = argparse.ArgumentParser(
        epilog=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--input",
                        default="lircd",
                        help="""The source of remote control
        presses. Values are the same as stbt record's --control-recorder.""")
    parser.add_argument("output",
                        nargs="+",
                        help="""One or more remote control
        configurations. Values are the same as stbt run's --control.""")
    argparser_add_verbose_argument(parser)
    args = parser.parse_args(argv[1:])

    signal.signal(signal.SIGTERM, lambda _signo, _stack_frame: sys.exit(0))

    r = MultiRemote(uri_to_remote(x) for x in args.output)
    listener = uri_to_remote_recorder(args.input)
    for key in listener:
        debug("Received %s" % key)
        try:
            r.press(key)
        except Exception as e:  # pylint: disable=broad-except
            sys.stderr.write("Error pressing key %r: %s\n" % (key, e))
Exemplo n.º 29
0
def wait_until(callable_,
               timeout_secs=10,
               interval_secs=0,
               predicate=None,
               stable_secs=0):
    """Wait until a condition becomes true, or until a timeout.

    Calls ``callable_`` repeatedly (with a delay of ``interval_secs`` seconds
    between successive calls) until it succeeds (that is, it returns a
    `truthy`_ value) or until ``timeout_secs`` seconds have passed.

    .. _truthy: https://docs.python.org/2/library/stdtypes.html#truth-value-testing

    :param callable_: any Python callable (such as a function or a lambda
        expression) with no arguments.

    :type timeout_secs: int or float, in seconds
    :param timeout_secs: After this timeout elapses, ``wait_until`` will return
        the last value that ``callable_`` returned, even if it's falsey.

    :type interval_secs: int or float, in seconds
    :param interval_secs: Delay between successive invocations of ``callable_``.

    :param predicate: A function that takes a single value. It will be given
        the return value from ``callable_``. The return value of *this* function
        will then be used to determine truthiness. If the predicate test
        succeeds, ``wait_until`` will still return the original value from
        ``callable_``, not the predicate value.

    :type stable_secs: int or float, in seconds
    :param stable_secs: Wait for ``callable_``'s return value to remain the same
        (as determined by ``==``) for this duration before returning. If
        ``predicate`` is also given, the values returned from ``predicate``
        will be compared.

    :returns: The return value from ``callable_`` (which will be truthy if it
        succeeded, or falsey if ``wait_until`` timed out). If the value was
        truthy when the timeout was reached but it failed the ``predicate`` or
        ``stable_secs`` conditions (if any) then ``wait_until`` returns
        ``None``.

    After you send a remote-control signal to the device-under-test it usually
    takes a few frames to react, so a test script like this would probably
    fail::

        press("KEY_EPG")
        assert match("guide.png")

    Instead, use this::

        press("KEY_EPG")
        assert wait_until(lambda: match("guide.png"))

    Note that instead of the above ``assert wait_until(...)`` you could use
    ``wait_for_match("guide.png")``. ``wait_until`` is a generic solution that
    also works with stbt's other functions, like `match_text` and
    `is_screen_black`.

    ``wait_until`` allows composing more complex conditions, such as::

        # Wait until something disappears:
        assert wait_until(lambda: not match("xyz.png"))

        # Assert that something doesn't appear within 10 seconds:
        assert not wait_until(lambda: match("xyz.png"))

        # Assert that two images are present at the same time:
        assert wait_until(lambda: match("a.png") and match("b.png"))

        # Wait but don't raise an exception:
        if not wait_until(lambda: match("xyz.png")):
            do_something_else()

        # Wait for a menu selection to change. Here ``Menu`` is a `FrameObject`
        # with a property called `selection` that returns a string with the
        # name of the currently-selected menu item:
        # The return value (``menu``) is an instance of ``Menu``.
        menu = wait_until(Menu, predicate=lambda x: x.selection == "Home")

        # Wait for a match to stabilise position, returning the first stable
        # match. Used in performance measurements, for example to wait for a
        # selection highlight to finish moving:
        press("KEY_DOWN")
        start_time = time.time()
        match_result = wait_until(lambda: stbt.match("selection.png"),
                                  predicate=lambda x: x and x.region,
                                  stable_secs=2)
        assert match_result
        end_time = match_result.time  # this is the first stable frame
        print "Transition took %s seconds" % (end_time - start_time)

    Added in v28: The ``predicate`` and ``stable_secs`` parameters.
    """
    import time

    if predicate is None:
        predicate = lambda x: x
    stable_value = None
    stable_predicate_value = None
    expiry_time = time.time() + timeout_secs

    while True:
        t = time.time()
        value = callable_()
        predicate_value = predicate(value)

        if stable_secs:
            if predicate_value != stable_predicate_value:
                stable_since = t
                stable_value = value
                stable_predicate_value = predicate_value
            if predicate_value and t - stable_since >= stable_secs:
                return stable_value
        else:
            if predicate_value:
                return value

        if t >= expiry_time:
            debug("wait_until timed out: %s" %
                  _callable_description(callable_))
            if not value:
                return value  # it's falsey
            else:
                return None  # must have failed stable_secs or predicate checks

        time.sleep(interval_secs)
Exemplo n.º 30
0
 def _on_eos_from_sink_pipeline(self, _bus, _message):
     debug("Got EOS from sink pipeline")
     self.received_eos.set()
Exemplo n.º 31
0
 def _on_eos_from_sink_pipeline(self, _bus, _message):
     debug("Got EOS from sink pipeline")
     self.received_eos.set()
Exemplo n.º 32
0
def wait_until(callable_, timeout_secs=10, interval_secs=0, predicate=None,
               stable_secs=0):
    """Wait until a condition becomes true, or until a timeout.

    Calls ``callable_`` repeatedly (with a delay of ``interval_secs`` seconds
    between successive calls) until it succeeds (that is, it returns a
    `truthy`_ value) or until ``timeout_secs`` seconds have passed.

    .. _truthy: https://docs.python.org/2/library/stdtypes.html#truth-value-testing

    :param callable_: any Python callable (such as a function or a lambda
        expression) with no arguments.

    :type timeout_secs: int or float, in seconds
    :param timeout_secs: After this timeout elapses, ``wait_until`` will return
        the last value that ``callable_`` returned, even if it's falsey.

    :type interval_secs: int or float, in seconds
    :param interval_secs: Delay between successive invocations of ``callable_``.

    :param predicate: A function that takes a single value. It will be given
        the return value from ``callable_``. The return value of *this* function
        will then be used to determine truthiness. If the predicate test
        succeeds, ``wait_until`` will still return the original value from
        ``callable_``, not the predicate value.

    :type stable_secs: int or float, in seconds
    :param stable_secs: Wait for ``callable_``'s return value to remain the same
        (as determined by ``==``) for this duration before returning. If
        ``predicate`` is also given, the values returned from ``predicate``
        will be compared.

    :returns: The return value from ``callable_`` (which will be truthy if it
        succeeded, or falsey if ``wait_until`` timed out). If the value was
        truthy when the timeout was reached but it failed the ``predicate`` or
        ``stable_secs`` conditions (if any) then ``wait_until`` returns
        ``None``.

    After you send a remote-control signal to the device-under-test it usually
    takes a few frames to react, so a test script like this would probably
    fail::

        press("KEY_EPG")
        assert match("guide.png")

    Instead, use this::

        press("KEY_EPG")
        assert wait_until(lambda: match("guide.png"))

    Note that instead of the above ``assert wait_until(...)`` you could use
    ``wait_for_match("guide.png")``. ``wait_until`` is a generic solution that
    also works with stbt's other functions, like `match_text` and
    `is_screen_black`.

    ``wait_until`` allows composing more complex conditions, such as::

        # Wait until something disappears:
        assert wait_until(lambda: not match("xyz.png"))

        # Assert that something doesn't appear within 10 seconds:
        assert not wait_until(lambda: match("xyz.png"))

        # Assert that two images are present at the same time:
        assert wait_until(lambda: match("a.png") and match("b.png"))

        # Wait but don't raise an exception:
        if not wait_until(lambda: match("xyz.png")):
            do_something_else()

        # Wait for a menu selection to change. Here ``Menu`` is a `FrameObject`
        # with a property called `selection` that returns a string with the
        # name of the currently-selected menu item:
        # The return value (``menu``) is an instance of ``Menu``.
        menu = wait_until(Menu, predicate=lambda x: x.selection == "Home")

        # Wait for a match to stabilise position, returning the first stable
        # match. Used in performance measurements, for example to wait for a
        # selection highlight to finish moving:
        press("KEY_DOWN")
        start_time = time.time()
        match_result = wait_until(lambda: stbt.match("selection.png"),
                                  predicate=lambda x: x and x.region,
                                  stable_secs=2)
        assert match_result
        end_time = match_result.time  # this is the first stable frame
        print "Transition took %s seconds" % (end_time - start_time)

    Added in v28: The ``predicate`` and ``stable_secs`` parameters.
    """
    import time

    if predicate is None:
        predicate = lambda x: x
    stable_value = None
    stable_predicate_value = None
    expiry_time = time.time() + timeout_secs

    while True:
        t = time.time()
        value = callable_()
        predicate_value = predicate(value)

        if stable_secs:
            if predicate_value != stable_predicate_value:
                stable_since = t
                stable_value = value
                stable_predicate_value = predicate_value
            if predicate_value and t - stable_since >= stable_secs:
                debug("wait_until succeeded: %s"
                      % _callable_description(callable_))
                return stable_value
        else:
            if predicate_value:
                debug("wait_until succeeded: %s"
                      % _callable_description(callable_))
                return value

        if t >= expiry_time:
            debug("wait_until timed out after %s seconds: %s"
                  % (timeout_secs, _callable_description(callable_)))
            if not value:
                return value  # it's falsey
            else:
                return None  # must have failed stable_secs or predicate checks

        time.sleep(interval_secs)