3 # Copyright (c) 2013,Thibault Saunier <thibault.saunier@collabora.com>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program; if not, write to the
17 # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 # Boston, MA 02110-1301, USA.
30 from launcher.loggable import Loggable
32 from launcher.baseclasses import GstValidateTest, Test, \
33 ScenarioManager, NamedDic, GstValidateTestsGenerator, \
34 GstValidateMediaDescriptor, GstValidateEncodingTestInterface, \
35 GstValidateBaseTestManager, MediaDescriptor, MediaFormatCombination
37 from launcher.utils import path2url, url2path, DEFAULT_TIMEOUT, which, \
38 GST_SECOND, Result, Protocols, mkdir, printc, Colors, get_data_file, \
42 # Private global variables #
45 # definitions of commands to use
46 parser = argparse.ArgumentParser(add_help=False)
47 parser.add_argument("--validate-tools-path", dest="validate_tools_path",
49 help="defines the paths to look for GstValidate tools.")
50 options, args = parser.parse_known_args()
52 GstValidateBaseTestManager.update_commands(options.validate_tools_path)
53 AUDIO_ONLY_FILE_TRANSCODING_RATIO = 5
56 # API to be used to create testsuites #
60 Some info about protocols and how to handle them
62 GST_VALIDATE_CAPS_TO_PROTOCOL = [("application/x-hls", Protocols.HLS),
63 ("application/dash+xml", Protocols.DASH)]
66 class GstValidateMediaCheckTestsGenerator(GstValidateTestsGenerator):
68 def __init__(self, test_manager):
69 GstValidateTestsGenerator.__init__(self, "media_check", test_manager)
71 def populate_tests(self, uri_minfo_special_scenarios, scenarios):
72 for uri, mediainfo, special_scenarios in uri_minfo_special_scenarios:
73 protocol = mediainfo.media_descriptor.get_protocol()
74 timeout = DEFAULT_TIMEOUT
76 classname = "%s.media_check.%s" % (protocol,
77 os.path.basename(url2path(uri)).replace(".", "_"))
78 self.add_test(GstValidateMediaCheckTest(classname,
79 self.test_manager.options,
80 self.test_manager.reporter,
81 mediainfo.media_descriptor,
87 class GstValidateTranscodingTestsGenerator(GstValidateTestsGenerator):
89 def __init__(self, test_manager):
90 GstValidateTestsGenerator.__init__(self, "transcode", test_manager)
92 def populate_tests(self, uri_minfo_special_scenarios, scenarios):
93 for uri, mediainfo, special_scenarios in uri_minfo_special_scenarios:
94 if mediainfo.media_descriptor.is_image():
97 protocol = mediainfo.media_descriptor.get_protocol()
98 if protocol == Protocols.RTSP:
101 for comb in self.test_manager.get_encoding_formats():
102 classname = "%s.transcode.to_%s.%s" % (mediainfo.media_descriptor.get_protocol(),
105 mediainfo.media_descriptor.get_clean_name())
106 self.add_test(GstValidateTranscodingTest(classname,
107 self.test_manager.options,
108 self.test_manager.reporter,
111 mediainfo.media_descriptor))
114 class FakeMediaDescriptor(MediaDescriptor):
116 def __init__(self, infos, pipeline_desc):
117 MediaDescriptor.__init__(self)
119 self._pipeline_desc = pipeline_desc
122 return self._infos.get('path', None)
124 def get_media_filepath(self):
125 return self._infos.get('media-filepath', None)
128 return self._infos.get('caps', None)
131 return self._infos.get('uri', None)
133 def get_duration(self):
134 return int(self._infos.get('duration', 0)) * GST_SECOND
136 def get_protocol(self):
137 return self._infos.get('protocol', "launch_pipeline")
139 def is_seekable(self):
140 return self._infos.get('is-seekable', True)
143 return self._infos.get('is-image', False)
146 return self._infos.get('is-live', False)
148 def get_num_tracks(self, track_type):
149 return self._infos.get('num-%s-tracks' % track_type,
150 self._pipeline_desc.count(track_type + "sink"))
152 def can_play_reverse(self):
153 return self._infos.get('plays-reverse', False)
156 class GstValidatePipelineTestsGenerator(GstValidateTestsGenerator):
158 def __init__(self, name, test_manager, pipeline_template=None,
159 pipelines_descriptions=None, valid_scenarios=None):
161 @name: The name of the generator
162 @pipeline_template: A template pipeline to be used to generate actual pipelines
163 @pipelines_descriptions: A list of tuple of the form:
164 (test_name, pipeline_description, extra_data)
165 extra_data being a dictionnary with the follwing keys:
166 'scenarios': ["the", "valide", "scenarios", "names"]
167 'duration': the_duration # in seconds
168 'timeout': a_timeout # in seconds
169 'hard_timeout': a_hard_timeout # in seconds
171 @valid_scenarios: A list of scenario name that can be used with that generator
173 valid_scenarios = valid_scenarios or []
174 GstValidateTestsGenerator.__init__(self, name, test_manager)
175 self._pipeline_template = pipeline_template
176 self._pipelines_descriptions = []
177 for description in pipelines_descriptions or []:
178 if not isinstance(description, dict):
179 desc_dict = {"name": description[0],
180 "pipeline": description[1]}
181 if len(description) >= 3:
182 desc_dict["extra_data"] = description[2]
183 self._pipelines_descriptions.append(desc_dict)
185 self._pipelines_descriptions.append(description)
186 self._valid_scenarios = valid_scenarios
189 def from_json(cls, test_manager, json_file, extra_data=None):
191 :param json_file: Path to a JSON file containing pipeline tests.
192 :param extra_data: Variables available for interpolation in validate
193 configs and scenario actions.
195 if extra_data is None:
197 with open(json_file, 'r') as f:
198 descriptions = json.load(f)
200 name = os.path.basename(json_file).replace('.json', '')
201 pipelines_descriptions = []
202 for test_name, defs in descriptions.items():
203 tests_definition = {'name': test_name, 'pipeline': defs['pipeline']}
204 test_private_dir = os.path.join(test_manager.options.privatedir,
209 os.makedirs(test_private_dir, exist_ok=True)
210 config_file = os.path.join(test_private_dir,
211 test_name + '.config')
212 with open(config_file, 'w') as f:
213 f.write(cls._format_config_template(extra_data,
214 '\n'.join(defs['config']) + '\n', test_name))
217 for scenario in defs.get('scenarios', []):
218 if isinstance(scenario, str):
219 # Path to a scenario file
220 scenarios.append(scenario)
222 # Dictionary defining a new scenario in-line
223 scenario_name = scenario_file = scenario['name']
224 actions = scenario.get('actions')
226 os.makedirs(test_private_dir, exist_ok=True)
227 scenario_file = os.path.join(
228 test_private_dir, scenario_name + '.scenario')
229 with open(scenario_file, 'w') as f:
230 f.write('\n'.join(action % extra_data for action in actions) + '\n')
231 scenarios.append(scenario_file)
232 tests_definition['extra_data'] = {'scenarios': scenarios, 'config_file': config_file}
233 tests_definition['pipeline_data'] = {"config_path": os.path.dirname(json_file)}
234 pipelines_descriptions.append(tests_definition)
236 return GstValidatePipelineTestsGenerator(name, test_manager, pipelines_descriptions=pipelines_descriptions)
239 def _format_config_template(cls, extra_data, config_text, test_name):
240 # Variables available for interpolation inside config blocks.
242 extra_vars = extra_data.copy()
244 if 'validate-flow-expectations-dir' in extra_vars and \
245 'validate-flow-actual-results-dir' in extra_vars:
246 expectations_dir = os.path.join(extra_vars['validate-flow-expectations-dir'],
247 test_name.replace('.', os.sep))
248 actual_results_dir = os.path.join(extra_vars['validate-flow-actual-results-dir'],
249 test_name.replace('.', os.sep))
250 extra_vars['validateflow'] = "validateflow, expectations-dir=\"%s\", actual-results-dir=\"%s\"" % (expectations_dir, actual_results_dir)
252 return config_text % extra_vars
254 def get_fname(self, scenario, protocol=None, name=None):
258 if protocol is not None:
259 protocol_str = "%s." % protocol
263 if scenario is not None and scenario.name.lower() != "none":
264 return "%s%s.%s" % (protocol_str, name, scenario.name)
266 return ("%s.%s.%s" % (protocol_str, self.name, name)).replace("..", ".")
268 def generate_tests(self, uri_minfo_special_scenarios, scenarios):
269 if self._valid_scenarios is None:
271 elif self._valid_scenarios:
272 scenarios = [scenario for scenario in scenarios if
273 scenario is not None and scenario.name in self._valid_scenarios]
275 return super(GstValidatePipelineTestsGenerator, self).generate_tests(
276 uri_minfo_special_scenarios, scenarios)
278 def populate_tests(self, uri_minfo_special_scenarios, scenarios):
279 for description in self._pipelines_descriptions:
280 pipeline = description['pipeline']
281 extra_data = description.get('extra_data', {})
282 pipeline_data = description.get('pipeline_data', {})
284 if 'scenarios' in extra_data:
285 # A pipeline description can override the default scenario set.
286 # The pipeline description may specify an empty list of
287 # scenarios, in which case one test will be generated with no
289 scenarios_to_iterate = extra_data['scenarios'] or [None]
291 scenarios_to_iterate = scenarios
293 for scenario in scenarios_to_iterate:
294 if isinstance(scenario, str):
295 scenario = self.test_manager.scenarios_manager.get_scenario(
298 mediainfo = FakeMediaDescriptor(extra_data, pipeline)
299 if not mediainfo.is_compatible(scenario):
302 if self.test_manager.options.mute:
303 needs_clock = scenario.needs_clock_sync() \
304 if scenario else False
305 audiosink = self.get_fakesink_for_media_type(
306 "audio", needs_clock)
307 videosink = self.get_fakesink_for_media_type(
308 "video", needs_clock)
310 audiosink = 'autoaudiosink'
311 videosink = 'autovideosink'
313 pipeline_data.update({'videosink': videosink, 'audiosink': audiosink})
314 pipeline_desc = pipeline % pipeline_data
316 fname = self.get_fname(
317 scenario, protocol=mediainfo.get_protocol(), name=description["name"])
319 expected_failures = extra_data.get("expected-failures")
320 extra_env_vars = extra_data.get("extra_env_vars")
321 test = GstValidateLaunchTest(fname,
322 self.test_manager.options,
323 self.test_manager.reporter,
326 media_descriptor=mediainfo,
327 expected_failures=expected_failures,
328 extra_env_variables=extra_env_vars)
329 if extra_data.get('config_file'):
330 test.add_validate_config(extra_data['config_file'])
334 class GstValidatePlaybinTestsGenerator(GstValidatePipelineTestsGenerator):
336 def __init__(self, test_manager):
337 if os.getenv("USE_PLAYBIN3") is None:
338 GstValidatePipelineTestsGenerator.__init__(
339 self, "playback", test_manager, "playbin")
341 GstValidatePipelineTestsGenerator.__init__(
342 self, "playback", test_manager, "playbin3")
344 def _set_sinks(self, minfo, pipe_str, scenario):
345 if self.test_manager.options.mute:
346 needs_clock = scenario.needs_clock_sync() or minfo.media_descriptor.need_clock_sync()
348 afakesink = self.get_fakesink_for_media_type("audio", needs_clock)
349 vfakesink = self.get_fakesink_for_media_type("video", needs_clock)
350 pipe_str += " audio-sink='%s' video-sink='%s'" % (
351 afakesink, vfakesink)
355 def _get_name(self, scenario, protocol, minfo):
356 return "%s.%s" % (self.get_fname(scenario,
358 os.path.basename(minfo.media_descriptor.get_clean_name()))
360 def populate_tests(self, uri_minfo_special_scenarios, scenarios):
361 test_rtsp = GstValidateBaseTestManager.RTSP_SERVER_COMMAND
363 printc("\n\nRTSP server not available, you should make sure"
364 " that %s is available in your $PATH." % GstValidateBaseTestManager.RTSP_SERVER_COMMAND,
366 elif self.test_manager.options.disable_rtsp:
367 printc("\n\nRTSP tests are disabled")
370 for uri, minfo, special_scenarios in uri_minfo_special_scenarios:
371 pipe = self._pipeline_template
372 protocol = minfo.media_descriptor.get_protocol()
374 if protocol == Protocols.RTSP:
375 self.debug("SKIPPING %s as it is a RTSP stream" % uri)
378 pipe += " uri=%s" % uri
380 for scenario in special_scenarios + scenarios:
382 if not minfo.media_descriptor.is_compatible(scenario):
385 cpipe = self._set_sinks(minfo, cpipe, scenario)
386 fname = self._get_name(scenario, protocol, minfo)
388 self.debug("Adding: %s", fname)
390 if scenario.does_reverse_playback() and protocol == Protocols.HTTP:
391 # 10MB so we can reverse playback
392 cpipe += " ring-buffer-max-size=10485760"
394 self.add_test(GstValidateLaunchTest(fname,
395 self.test_manager.options,
396 self.test_manager.reporter,
399 media_descriptor=minfo.media_descriptor)
402 if test_rtsp and protocol == Protocols.FILE and not minfo.media_descriptor.is_image():
403 rtspminfo = NamedDic({"path": minfo.media_descriptor.get_path(),
404 "media_descriptor": GstValidateRTSPMediaDesciptor(minfo.media_descriptor.get_path())})
405 if not rtspminfo.media_descriptor.is_compatible(scenario):
408 cpipe = self._set_sinks(rtspminfo, "%s uri=rtsp://127.0.0.1:<RTSPPORTNUMBER>/test"
409 % self._pipeline_template, scenario)
410 fname = self._get_name(scenario, Protocols.RTSP, rtspminfo)
412 self.add_test(GstValidateRTSPTest(
413 fname, self.test_manager.options, self.test_manager.reporter,
414 cpipe, uri, scenario=scenario,
415 media_descriptor=rtspminfo.media_descriptor))
417 fname = self._get_name(scenario, Protocols.RTSP + '2', rtspminfo)
418 self.add_test(GstValidateRTSPTest(
419 fname, self.test_manager.options, self.test_manager.reporter,
420 cpipe, uri, scenario=scenario,
421 media_descriptor=rtspminfo.media_descriptor,
425 class GstValidateMixerTestsGenerator(GstValidatePipelineTestsGenerator):
427 def __init__(self, name, test_manager, mixer, media_type, converter="",
428 num_sources=3, mixed_srcs=None, valid_scenarios=None):
429 mixed_srcs = mixed_srcs or {}
430 valid_scenarios = valid_scenarios or []
432 pipe_template = "%(mixer)s name=_mixer ! " + \
433 converter + " ! %(sink)s "
434 self.converter = converter
436 self.media_type = media_type
437 self.num_sources = num_sources
438 self.mixed_srcs = mixed_srcs
440 GstValidateMixerTestsGenerator, self).__init__(name, test_manager, pipe_template,
441 valid_scenarios=valid_scenarios)
443 def populate_tests(self, uri_minfo_special_scenarios, scenarios):
444 if self.test_manager.options.validate_uris:
447 wanted_ressources = []
448 for uri, minfo, special_scenarios in uri_minfo_special_scenarios:
449 protocol = minfo.media_descriptor.get_protocol()
450 if protocol == Protocols.FILE and \
451 minfo.media_descriptor.get_num_tracks(self.media_type) > 0:
452 wanted_ressources.append((uri, minfo))
454 if not self.mixed_srcs:
455 if not wanted_ressources:
458 for i in range(len(uri_minfo_special_scenarios) / self.num_sources):
461 for nsource in range(self.num_sources):
462 uri, minfo = wanted_ressources[i + nsource]
463 if os.getenv("USE_PLAYBIN3") is None:
465 "uridecodebin uri=%s ! %s" % (uri, self.converter))
468 "uridecodebin3 uri=%s ! %s" % (uri, self.converter))
469 fname = os.path.basename(uri).replace(".", "_")
473 name += "+%s" % fname
475 self.mixed_srcs[name] = tuple(srcs)
477 for name, srcs in self.mixed_srcs.items():
478 if isinstance(srcs, dict):
480 "mixer": self.mixer + " %s" % srcs["mixer_props"]}
481 srcs = srcs["sources"]
483 pipe_arguments = {"mixer": self.mixer}
485 for scenario in scenarios:
486 fname = self.get_fname(scenario, Protocols.FILE) + "."
489 self.debug("Adding: %s", fname)
491 if self.test_manager.options.mute:
492 pipe_arguments["sink"] = self.get_fakesink_for_media_type(self.media_type,
493 scenario.needs_clock_sync())
495 pipe_arguments["sink"] = "auto%ssink" % self.media_type
497 pipe = self._pipeline_template % pipe_arguments
500 pipe += "%s ! _mixer. " % src
502 self.add_test(GstValidateLaunchTest(fname,
503 self.test_manager.options,
504 self.test_manager.reporter,
510 class GstValidateLaunchTest(GstValidateTest):
512 def __init__(self, classname, options, reporter, pipeline_desc,
513 timeout=DEFAULT_TIMEOUT, scenario=None,
514 media_descriptor=None, duration=0, hard_timeout=None,
515 extra_env_variables=None, expected_failures=None):
517 extra_env_variables = extra_env_variables or {}
520 duration = scenario.get_duration()
521 elif media_descriptor:
522 duration = media_descriptor.get_duration() / GST_SECOND
525 GstValidateLaunchTest, self).__init__(GstValidateBaseTestManager.COMMAND,
531 hard_timeout=hard_timeout,
532 media_descriptor=media_descriptor,
533 extra_env_variables=extra_env_variables,
534 expected_failures=expected_failures)
536 self.pipeline_desc = pipeline_desc
537 self.media_descriptor = media_descriptor
539 def build_arguments(self):
540 GstValidateTest.build_arguments(self)
541 self.add_arguments(*shlex.split(self.pipeline_desc))
542 if self.media_descriptor is not None and self.media_descriptor.get_path():
544 "--set-media-info", self.media_descriptor.get_path())
547 class GstValidateMediaCheckTest(GstValidateTest):
549 def __init__(self, classname, options, reporter, media_descriptor,
550 uri, minfo_path, timeout=DEFAULT_TIMEOUT,
551 extra_env_variables=None,
552 expected_failures=None):
553 extra_env_variables = extra_env_variables or {}
556 GstValidateMediaCheckTest, self).__init__(GstValidateBaseTestManager.MEDIA_CHECK_COMMAND, classname,
559 media_descriptor=media_descriptor,
560 extra_env_variables=extra_env_variables,
561 expected_failures=expected_failures)
563 self._media_info_path = minfo_path
565 def build_arguments(self):
566 Test.build_arguments(self)
567 self.add_arguments(self._uri, "--expected-results",
568 self._media_info_path)
570 if self.media_descriptor.skip_parsers():
571 self.add_arguments("--skip-parsers")
574 class GstValidateTranscodingTest(GstValidateTest, GstValidateEncodingTestInterface):
575 scenarios_manager = ScenarioManager()
577 def __init__(self, classname, options, reporter,
578 combination, uri, media_descriptor,
579 timeout=DEFAULT_TIMEOUT,
581 extra_env_variables=None,
582 expected_failures=None):
583 Loggable.__init__(self)
585 extra_env_variables = extra_env_variables or {}
587 file_dur = int(media_descriptor.get_duration()) / GST_SECOND
588 if not media_descriptor.get_num_tracks("video"):
589 self.debug("%s audio only file applying transcoding ratio."
590 "File 'duration' : %s" % (classname, file_dur))
591 duration = file_dur / AUDIO_ONLY_FILE_TRANSCODING_RATIO
596 GstValidateTranscodingTest, self).__init__(GstValidateBaseTestManager.TRANSCODING_COMMAND,
603 media_descriptor=media_descriptor,
604 extra_env_variables=None,
605 expected_failures=expected_failures)
606 extra_env_variables = extra_env_variables or {}
608 GstValidateEncodingTestInterface.__init__(
609 self, combination, media_descriptor)
613 def run_external_checks(self):
614 if self.media_descriptor.get_num_tracks("video") == 1 and \
615 self.options.validate_enable_iqa_tests:
616 self.run_iqa_test(self.uri)
618 def set_rendering_info(self):
619 self.dest_file = os.path.join(self.options.dest,
620 self.classname.replace(".transcode.", os.sep).
621 replace(".", os.sep))
622 mkdir(os.path.dirname(urllib.parse.urlsplit(self.dest_file).path))
623 if urllib.parse.urlparse(self.dest_file).scheme == "":
624 self.dest_file = path2url(self.dest_file)
626 profile = self.get_profile()
627 self.add_arguments("-o", profile)
629 def build_arguments(self):
630 GstValidateTest.build_arguments(self)
631 self.set_rendering_info()
632 self.add_arguments(self.uri, self.dest_file)
634 def get_current_value(self):
636 sent_eos = self.sent_eos_position()
637 if sent_eos is not None:
639 if ((t - sent_eos)) > 30:
640 if self.media_descriptor.get_protocol() == Protocols.HLS:
641 self.set_result(Result.PASSED,
642 """Got no EOS 30 seconds after sending EOS,
643 in HLS known and tolerated issue:
644 https://bugzilla.gnome.org/show_bug.cgi?id=723868""")
645 return Result.KNOWN_ERROR
648 Result.FAILED, "Pipeline did not stop 30 Seconds after sending EOS")
652 size = self.get_current_size()
654 return self.get_current_position()
658 def check_results(self):
659 if self.result in [Result.FAILED, Result.TIMEOUT] or \
660 self.process.returncode != 0:
661 GstValidateTest.check_results(self)
664 res, msg = self.check_encoded_file()
665 self.set_result(res, msg)
668 class GstValidateBaseRTSPTest:
669 """ Interface for RTSP tests, requires implementing Test"""
672 def __init__(self, local_uri):
673 self._local_uri = local_uri
674 self.rtsp_server = None
675 self._unsetport_pipeline_desc = None
679 def __get_open_port(cls):
682 # http://stackoverflow.com/questions/2838244/get-open-tcp-port-in-python?answertab=votes#tab-top
683 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
685 port = s.getsockname()[1]
686 if port not in cls.__used_ports:
687 cls.__used_ports.add(port)
693 def launch_server(self):
694 if self.options.redirect_logs == 'stdout':
695 self.rtspserver_logs = sys.stdout
696 elif self.options.redirect_logs == 'stderr':
697 self.rtspserver_logs = sys.stderr
699 self.server_port = self.__get_open_port()
700 command = [GstValidateBaseTestManager.RTSP_SERVER_COMMAND, self._local_uri, '--port', str(self.server_port)]
702 if self.options.validate_gdb_server:
703 command = self.use_gdb(command)
704 self.rtspserver_logs = sys.stdout
705 elif self.options.redirect_logs:
706 self.rtspserver_logs = sys.stdout
708 self.rtspserver_logs = open(self.logfile + '_rtspserver.log', 'w+')
709 self.extra_logfiles.append(self.rtspserver_logs.name)
711 server_env = os.environ.copy()
713 self.rtsp_server = subprocess.Popen(command,
714 stderr=self.rtspserver_logs,
715 stdout=self.rtspserver_logs,
720 s.connect((("127.0.0.1", self.server_port)))
722 except ConnectionRefusedError:
728 if not self._unsetport_pipeline_desc:
729 self._unsetport_pipeline_desc = self.pipeline_desc
731 self.pipeline_desc = self._unsetport_pipeline_desc.replace(
732 "<RTSPPORTNUMBER>", str(self.server_port))
734 return ' '.join(command)
736 def close_logfile(self):
737 super().close_logfile()
738 if not self.options.redirect_logs:
739 self.rtspserver_logs.close()
741 def process_update(self):
742 res = super().process_update()
744 kill_subprocess(self, self.rtsp_server, DEFAULT_TIMEOUT)
745 self.__used_ports.remove(self.server_port)
750 class GstValidateRTSPTest(GstValidateBaseRTSPTest, GstValidateLaunchTest):
752 def __init__(self, classname, options, reporter, pipeline_desc,
753 local_uri, timeout=DEFAULT_TIMEOUT, scenario=None,
754 media_descriptor=None, rtsp2=False):
755 GstValidateLaunchTest.__init__(self, classname, options, reporter,
756 pipeline_desc, timeout, scenario,
758 GstValidateBaseRTSPTest.__init__(self, local_uri)
761 def get_subproc_env(self):
762 env = super().get_subproc_env()
763 path = env.get('GST_VALIDATE_SCENARIOS_PATH', '')
764 override_dir = get_data_file(os.path.join('data', 'scenarios'), 'rtsp_overrides')
765 env['GST_VALIDATE_SCENARIOS_PATH'] = '%s:%s' % (override_dir, path)
767 env['GST_VALIDATE_SCENARIO'] = env.get('GST_VALIDATE_SCENARIO', '') + ':' + 'force_rtsp2'
772 class GstValidateRTSPMediaDesciptor(GstValidateMediaDescriptor):
774 def __init__(self, xml_path):
775 GstValidateMediaDescriptor.__init__(self, xml_path)
778 return "rtsp://127.0.0.1:8554/test"
780 def get_protocol(self):
781 return Protocols.RTSP
787 class GstValidateTestManager(GstValidateBaseTestManager):
791 # List of all classes to create testsuites
792 GstValidateMediaCheckTestsGenerator = GstValidateMediaCheckTestsGenerator
793 GstValidateTranscodingTestsGenerator = GstValidateTranscodingTestsGenerator
794 GstValidatePipelineTestsGenerator = GstValidatePipelineTestsGenerator
795 GstValidatePlaybinTestsGenerator = GstValidatePlaybinTestsGenerator
796 GstValidateMixerTestsGenerator = GstValidateMixerTestsGenerator
797 GstValidateLaunchTest = GstValidateLaunchTest
798 GstValidateMediaCheckTest = GstValidateMediaCheckTest
799 GstValidateTranscodingTest = GstValidateTranscodingTest
802 super(GstValidateTestManager, self).__init__()
804 self._run_defaults = True
805 self._is_populated = False
806 self._default_generators_registered = False
809 for command, name in [
810 (GstValidateBaseTestManager.TRANSCODING_COMMAND, "gst-validate-transcoding-1.0"),
811 (GstValidateBaseTestManager.COMMAND, "gst-validate-1.0"),
812 (GstValidateBaseTestManager.MEDIA_CHECK_COMMAND, "gst-validate-media-check-1.0")]:
814 self.error("command not found: %s" % name)
819 def add_options(self, parser):
820 group = parser.add_argument_group("GstValidate tools specific options"
822 description="""When using --wanted-tests, all the scenarios can be used, even those which have
823 not been tested and explicitely activated if you set use --wanted-tests ALL""")
824 group.add_argument("--validate-check-uri", dest="validate_uris",
825 action="append", help="defines the uris to run default tests on")
826 group.add_argument("--validate-tools-path", dest="validate_tools_path",
827 action="append", help="defines the paths to look for GstValidate tools.")
828 group.add_argument("--validate-gdb-server", dest="validate_gdb_server",
829 help="Run the server in GDB.")
830 group.add_argument("--validate-disable-rtsp", dest="disable_rtsp",
831 help="Disable RTSP tests.")
832 group.add_argument("--validate-enable-iqa-tests", dest="validate_enable_iqa_tests",
833 help="Enable Image Quality Assessment validation tests.",
834 default=False, action='store_true')
836 def print_valgrind_bugs(self):
837 # Look for all the 'pending' bugs in our supp file
839 p = get_data_file('data', 'gstvalidate.supp')
841 for line in f.readlines():
843 if line.startswith('# PENDING:'):
844 tmp = line.split(' ')
848 msg = "Ignored valgrind bugs:\n"
851 printc(msg, Colors.FAIL, True)
853 def populate_testsuite(self):
855 if self._is_populated is True:
858 if not self.options.config and not self.options.testsuites:
859 if self._run_defaults:
860 self.register_defaults()
864 self._is_populated = True
866 def list_tests(self):
870 if self._run_defaults:
871 scenarios = [self.scenarios_manager.get_scenario(scenario_name)
872 for scenario_name in self.get_scenarios()]
874 scenarios = self.scenarios_manager.get_scenario(None)
875 uris = self._list_uris()
877 for generator in self.get_generators():
878 for test in generator.generate_tests(uris, scenarios):
881 if not self.tests and not uris and not self.options.wanted_tests:
883 "No valid uris present in the path. Check if media files and info files exist", Colors.FAIL)
887 def _add_media(self, media_info, uri=None):
888 self.debug("Checking %s", media_info)
889 if isinstance(media_info, GstValidateMediaDescriptor):
890 media_descriptor = media_info
891 media_info = media_descriptor.get_path()
893 media_descriptor = GstValidateMediaDescriptor(media_info)
896 # Just testing that the vairous mandatory infos are present
897 caps = media_descriptor.get_caps()
899 uri = media_descriptor.get_uri()
901 # Adjust local http uri
902 if self.options.http_server_port != 8079 and \
903 uri.startswith("http://127.0.0.1:8079/"):
904 uri = uri.replace("http://127.0.0.1:8079/",
905 "http://127.0.0.1:%r/" % self.options.http_server_port, 1)
906 media_descriptor.set_protocol(urllib.parse.urlparse(uri).scheme)
907 for caps2, prot in GST_VALIDATE_CAPS_TO_PROTOCOL:
909 media_descriptor.set_protocol(prot)
912 scenario_bname = media_descriptor.get_media_filepath()
913 special_scenarios = self.scenarios_manager.find_special_scenarios(
915 self._uris.append((uri,
916 NamedDic({"path": media_info,
917 "media_descriptor": media_descriptor}),
919 except configparser.NoOptionError as e:
920 self.debug("Exception: %s for %s", e, media_info)
922 def _discover_file(self, uri, fpath):
923 for ext in (GstValidateMediaDescriptor.MEDIA_INFO_EXT,
924 GstValidateMediaDescriptor.PUSH_MEDIA_INFO_EXT):
927 media_info = "%s.%s" % (fpath, ext)
928 if ext == GstValidateMediaDescriptor.PUSH_MEDIA_INFO_EXT:
929 if not os.path.exists(media_info):
933 args = GstValidateBaseTestManager.MEDIA_CHECK_COMMAND.split(" ")
936 if os.path.isfile(media_info) and not self.options.update_media_info:
937 self._add_media(media_info, uri)
939 elif fpath.endswith(GstValidateMediaDescriptor.STREAM_INFO_EXT):
940 self._add_media(fpath)
942 elif not self.options.generate_info and not self.options.update_media_info and not self.options.validate_uris:
944 elif self.options.update_media_info and not os.path.isfile(media_info):
946 "%s not present. Use --generate-media-info", media_info)
948 elif os.path.islink(media_info):
950 "%s is a symlink, not updating and hopefully the actual file gets updated!", media_info)
954 if self.options.update_media_info:
956 elif self.options.generate_info_full:
959 media_descriptor = GstValidateMediaDescriptor.new_from_uri(
960 uri, True, include_frames, is_push)
962 self._add_media(media_descriptor, uri)
964 self.warning("Could not get any descriptor for %s" % uri)
966 except subprocess.CalledProcessError as e:
967 if self.options.generate_info:
968 printc("Result: Failed", Colors.FAIL)
970 self.error("Exception: %s", e)
974 def _list_uris(self):
978 if self.options.validate_uris:
979 for uri in self.options.validate_uris:
980 self._discover_file(uri, uri)
984 if isinstance(self.options.paths, str):
985 self.options.paths = [os.path.join(self.options.paths)]
987 for path in self.options.paths:
988 if os.path.isfile(path):
989 path = os.path.abspath(path)
990 self._discover_file(path2url(path), path)
992 for root, dirs, files in os.walk(path):
994 fpath = os.path.abspath(os.path.join(root, f))
995 if os.path.isdir(fpath) or \
996 fpath.endswith(GstValidateMediaDescriptor.MEDIA_INFO_EXT) or\
997 fpath.endswith(ScenarioManager.FILE_EXTENSION):
1000 self._discover_file(path2url(fpath), fpath)
1002 self.debug("Uris found: %s", self._uris)
1006 def needs_http_server(self):
1007 for test in self.list_tests():
1008 if self._is_test_wanted(test) and test.media_descriptor is not None:
1009 protocol = test.media_descriptor.get_protocol()
1010 uri = test.media_descriptor.get_uri()
1012 if protocol in [Protocols.HTTP, Protocols.HLS, Protocols.DASH] and \
1014 self.options.http_server_port) in uri or "127.0.0.1:8079" in uri):
1018 def set_settings(self, options, args, reporter):
1019 if options.wanted_tests:
1020 for i in range(len(options.wanted_tests)):
1021 if "ALL" in options.wanted_tests[i]:
1022 self._run_defaults = False
1023 options.wanted_tests[
1024 i] = options.wanted_tests[i].replace("ALL", "")
1026 options.wanted_tests.remove("")
1030 if options.validate_uris:
1031 self.check_testslist = False
1033 super(GstValidateTestManager, self).set_settings(
1034 options, args, reporter)
1036 def register_defaults(self):
1038 Registers the defaults:
1039 * Scenarios to be used
1040 * Encoding formats to be used
1044 self.register_default_scenarios()
1045 self.register_default_encoding_formats()
1046 self.register_default_blacklist()
1047 self.register_default_test_generators()
1049 def register_default_scenarios(self):
1051 Registers default test scenarios
1053 if self.options.long_limit != 0:
1054 self.add_scenarios([
1061 "switch_audio_track",
1062 "switch_audio_track_while_paused",
1063 "switch_subtitle_track",
1064 "switch_subtitle_track_while_paused",
1065 "disable_subtitle_track_while_paused",
1066 "change_state_intensive",
1067 "scrub_forward_seeking"])
1069 self.add_scenarios([
1076 "switch_audio_track",
1077 "switch_audio_track_while_paused",
1078 "switch_subtitle_track",
1079 "switch_subtitle_track_while_paused",
1080 "disable_subtitle_track_while_paused",
1081 "change_state_intensive",
1082 "scrub_forward_seeking"])
1084 def register_default_encoding_formats(self):
1086 Registers default encoding formats
1088 self.add_encoding_formats([
1089 MediaFormatCombination("ogg", "vorbis", "theora"),
1090 MediaFormatCombination("webm", "vorbis", "vp8"),
1091 MediaFormatCombination("mp4", "mp3", "h264"),
1092 MediaFormatCombination("mkv", "vorbis", "h264"),
1095 def register_default_blacklist(self):
1096 self.set_default_blacklist([
1098 # ("hls.playback.seek_with_stop.*",
1099 # "https://bugzilla.gnome.org/show_bug.cgi?id=753689"),
1101 # testbin known issues
1102 ("testbin.media_check.*",
1103 "Not supported by GstDiscoverer."),
1106 ("dash.media_check.*",
1107 "Caps are different depending on selected bitrates, etc"),
1109 # Matroska/WEBM known issues:
1110 ("*.reverse_playback.*webm$",
1111 "https://bugzilla.gnome.org/show_bug.cgi?id=679250"),
1112 ("*.reverse_playback.*mkv$",
1113 "https://bugzilla.gnome.org/show_bug.cgi?id=679250"),
1114 ("http.playback.seek_with_stop.*webm",
1115 "matroskademux.gst_matroska_demux_handle_seek_push: Seek end-time not supported in streaming mode"),
1116 ("http.playback.seek_with_stop.*mkv",
1117 "matroskademux.gst_matroska_demux_handle_seek_push: Seek end-time not supported in streaming mode"),
1119 # MPEG TS known issues:
1120 ('(?i)*playback.reverse_playback.*(?:_|.)(?:|m)ts$',
1121 "https://bugzilla.gnome.org/show_bug.cgi?id=702595"),
1123 # Fragmented MP4 disabled tests:
1124 ('*.playback..*seek.*.fragmented_nonseekable_sink_mp4',
1125 "Seeking on fragmented files without indexes isn't implemented"),
1126 ('*.playback.reverse_playback.fragmented_nonseekable_sink_mp4',
1127 "Seeking on fragmented files without indexes isn't implemented"),
1129 # HTTP known issues:
1130 ("http.*scrub_forward_seeking.*",
1131 "This is not stable enough for now."),
1132 ("http.playback.change_state_intensive.raw_video_mov",
1133 "This is not stable enough for now. (flow return from pad push doesn't match expected value)"),
1136 ("*reverse_playback.*mxf",
1137 "Reverse playback is not handled in MXF"),
1138 ("file\.transcode.*mxf",
1139 "FIXME: Transcoding and mixing tests need to be tested"),
1142 ("*reverse_playback.*wmv",
1143 "Reverse playback is not handled in wmv"),
1144 (".*reverse_playback.*asf",
1145 "Reverse playback is not handled in asf"),
1148 ("http.playback.seek.*vorbis_theora_1_ogg",
1149 "https://bugzilla.gnome.org/show_bug.cgi?id=769545"),
1151 ('rtsp.*playback.reverse.*',
1152 'https://bugzilla.gnome.org/show_bug.cgi?id=626811'),
1153 ('rtsp.*playback.seek_with_stop.*',
1154 'https://bugzilla.gnome.org/show_bug.cgi?id=784298'),
1155 ('rtsp.*playback.fast_*',
1156 'https://bugzilla.gnome.org/show_bug.cgi?id=754575'),
1159 def register_default_test_generators(self):
1161 Registers default test generators
1163 if self._default_generators_registered:
1166 self.add_generators([GstValidatePlaybinTestsGenerator(self),
1167 GstValidateMediaCheckTestsGenerator(self),
1168 GstValidateTranscodingTestsGenerator(self)])
1169 self._default_generators_registered = True