gst-validate-launcher: update documentation
[platform/upstream/gstreamer.git] / validate / launcher / main.py
1 #!/usr/bin/env python3
2 #
3 # Copyright (c) 2014,Thibault Saunier <thibault.saunier@collabora.com>
4 #
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.
9 #
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.
14 #
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.
19 import os
20 import sys
21 from . import utils
22 import urllib.parse
23 from . import loggable
24 import multiprocessing
25 import argparse
26 import tempfile
27 from . import reporters
28 import subprocess
29
30
31 from .loggable import Loggable
32 from .baseclasses import _TestsLauncher, ScenarioManager
33 from .utils import printc, path2url, DEFAULT_MAIN_DIR, launch_command, Colors, Protocols, which
34
35
36 LESS = "less"
37 HELP = '''
38
39 ===============================================================================
40                        gst-validate-launcher
41 ===============================================================================
42
43 1. Introduction
44 ----------------
45
46 gst-validate-launcher is a test launcher tool. It has been designed to
47 launch the various tools included in GstValidate, running tests on real
48 media files. This means that with gst-validate-launcher, you can launch
49 many tests automatically in one simple command. It then permits to
50 aggregate results and print them in a human readable way on stdout
51 and serializing them in the following implemented formats:
52
53  * %s
54
55 We support all the tools provided in GstValidate in the launcher, but
56 we also support ges-launch when the GStreamer Editing Services have
57 been compiled against GstValidate.
58
59 2. Default test suite
60 ---------------------
61
62 A default suite of tests is provided and is available at: http://gitlab.freedesktop.org/gstreamer/gst-integration-testsuites/
63 You can run it pretty simply doing:
64
65 .    $gst-validate-launcher --sync
66
67 That will download Gstreamer upstream default assets into the
68 default folder (%s) and run all currently
69 activated tests. Note that we use git-annex https://git-annex.branchable.com/ so
70 you will need that tool to get started.
71
72 3. Implement your own tests
73 ---------------------------
74
75 To implement new tests, you will just need to set the media path using the
76 --medias-paths argument. If you want to run all available scenarios on all the
77 file present in that folder, you should run the first time:
78
79 .    $gst-validate-launcher --medias-paths /path/to/media/files --generate-media-info
80
81 That will generate the .media_info files that contains information about the media
82 files present in that folder. Those media_info files are simple XML file describing
83 the topology of the media files. You need not reuse --generate-media-info from
84 next time. The generated media files will be used as a reference for following
85 runs. You might want to check that they contain the right information yourself
86 the first time.
87
88 Once .media-info is generated, you can update it using --update-media-info.
89
90 Those .media_info are the files that are used by gst-validate-launcher to know
91 what media files can be used for the different scenarios. For example if a
92 file is not seekable, seeking scenarios will not be run on it etc...
93
94 3.1 Scenarios specific to a media file/stream:
95 ----------------------------------------------
96
97 It is possible that some scenarios are very specific to one media file. In that case,
98 the .scenario file should be present in the same folder as the .media_info file and
99 be called similarly. For example for a file called /some/media/file.mp4, the media_info
100 file will be called /some/media/file.media_info and a scenario that will seek to a position that
101 is known to fail would be called: /some/media/file.mp4.seek_to_failing_pos.scenario and
102 gst-validate-launcher will run that scenario only on that media file.
103
104 3.2 Test media accessible through other protocols:
105 --------------------------------------------------
106
107 Currently gst-validate-launcher supports the following protocols:
108
109   * %s
110
111 It does not mean you can not test other protocols but it means that it has not been
112 properly tested.
113
114 To test medias that use those protocols, you should simply make sure that there
115 is a media descriptor file with .stream_info as an extension in your --media-paths.
116 You can generate such a file doing:
117
118 .   $gst-validate-media-check-1.0 http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8 --output-file /somewhere/in/you/media/path/bipbop.stream_info
119
120 Once this is done, gst-validate-launcher will run the scenarios on those media files the
121 same way as if they were local files.
122
123
124 4. Debug gst-validate-launcher execution
125 ----------------------------------------
126
127 You can activate debug logs setting the environment variable GST_VALIDATE_LAUNCHER_DEBUG.
128
129 .   $GST_VALIDATE_LAUNCHER_DEBUG=6 gst-validate-launcher
130
131 It uses the same syntax as PITIVI_DEBUG (more information at:
132 https://developer.pitivi.org/Bug_reporting.html#debug-logs).
133 ''' % ("\n  * ".join([reporter.name for reporter in
134                       utils.get_subclasses(reporters.Reporter, reporters.__dict__)]
135                      ),
136        DEFAULT_MAIN_DIR,
137        "\n  * ".join([getattr(Protocols, att) for att in
138                       dir(Protocols) if isinstance(getattr(Protocols, att), str) and not
139                       att.startswith("_")]))
140
141 if "--help" not in sys.argv:
142     HELP = "Use --help for the full help"
143
144 QA_ASSETS = "gst-integration-testsuites"
145 MEDIAS_FOLDER = "medias"
146 DEFAULT_GST_QA_ASSETS_REPO = "https://gitlab.freedesktop.org/gstreamer/gst-integration-testsuites.git"
147
148
149 def download_assets(options):
150     try:
151         printc("About to download assets from %s to %s" % options.remote_assets_url,
152                options.clone_dir)
153         launch_command("%s %s %s" % (options.get_assets_command,
154                                      options.remote_assets_url,
155                                      options.clone_dir),
156                        fails=True)
157     except subprocess.CalledProcessError as e:
158         if "git" in options.get_assets_command:
159             m = "\n\nMAKE SURE YOU HAVE git INSTALLED!"
160         else:
161             m = ""
162
163         printc("Could not download assets\n\nError: %s%s" % (e, m),
164                Colors.FAIL, True)
165
166         return False
167
168     return True
169
170
171 class PrintUsage(argparse.Action):
172
173     def __init__(self, option_strings, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, help=None):
174         super(
175             PrintUsage, self).__init__(option_strings=option_strings, dest=dest,
176                                        default=default, nargs=0, help=help)
177
178     def __call__(self, parser, namespace, values, option_string=None):
179         print(HELP)
180         parser.exit()
181
182
183 class LauncherConfig(Loggable):
184
185     def __init__(self):
186         self.testsuites = []
187         self.debug = False
188         self.forever = False
189         self.n_runs = None
190         self.fatal_error = False
191         self.wanted_tests = []
192         self.blacklisted_tests = []
193         self.list_tests = False
194         self.mute = True
195         self.unmute = not self.mute
196         self.no_color = False
197         self.generate_info = False
198         self.update_media_info = False
199         self.generate_info_full = False
200         self.long_limit = utils.LONG_TEST
201         self.config = None
202         self.valgrind = False
203         self.gdb = False
204         self.no_display = False
205         self.xunit_file = None
206         self.main_dir = utils.DEFAULT_MAIN_DIR
207         self.output_dir = None
208         self.logsdir = None
209         self.privatedir = None
210         self.redirect_logs = False
211         self.num_jobs = int(multiprocessing.cpu_count() / 2)
212         self.dest = None
213         self._using_default_paths = False
214         # paths passed with --media-path, and not defined by a testsuite
215         self.user_paths = []
216         self.paths = []
217         self.testsuites_dirs = utils.DEFAULT_TESTSUITES_DIRS
218
219         self.clone_dir = None
220
221         self.http_server_port = 8079
222         self.http_bandwith = 1024 * 1024
223         self.http_server_dir = None
224         self.httponly = False
225         self.get_assets_command = "git clone"
226         self.remote_assets_url = DEFAULT_GST_QA_ASSETS_REPO
227         self.sync = False
228         self.force_sync = False
229         self.sync_all = False
230         self.check_bugs_status = False
231         self.retry_on_failures = False
232         self.html = False
233
234     def cleanup(self):
235         """
236         Cleanup the options looking after user options have been parsed
237         """
238
239         # Get absolute path for main_dir and base everything on that
240         self.main_dir = os.path.abspath(self.main_dir)
241         os.environ['GST_VALIDATE_LAUNCHER_MAIN_DIR'] = self.main_dir
242
243         # default for output_dir is MAINDIR
244         if not self.output_dir:
245             self.output_dir = self.main_dir
246         else:
247             self.output_dir = os.path.abspath(self.output_dir)
248
249         self.mute = not self.unmute
250         if self.gdb_non_stop:
251             self.gdb = True
252
253         if self.gdb:
254             self.logsdir = "stdout"
255             self.debug = True
256             self.num_jobs = 1
257             try:
258                 subprocess.check_output("gdb --help", shell=True)
259             except subprocess.CalledProcessError:
260                 printc("Want to use gdb, but not available on the system",
261                        Colors.FAIL)
262                 return False
263
264         # other output directories
265         if self.logsdir in ['stdout', 'stderr']:
266             # Allow -l stdout/stderr to work like -rl stdout/stderr
267             self.redirect_logs = self.logsdir
268             self.logsdir = None
269         if self.verbose:
270             self.redirect_logs = 'stdout'
271             self.logsdir = None
272         if self.logsdir is None:
273             self.logsdir = os.path.join(self.output_dir, "logs")
274         if self.dest is None:
275             self.dest = os.path.join(self.output_dir, "rendered")
276         self.privatedir = os.path.join(self.output_dir, "launcher-private")
277
278         if not os.path.exists(self.dest):
279             os.makedirs(self.dest)
280         if not os.path.exists(self.logsdir):
281             os.makedirs(self.logsdir)
282         if not os.path.exists(self.privatedir):
283             os.makedirs(self.privatedir)
284
285         if self.redirect_logs not in ['stdout', 'stderr', False]:
286             printc("Log redirection (%s) must be either 'stdout' or 'stderr'."
287                    % self.redirect_logs, Colors.FAIL, True)
288             return False
289
290         if urllib.parse.urlparse(self.dest).scheme == "":
291             self.dest = path2url(self.dest)
292
293         if self.no_color:
294             utils.desactivate_colors()
295         if self.clone_dir is None:
296             if not utils.USING_SUBPROJECT:
297                 self.clone_dir = os.path.join(self.main_dir, QA_ASSETS)
298             else:
299                 self.clone_dir = self.main_dir
300
301         if not isinstance(self.paths, list):
302             self.paths = [self.paths]
303
304         if not isinstance(self.user_paths, list):
305             self.user_paths = [self.user_paths]
306
307         self.paths = list(set(self.paths).union(set(self.user_paths)))
308
309         if self.generate_info_full is True:
310             self.generate_info = True
311
312         if not utils.USING_SUBPROJECT:
313             if self.sync_all is True or self.force_sync is True:
314                 self.sync = True
315
316             if not self.sync and not os.path.exists(self.clone_dir) and \
317                     self.clone_dir == os.path.join(self.clone_dir, MEDIAS_FOLDER):
318                 printc("Media path (%s) does not exists. Forgot to run --sync ?"
319                        % self.clone_dir, Colors.FAIL, True)
320                 return False
321
322         if (self.main_dir != DEFAULT_MAIN_DIR or self.clone_dir != QA_ASSETS):
323             local_clone_dir = os.path.join(
324                 self.main_dir, self.clone_dir, "testsuites")
325             if local_clone_dir not in self.testsuites_dirs:
326                 self.testsuites_dirs.insert(0, local_clone_dir)
327         if self.valgrind:
328             try:
329                 subprocess.check_output("valgrind --help", shell=True)
330             except subprocess.CalledProcessError:
331                 printc("Want to use valgrind, but not available on the system",
332                        Colors.FAIL)
333                 return False
334
335         if self.html:
336             try:
337                 import commonmark
338             except ImportError:
339                 printc("You want to output html logs but commonmark not found. Install it"
340                        " with `pip install commonmark` and try again.", Colors.FAIL)
341                 return False
342
343         return True
344
345     def set_http_server_dir(self, path):
346         if self.http_server_dir is not None:
347             printc("Server directory already set to %s" % self.http_server_dir)
348             return
349
350         self.http_server_dir = path
351
352     def add_paths(self, paths, force=False):
353         if force is False:
354             if self.paths:
355                 return
356         if not isinstance(paths, list):
357             paths = [paths]
358
359         if self._using_default_paths:
360             self.paths = paths
361             self._using_default_paths = False
362         else:
363             for path in paths:
364                 if path not in self.paths:
365                     self.paths.append(path)
366
367     @staticmethod
368     def create_parser():
369         parser = argparse.ArgumentParser(
370             formatter_class=argparse.RawTextHelpFormatter,
371             prog='gst-validate-launcher', description=HELP)
372
373         parser.add_argument('testsuites', metavar='N', nargs='*',
374                             help="""Lets you specify a test to run, a testsuite name or a file where the testsuite to execute is defined.
375
376     In the module if you want to work with a specific test manager(s) (for example,
377     'ges' or 'validate'), you should define the TEST_MANAGER variable in the
378     testsuite file (it can be a list of test manager names)
379
380     In this file you should implement a setup_tests function. That function takes
381     a TestManager and the GstValidateLauncher option as parameters and return True
382     if it succeeded loading the tests, False otherwise.
383     You will be able to configure the TestManager with its various methods. This
384     function will be called with each TestManager usable, for example you will be
385     passed the 'validate' TestManager in case the GstValidateManager launcher is
386     available. You should configure it using:
387
388     * test_manager.add_scenarios: which allows you to register a list of scenario names to be run
389     * test_manager.set_default_blacklist: Lets you set a list of tuple of the form:
390             (@regex_defining_blacklister_test_names, @reason_for_the_blacklisting)
391     * test_manager.add_generators: which allows you to register a list of #GstValidateTestsGenerator
392         to be used to generate tests
393     * test_manager.add_encoding_formats:: which allows you to register a list #MediaFormatCombination to be used for transcoding tests
394
395     You can also set default values with:
396         * test_manager.register_defaults: Sets default values for all parametters
397         * test_manager.register_default_test_generators: Sets default values for the TestsGenerators to be used
398         * test_manager.register_default_scenarios: Sets default values for the scenarios to be executed
399         * test_manager.register_default_encoding_formats: Sets default values for the encoding formats to be tested
400
401     Note that all testsuite should be inside python modules, so the directory should contain a __init__.py file
402     """,
403                             default=["validate"])
404         parser.add_argument("-d", "--debug", dest="debug",
405                             action="store_true",
406                             help="Let user debug the process on timeout")
407         parser.add_argument("--timeout-factor", dest="timeout_factor",
408                             default=1.0, type=float,
409                             help="Factor to be applied on all timeout values.")
410         parser.add_argument("-f", "--forever", dest="forever",
411                             action="store_true",
412                             help="Keep running tests until one fails")
413         parser.add_argument("--n-runs", dest="n_runs", action='store',
414                             help="Number of runs, if the testsuites."
415                             " Meaning no failure will stop the testuite"
416                             " run meanwhile.", type=int),
417         parser.add_argument("-F", "--fatal-error", dest="fatal_error",
418                             action="store_true",
419                             help="Stop on first fail")
420         parser.add_argument("--fail-on-testlist-change",
421                             dest="fail_on_testlist_change",
422                             action="store_true",
423                             help="Fail the testsuite if a test has been added"
424                             " or removed without being explicitely added/removed "
425                             "from the testlist file.")
426         parser.add_argument("-t", "--wanted-tests", dest="wanted_tests",
427                             action="append",
428                             help="Define the tests to execute, it can be a regex."
429                             " If it contains defaults_only, only default scenarios"
430                             " will be executed")
431         parser.add_argument("-b", "--blacklisted-tests", dest="blacklisted_tests",
432                             action="append",
433                             help="Define the tests not to execute, it can be a regex.")
434         parser.add_argument("--check-bugs", dest="check_bugs_status",
435                             action="store_true",
436                             help="Check if the bug linked to blacklisted tests has"
437                             " been marked as resolved. (works with gitlab and bugzilla)")
438         parser.add_argument("-L", "--list-tests",
439                             dest="list_tests",
440                             action="store_true",
441                             help="List tests and exit")
442         parser.add_argument("--unmute", dest="unmute",
443                             action="store_true",
444                             help="Unmute playback output, which means that we use "
445                             "'real' sinks")
446         parser.add_argument("-m", "--mute", dest="mute",
447                             action="store_true",
448                             help="Mute playback output, which means that we use "
449                             "a fakesink")
450         parser.add_argument("-n", "--no-color", dest="no_color",
451                             action="store_true",
452                             help="Set it to output no colored text in the terminal")
453         parser.add_argument("-g", "--generate-media-info", dest="generate_info",
454                             action="store_true",
455                             help="Set it in order to generate the missing .media_infos files")
456         parser.add_argument("--update-media-info", dest="update_media_info",
457                             action="store_true",
458                             help="Set it in order to update existing .media_infos files")
459         parser.add_argument(
460             "-G", "--generate-media-info-with-frame-detection", dest="generate_info_full",
461             action="store_true",
462             help="Set it in order to generate the missing .media_infos files. "
463             "It implies --generate-media-info but enabling frame detection")
464         parser.add_argument("-lt", "--long-test-limit", dest="long_limit",
465                             action='store',
466                             help="Defines the limit for which a test is considered as long (in seconds)."
467                             " Note that 0 will enable all tests", type=int),
468         parser.add_argument("--dump-on-failure", dest="dump_on_failure",
469                             action="store_true", default=False,
470                             help="Dump logs to stdout when a test fails."
471                             " Note that bat is used to enhance output if available"
472                             " (See https://github.com/sharkdp/bat)")
473         parser.add_argument("--max-dump-size", dest="max_dump_size", type=float,
474                             default=0.5, help="Maximum size of logs to dump on stdout in MB.")
475         parser.add_argument("-c", "--config", dest="config",
476                             help="This is DEPRECATED, prefer using the testsuite format"
477                             " to configure testsuites")
478         parser.add_argument("-vg", "--valgrind", dest="valgrind",
479                             action="store_true",
480                             help="Run the tests inside Valgrind")
481         parser.add_argument("--gdb", dest="gdb",
482                             action="store_true",
483                             help="Run the tests inside gdb (implies"
484                             " --output-dir=stdout and --jobs=1)")
485         parser.add_argument("--gdb-non-stop", dest="gdb_non_stop",
486                             action="store_true",
487                             help="Run the test automatically in gdb (implies --gdb)")
488         parser.add_argument("-nd", "--no-display", dest="no_display",
489                             action="store_true",
490                             help="Run the tests without outputting graphics"
491                             " on any display. It tries to run all graphical operation"
492                             " in a virtual framebuffer."
493                             " Note that it is currently implemented only"
494                             " for the X  server thanks to Xvfb (which is requeried in that case)")
495         parser.add_argument('--xunit-file', dest='xunit_file',
496                             action='store', metavar="FILE",
497                             help=("Path to xml file to store the xunit report in."))
498         parser.add_argument('--shuffle', dest="shuffle", action="store_true",
499                             help="Runs the test in a random order. Can help speed up the overall"
500                             " test time by running synchronized and unsynchronized tests"
501                             " at the same time")
502         parser.add_argument('--retry-on-failures', dest="retry_on_failures", action="store_true",
503                             help="Re-try tests that produce unexpected results")
504         parser.add_argument('--html', dest="html", action="store_true",
505                             help="Write logs as html")
506         dir_group = parser.add_argument_group(
507             "Directories and files to be used by the launcher")
508         dir_group.add_argument("-M", "--main-dir", dest="main_dir",
509                                help="Main directory where to put files."
510                                " Respects the GST_VALIDATE_LAUNCHER_MAIN_DIR environment variable."
511                                " Default is %s" % DEFAULT_MAIN_DIR)
512         dir_group.add_argument("--testsuites-dir", dest="testsuites_dirs", action='append',
513                                help="Directory where to look for testsuites. Default is %s"
514                                % utils.DEFAULT_TESTSUITES_DIRS)
515         dir_group.add_argument("-o", "--output-dir", dest="output_dir",
516                                help="Directory where to store logs and rendered files. Default is MAIN_DIR")
517         dir_group.add_argument("-l", "--logs-dir", dest="logsdir",
518                                help="Directory where to store logs, default is OUTPUT_DIR/logs.")
519         dir_group.add_argument("-R", "--render-path", dest="dest",
520                                help="Set the path to which projects should be rendered, default is OUTPUT_DIR/rendered")
521         dir_group.add_argument("-p", "--medias-paths", dest="user_paths", action="append",
522                                help="Paths in which to look for media files")
523         dir_group.add_argument("-a", "--clone-dir", dest="clone_dir",
524                                help="Paths where to clone the testuite to run."
525                                " default is MAIN_DIR/gst-integration-testsuites")
526         dir_group.add_argument("-rl", "--redirect-logs", dest="redirect_logs",
527                                help="Redirect logs to 'stdout' or 'sdterr'.")
528         dir_group.add_argument("-v", "--verbose", dest="verbose",
529                                action='count',
530                                help="Redirect logs to stdout.")
531         dir_group.add_argument("-j", "--jobs", dest="num_jobs",
532                                help="Number of tests to execute simultaneously"
533                                " (Defaults to number of cores of the processor)",
534                                type=int)
535         dir_group.add_argument("--ignore-numfailures", dest="ignore_numfailures",
536                                help="Ignore the number of failed test in exit code",
537                                default=False, action='store_true')
538         dir_group.add_argument("--parts", dest="num_parts",
539                                help="Splits the tests in equally distributed parts and only run one part"
540                                " (Defaults to 1 part)",
541                                type=int, default=1)
542         dir_group.add_argument("--part-index", dest="part_index",
543                                help="The index of the part to be run (starts at 1).",
544                                type=int, default=1)
545
546         http_server_group = parser.add_argument_group(
547             "Handle the HTTP server to be created")
548         http_server_group.add_argument(
549             "--http-server-port", dest="http_server_port",
550             help="Port on which to run the http server on localhost", type=int)
551         http_server_group.add_argument(
552             "--http-bandwith-limitation", dest="http_bandwith",
553             help="The artificial bandwith limitation to introduce to the local server (in Bytes/sec) (default: 1 MBps)")
554         http_server_group.add_argument(
555             "-s", "--folder-for-http-server", dest="http_server_dir",
556             help="Folder in which to create an http server on localhost. Default is PATHS")
557         http_server_group.add_argument("--http-only", dest="httponly",
558                                        action='store_true',
559                                        help="Start the http server and quit")
560
561         assets_group = parser.add_argument_group("Handle remote assets")
562         assets_group.add_argument(
563             "--get-assets-command", dest="get_assets_command",
564             help="Command to get assets")
565         assets_group.add_argument("--remote-assets-url", dest="remote_assets_url",
566                                   help="Url to the remote assets (default:%s)" % DEFAULT_GST_QA_ASSETS_REPO)
567         assets_group.add_argument("-S", "--sync", dest="sync", action="store_true",
568                                   help="Synchronize asset repository")
569         assets_group.add_argument("-fs", "--force-sync", dest="force_sync", action="store_true",
570                                   help="Synchronize asset repository reseting any change that might have"
571                                   " happened in the testsuite")
572         assets_group.add_argument("--sync-all", dest="sync_all", action="store_true",
573                                   help="Synchronize asset repository,"
574                                   " including big media files")
575         assets_group.add_argument("--usage", action=PrintUsage,
576                                   help="Print usage documentation")
577         return parser
578
579
580 def setup_launcher_from_args(args, main_options=None):
581     loggable.init("GST_VALIDATE_LAUNCHER_DEBUG", True, False)
582     parser = LauncherConfig.create_parser()
583     tests_launcher = _TestsLauncher()
584     tests_launcher.add_options(parser)
585
586     if "--help" in sys.argv and which(LESS):
587         tmpf = tempfile.NamedTemporaryFile(mode='r+')
588
589         parser.print_help(file=tmpf)
590         os.system("%s %s" % (LESS, tmpf.name))
591         return False, None, None
592
593     options = LauncherConfig()
594     parser.parse_args(args=args, namespace=options)
595     if main_options:
596         # Override output directories and logging properties of the sub launcher.
597         for option in ["main_dir", "output_dir", "logsdir", "dest", "clone_dir",
598                        "redirect_logs", "verbose", "timeout_factor"]:
599             setattr(options, option, getattr(main_options, option))
600     if not options.cleanup():
601         return False, None, None
602
603     if options.remote_assets_url and options.sync and not os.path.exists(options.clone_dir):
604         if not download_assets(options):
605             return False, None, None
606
607     # Ensure that the scenario manager singleton is ready to be used
608     ScenarioManager().config = options
609     if not tests_launcher.set_settings(options, []):
610         return False, None, None
611
612     return True, options, tests_launcher
613
614
615 def main(libsdir):
616     global LIBSDIR
617     LIBSDIR = libsdir
618
619     utils.DEFAULT_TESTSUITES_DIRS.append(os.path.join(LIBSDIR, "testsuites"))
620     os.environ["GST_VALIDATE_APPS_DIR"] = os.path.join(
621         LIBSDIR, "apps") + os.pathsep + os.environ.get("GST_VALIDATE_APPS_DIR", "")
622
623     res, options, tests_launcher = setup_launcher_from_args(sys.argv[1:])
624     if res is False:
625         return 1
626
627     if options.list_tests:
628         if tests_launcher.list_tests() == -1:
629             printc("\nFailling as tests have been removed/added "
630                    " (--fail-on-testlist-change)", Colors.FAIL)
631             return 1
632
633         tests = tests_launcher.tests
634         for test in tests:
635             printc(test)
636
637         printc("\nNumber of tests: %d" % len(tests), Colors.OKGREEN)
638         return 0
639
640     if options.httponly is True:
641         print("Running HTTP server only")
642         return 0
643
644     # There seems to be some issue with forking, dconf and some gtype
645     # initialization that deadlocks occasionally, setting the
646     # GSettings backend make it go away.
647     # Also happened here:
648     # https://cgit.freedesktop.org/gstreamer/gst-plugins-good/commit/tests/check/Makefile.am?id=8e2c1d1de56bddbff22170f8b17473882e0e63f9
649     os.environ['GSETTINGS_BACKEND'] = "memory"
650
651     exception = None
652     try:
653         tests_launcher.run_tests()
654     except Exception as e:
655         exception = e
656         pass
657     finally:
658         res = tests_launcher.final_report()
659         if options.ignore_numfailures:
660             res = 0
661         if exception is not None:
662             raise exception
663
664     return res