From d2b8e9d3d9b8ec152d87c77e2acbfb0d76329ae6 Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Thu, 23 Oct 2014 21:46:04 +0200 Subject: [PATCH] tests: implement our validate TestManager. And make sure it installs alongside the other validate apps. https://bugzilla.gnome.org/show_bug.cgi?id=739093 --- configure.ac | 3 +- tests/Makefile.am | 8 +- tests/validate/Makefile.am | 7 + tests/validate/geslaunch.py | 302 ++++++++++++++++++ tests/{ => validate}/scenarios/Makefile.am | 0 .../ges-edit-clip-while-paused.scenario | 0 6 files changed, 315 insertions(+), 5 deletions(-) create mode 100644 tests/validate/Makefile.am create mode 100644 tests/validate/geslaunch.py rename tests/{ => validate}/scenarios/Makefile.am (100%) rename tests/{ => validate}/scenarios/ges-edit-clip-while-paused.scenario (100%) diff --git a/configure.ac b/configure.ac index 138e2584f5..ec5f1b4a10 100644 --- a/configure.ac +++ b/configure.ac @@ -386,7 +386,8 @@ tests/Makefile tests/check/Makefile tests/benchmarks/Makefile tests/examples/Makefile -tests/scenarios/Makefile +tests/validate/Makefile +tests/validate/scenarios/Makefile tools/Makefile docs/Makefile docs/version.entities diff --git a/tests/Makefile.am b/tests/Makefile.am index ce15144594..3b1d741a3f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -17,12 +17,12 @@ BENCHMARKS_SUBDIR= endif if HAVE_GST_VALIDATE -SCENARIOS_SUBDIRS= scenarios +VALIDATE_SUBDIRS= validate else -SCENARIOS_SUBDIRS= +VALIDATE_SUBDIRS= endif -SUBDIRS= $(CHECK_SUBDIRS) $(EXAMPLES_SUBDIRS) $(BENCHMARKS_SUBDIR) $(SCENARIOS_SUBDIRS) +SUBDIRS= $(CHECK_SUBDIRS) $(EXAMPLES_SUBDIRS) $(BENCHMARKS_SUBDIR) $(VALIDATE_SUBDIRS) -DIST_SUBDIRS = check examples benchmarks scenarios +DIST_SUBDIRS = check examples benchmarks validate diff --git a/tests/validate/Makefile.am b/tests/validate/Makefile.am new file mode 100644 index 0000000000..2a59db7d5c --- /dev/null +++ b/tests/validate/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS = scenarios + +validatedir=${libdir}/gst-validate-launcher/python/launcher/apps/ + +validate_DATA = geslaunch.py + +EXTRA_DIST = geslaunch.py diff --git a/tests/validate/geslaunch.py b/tests/validate/geslaunch.py new file mode 100644 index 0000000000..d9620371f7 --- /dev/null +++ b/tests/validate/geslaunch.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2013,Thibault Saunier +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +import os +import sys +import urlparse +import subprocess +import utils +from urllib import unquote +import xml.etree.ElementTree as ET +from baseclasses import GstValidateTest, TestsManager, ScenarioManager, MediaFormatCombination, \ + MediaDescriptor, GstValidateEncodingTestInterface + +GES_DURATION_TOLERANCE = utils.GST_SECOND / 2 + +GES_LAUNCH_COMMAND = "ges-launch-1.0" +if "win32" in sys.platform: + GES_LAUNCH_COMMAND += ".exe" + + +GES_ENCODING_TARGET_COMBINATIONS = [ + MediaFormatCombination("ogg", "vorbis", "theora"), + MediaFormatCombination("webm", "vorbis", "vp8"), + MediaFormatCombination("mp4", "mp3", "h264"), + MediaFormatCombination("mkv", "vorbis", "h264")] + + +def quote_uri(uri): + """ + Encode a URI/path according to RFC 2396, without touching the file:/// part. + """ + # Split off the "file:///" part, if present. + parts = urlparse.urlsplit(uri, allow_fragments=False) + # Make absolutely sure the string is unquoted before quoting again! + raw_path = unquote(parts.path) + return utils.path2url(raw_path) + + +class XgesProjectDescriptor(MediaDescriptor): + def __init__(self, uri): + super(XgesProjectDescriptor, self).__init__() + + self._uri = uri + self._xml_path = utils.url2path(uri) + self._root = ET.parse(self._xml_path) + self._duration = None + + def get_media_filepath(self): + return self._xml_path + + def get_path(self): + return self._xml_path + + def get_caps(self): + raise NotImplemented + + def get_uri(self): + return self._uri + + def get_duration(self): + if self._duration: + print("RETURN %s" % self._duration) + return self._duration + + for l in self._root.iter(): + if l.tag == "timeline": + self._duration=long(l.attrib['metadatas'].split("duration=(guint64)")[1].split(" ")[0].split(";")[0]) + break + + if not self._duration: + self.error("%s does not have duration! (setting 2mins)" % self._uri) + self._duration = 2 * 60 + + print("RETURN %s" % self._duration) + return self._duration + + def get_protocol(self): + return Protocols.FILE + + def is_seekable(self): + return True + + def is_image(self): + return False + + def get_num_tracks(self, track_type): + num_tracks = 0 + for l in self._root.iter(): + if l.tag == "track": + if track_type in l.attrib["caps"]: + num_tracks += 1 + return num_tracks + + +class GESTest(GstValidateTest): + def __init__(self, classname, options, reporter, project_uri, scenario=None, + combination=None): + + super(GESTest, self).__init__(GES_LAUNCH_COMMAND, classname, options, reporter, + scenario=scenario) + + self.project = XgesProjectDescriptor(project_uri) + + def set_sample_paths(self): + if not self.options.paths: + if self.options.disable_recurse: + return + paths = [os.path.dirname(self.project.get_media_filepath())] + else: + paths = self.options.paths + + if not isinstance(paths, list): + paths = [paths] + + for path in paths: + # We always want paths separator to be cut with '/' for ges-launch + path = path.replace("\\", "/") + if not self.options.disable_recurse: + self.add_arguments("--sample-path-recurse", quote_uri(path)) + else: + self.add_arguments("--sample-path", quote_uri(path)) + + def build_arguments(self): + GstValidateTest.build_arguments(self) + + if self.options.mute: + self.add_arguments(" --mute") + + self.set_sample_paths() + self.add_arguments("-l", self.project.get_uri()) + + +class GESPlaybackTest(GESTest): + def __init__(self, classname, options, reporter, project_uri, scenario): + super(GESPlaybackTest, self).__init__(classname, options, reporter, + project_uri, scenario=scenario) + + def get_current_value(self): + return self.get_current_position() + + +class GESRenderTest(GESTest, GstValidateEncodingTestInterface): + def __init__(self, classname, options, reporter, project_uri, combination): + GESTest.__init__(self, classname, options, reporter, project_uri) + + GstValidateEncodingTestInterface.__init__(self, combination, self.project) + + def build_arguments(self): + GESTest.build_arguments(self) + self._set_rendering_info() + + def _set_rendering_info(self): + self.dest_file = path = os.path.join(self.options.dest, + self.classname.replace(".render.", os.sep). + replace(".", os.sep)) + utils.mkdir(os.path.dirname(urlparse.urlsplit(self.dest_file).path)) + if not utils.isuri(self.dest_file): + self.dest_file = utils.path2url(self.dest_file) + + profile = self.get_profile(video_restriction="video/x-raw,format=I420") + self.add_arguments("-f", profile, "-o", self.dest_file) + + def check_results(self): + if self.result in [Result.PASSED, Result.NOT_RUN] and self.scenario is None: + res, msg = self.check_encoded_file() + self.set_result(res, msg) + else: + if self.result == utils.Result.TIMEOUT: + missing_eos = False + try: + if utils.get_duration(self.dest_file) == self.project.get_duration(): + missing_eos = True + except Exception as e: + pass + + if missing_eos is True: + self.set_result(utils.Result.TIMEOUT, "The rendered file add right duration, MISSING EOS?\n", + "failure", e) + else: + GstValidateTest.check_results(self) + + def get_current_value(self): + size = self.get_current_size() + if size is None: + return self.get_current_position() + + return size + + +class GESTestsManager(TestsManager): + name = "ges" + + _scenarios = ScenarioManager() + + def __init__(self): + super(GESTestsManager, self).__init__() + + def init(self): + try: + if "--set-scenario=" in subprocess.check_output([GES_LAUNCH_COMMAND, "--help"]): + + return True + else: + self.warning("Can not use ges-launch, it seems not to be compiled against" + " gst-validate") + except subprocess.CalledProcessError as e: + self.warning("Can not use ges-launch: %s" % e) + except OSError as e: + self.warning("Can not use ges-launch: %s" % e) + + def add_options(self, parser): + group = parser.add_argument_group("GStreamer Editing Services specific option" + " and behaviours", + description=""" +The GStreamer Editing Services launcher will be usable only if GES has been compiled against GstValidate +You can simply run scenarios specifying project as args. For example the following will run all available +and activated scenarios on project.xges: + + $gst-validate-launcher ges /some/ges/project.xges + + +Available options:""") + group.add_argument("-P", "--projects-paths", dest="projects_paths", + default=os.path.join(utils.DEFAULT_GST_QA_ASSETS, + "ges-projects"), + help="Paths in which to look for moved medias") + group.add_argument("-r", "--disable-recurse-paths", dest="disable_recurse", + default=False, action="store_true", + help="Whether to recurse into paths to find medias") + + def set_settings(self, options, args, reporter): + TestsManager.set_settings(self, options, args, reporter) + + try: + os.makedirs(utils.url2path(options.dest)[0]) + except OSError: + pass + + def list_tests(self): + if self.tests: + return self.tests + + projects = list() + if not self.args: + path = self.options.projects_paths + for root, dirs, files in os.walk(path): + for f in files: + if not f.endswith(".xges"): + continue + projects.append(utils.path2url(os.path.join(path, root, f))) + else: + for proj in self.args: + if not utils.isuri(proj): + proj = utils.path2url(proj) + + if os.path.exists(proj): + projects.append(proj) + + SCENARIOS = ["play_15s", + "scrub_forward_seeking", + "scrub_backward_seeking"] + for proj in projects: + # First playback casses + for scenario_name in SCENARIOS: + scenario = self._scenarios.get_scenario(scenario_name) + if scenario is None: + continue + classname = "ges.playback.%s.%s" % (scenario.name, + os.path.basename(proj).replace(".xges", "")) + self.add_test(GESPlaybackTest(classname, + self.options, + self.reporter, + proj, + scenario=scenario) + ) + + # And now rendering casses + for comb in GES_ENCODING_TARGET_COMBINATIONS: + classname = "ges.render.%s.%s" % (str(comb).replace(' ', '_'), + os.path.splitext(os.path.basename(proj))[0]) + self.add_test(GESRenderTest(classname, self.options, + self.reporter, proj, + combination=comb) + ) + + return self.tests diff --git a/tests/scenarios/Makefile.am b/tests/validate/scenarios/Makefile.am similarity index 100% rename from tests/scenarios/Makefile.am rename to tests/validate/scenarios/Makefile.am diff --git a/tests/scenarios/ges-edit-clip-while-paused.scenario b/tests/validate/scenarios/ges-edit-clip-while-paused.scenario similarity index 100% rename from tests/scenarios/ges-edit-clip-while-paused.scenario rename to tests/validate/scenarios/ges-edit-clip-while-paused.scenario