1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 # Integrates the xpcshell test runner with mach.
11 from multiprocessing
import cpu_count
13 from mach
.decorators
import Command
14 from mozbuild
.base
import BinaryNotFoundException
, MozbuildObject
15 from mozbuild
.base
import MachCommandConditions
as conditions
16 from mozlog
import structured
17 from xpcshellcommandline
import parser_desktop
, parser_remote
19 here
= os
.path
.abspath(os
.path
.dirname(__file__
))
22 # This should probably be consolidated with similar classes in other test
24 class InvalidTestPathError(Exception):
25 """Exception raised when the test path is not valid."""
28 class XPCShellRunner(MozbuildObject
):
29 """Run xpcshell tests."""
31 def run_suite(self
, **kwargs
):
32 return self
._run
_xpcshell
_harness
(**kwargs
)
34 def run_test(self
, **kwargs
):
35 """Runs an individual xpcshell test."""
37 # TODO Bug 794506 remove once mach integrates with virtualenv.
38 build_path
= os
.path
.join(self
.topobjdir
, "build")
39 if build_path
not in sys
.path
:
40 sys
.path
.append(build_path
)
42 src_build_path
= os
.path
.join(self
.topsrcdir
, "mozilla", "build")
43 if os
.path
.isdir(src_build_path
):
44 sys
.path
.append(src_build_path
)
46 return self
.run_suite(**kwargs
)
48 def _run_xpcshell_harness(self
, **kwargs
):
49 # Obtain a reference to the xpcshell test runner.
50 import runxpcshelltests
52 log
= kwargs
.pop("log")
54 xpcshell
= runxpcshelltests
.XPCShellTests(log
=log
)
55 self
.log_manager
.enable_unstructured()
57 tests_dir
= os
.path
.join(self
.topobjdir
, "_tests", "xpcshell")
58 # We want output from the test to be written immediately if we are only
59 # running a single test.
61 len(kwargs
["testPaths"]) == 1
62 and os
.path
.isfile(kwargs
["testPaths"][0])
64 and (len(kwargs
["manifest"].test_paths()) == 1)
68 kwargs
["verbose"] = True
70 if kwargs
["xpcshell"] is None:
72 kwargs
["xpcshell"] = self
.get_binary_path("xpcshell")
73 except BinaryNotFoundException
as e
:
75 logging
.ERROR
, "xpcshell-test", {"error": str(e
)}, "ERROR: {error}"
77 self
.log(logging
.INFO
, "xpcshell-test", {"help": e
.help()}, "{help}")
80 if kwargs
["mozInfo"] is None:
81 kwargs
["mozInfo"] = os
.path
.join(self
.topobjdir
, "mozinfo.json")
83 if kwargs
["symbolsPath"] is None:
84 kwargs
["symbolsPath"] = os
.path
.join(self
.distdir
, "crashreporter-symbols")
86 if kwargs
["logfiles"] is None:
87 kwargs
["logfiles"] = False
89 if kwargs
["profileName"] is None:
90 kwargs
["profileName"] = "firefox"
92 if kwargs
["testingModulesDir"] is None:
93 kwargs
["testingModulesDir"] = os
.path
.join(self
.topobjdir
, "_tests/modules")
95 if kwargs
["utility_path"] is None:
96 kwargs
["utility_path"] = self
.bindir
98 if kwargs
["manifest"] is None:
99 kwargs
["manifest"] = os
.path
.join(tests_dir
, "xpcshell.ini")
101 if kwargs
["failure_manifest"] is None:
102 kwargs
["failure_manifest"] = os
.path
.join(
103 self
.statedir
, "xpcshell.failures.ini"
106 # Use the object directory for the temp directory to minimize the chance
107 # of file scanning. The overhead from e.g. search indexers and anti-virus
108 # scanners like Windows Defender can add tons of overhead to test execution.
109 # We encourage people to disable these things in the object directory.
110 temp_dir
= os
.path
.join(self
.topobjdir
, "temp")
114 if e
.errno
!= errno
.EEXIST
:
116 kwargs
["tempDir"] = temp_dir
118 result
= xpcshell
.runTests(kwargs
)
120 self
.log_manager
.disable_unstructured()
122 if not result
and not xpcshell
.sequential
:
124 "Tests were run in parallel. Try running with --sequential "
125 "to make sure the failures were not caused by this."
127 return int(not result
)
130 class AndroidXPCShellRunner(MozbuildObject
):
131 """Run Android xpcshell tests."""
133 def run_test(self
, **kwargs
):
134 # TODO Bug 794506 remove once mach integrates with virtualenv.
135 build_path
= os
.path
.join(self
.topobjdir
, "build")
136 if build_path
not in sys
.path
:
137 sys
.path
.append(build_path
)
139 import remotexpcshelltests
141 log
= kwargs
.pop("log")
142 self
.log_manager
.enable_unstructured()
144 if kwargs
["xpcshell"] is None:
145 kwargs
["xpcshell"] = "xpcshell"
147 if not kwargs
["objdir"]:
148 kwargs
["objdir"] = self
.topobjdir
150 if not kwargs
["localBin"]:
151 kwargs
["localBin"] = os
.path
.join(self
.topobjdir
, "dist/bin")
153 if not kwargs
["testingModulesDir"]:
154 kwargs
["testingModulesDir"] = os
.path
.join(self
.topobjdir
, "_tests/modules")
156 if not kwargs
["mozInfo"]:
157 kwargs
["mozInfo"] = os
.path
.join(self
.topobjdir
, "mozinfo.json")
159 if not kwargs
["manifest"]:
160 kwargs
["manifest"] = os
.path
.join(
161 self
.topobjdir
, "_tests/xpcshell/xpcshell.ini"
164 if not kwargs
["symbolsPath"]:
165 kwargs
["symbolsPath"] = os
.path
.join(self
.distdir
, "crashreporter-symbols")
167 if self
.substs
.get("MOZ_BUILD_APP") == "b2g":
168 kwargs
["localAPK"] = None
169 elif not kwargs
["localAPK"]:
170 for root
, _
, paths
in os
.walk(os
.path
.join(kwargs
["objdir"], "gradle")):
171 for file_name
in paths
:
172 if file_name
.endswith(".apk") and file_name
.startswith(
173 "test_runner-withGeckoBinaries"
175 kwargs
["localAPK"] = os
.path
.join(root
, file_name
)
176 print("using APK: %s" % kwargs
["localAPK"])
178 if kwargs
["localAPK"]:
181 raise Exception("APK not found in objdir. You must specify an APK.")
183 xpcshell
= remotexpcshelltests
.XPCShellRemote(kwargs
, log
)
185 result
= xpcshell
.runTests(
187 testClass
=remotexpcshelltests
.RemoteXPCShellTestThread
,
188 mobileArgs
=xpcshell
.mobileArgs
,
191 self
.log_manager
.disable_unstructured()
193 return int(not result
)
197 build_obj
= MozbuildObject
.from_environment(cwd
=here
)
199 conditions
.is_android(build_obj
)
200 or build_obj
.substs
.get("MOZ_BUILD_APP") == "b2g"
202 return parser_remote()
204 return parser_desktop()
210 description
="Run XPCOM Shell tests (API direct unit testing)",
211 conditions
=[lambda *args
: True],
214 def run_xpcshell_test(command_context
, test_objects
=None, **params
):
215 from mozbuild
.controller
.building
import BuildDriver
217 if test_objects
is not None:
218 from manifestparser
import TestManifest
221 m
.tests
.extend(test_objects
)
222 params
["manifest"] = m
224 driver
= command_context
._spawn
(BuildDriver
)
225 driver
.install_tests()
227 # We should probably have a utility function to ensure the tree is
228 # ready to run tests. Until then, we just create the state dir (in
229 # case the tree wasn't built with mach).
230 command_context
._ensure
_state
_subdir
_exists
(".")
232 if not params
.get("log"):
234 command_context
._mach
_context
.settings
["test"]["format"]: sys
.stdout
237 "level": command_context
._mach
_context
.settings
["test"]["level"],
240 params
["log"] = structured
.commandline
.setup_logging(
241 "XPCShellTests", params
, log_defaults
, fmt_defaults
244 if not params
["threadCount"]:
245 # pylint --py3k W1619
246 params
["threadCount"] = int((cpu_count() * 3) / 2)
249 conditions
.is_android(command_context
)
250 or command_context
.substs
.get("MOZ_BUILD_APP") == "b2g"
252 from mozrunner
.devices
.android_device
import (
255 verify_android_device
,
258 install
= InstallIntent
.YES
if params
["setup"] else InstallIntent
.NO
259 device_serial
= params
.get("deviceSerial")
260 verify_android_device(
264 device_serial
=device_serial
,
266 if not params
["adbPath"]:
267 params
["adbPath"] = get_adb_path(command_context
)
268 xpcshell
= command_context
._spawn
(AndroidXPCShellRunner
)
270 xpcshell
= command_context
._spawn
(XPCShellRunner
)
271 xpcshell
.cwd
= command_context
._mach
_context
.cwd
274 return xpcshell
.run_test(**params
)
275 except InvalidTestPathError
as e
: