Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 file,
3 # You can obtain one at http://mozilla.org/MPL/2.0/.
5 import json
6 import os
7 import posixpath
8 import shutil
9 import sys
10 import tempfile
11 import threading
12 import time
13 import traceback
15 here = os.path.abspath(os.path.dirname(__file__))
16 sys.path.insert(0, here)
18 from runtests import Mochitest
19 from runtests import MochitestUtilsMixin
20 from runtests import MochitestOptions
21 from runtests import MochitestServer
22 from mochitest_options import B2GOptions, MochitestOptions
24 from marionette import Marionette
26 from mozdevice import DeviceManagerADB
27 from mozprofile import Profile, Preferences
28 from mozrunner import B2GRunner
29 import mozlog
30 import mozinfo
31 import moznetwork
33 log = mozlog.getLogger('Mochitest')
35 class B2GMochitest(MochitestUtilsMixin):
36 def __init__(self, marionette,
37 out_of_process=True,
38 profile_data_dir=None,
39 locations=os.path.join(here, 'server-locations.txt')):
40 super(B2GMochitest, self).__init__()
41 self.marionette = marionette
42 self.out_of_process = out_of_process
43 self.locations_file = locations
44 self.preferences = []
45 self.webapps = None
46 self.test_script = os.path.join(here, 'b2g_start_script.js')
47 self.test_script_args = [self.out_of_process]
48 self.product = 'b2g'
50 if profile_data_dir:
51 self.preferences = [os.path.join(profile_data_dir, f)
52 for f in os.listdir(profile_data_dir) if f.startswith('pref')]
53 self.webapps = [os.path.join(profile_data_dir, f)
54 for f in os.listdir(profile_data_dir) if f.startswith('webapp')]
56 # mozinfo is populated by the parent class
57 if mozinfo.info['debug']:
58 self.SERVER_STARTUP_TIMEOUT = 180
59 else:
60 self.SERVER_STARTUP_TIMEOUT = 90
62 def setup_common_options(self, options):
63 test_url = self.buildTestPath(options)
64 if len(self.urlOpts) > 0:
65 test_url += "?" + "&".join(self.urlOpts)
66 self.test_script_args.append(test_url)
68 def buildTestPath(self, options):
69 # Skip over the manifest building that happens on desktop.
70 return self.buildTestURL(options)
72 def build_profile(self, options):
73 # preferences
74 prefs = {}
75 for path in self.preferences:
76 prefs.update(Preferences.read_prefs(path))
78 for v in options.extraPrefs:
79 thispref = v.split("=", 1)
80 if len(thispref) < 2:
81 print "Error: syntax error in --setpref=" + v
82 sys.exit(1)
83 prefs[thispref[0]] = thispref[1]
85 # interpolate the preferences
86 interpolation = { "server": "%s:%s" % (options.webServer, options.httpPort),
87 "OOP": "true" if self.out_of_process else "false" }
88 prefs = json.loads(json.dumps(prefs) % interpolation)
89 for pref in prefs:
90 prefs[pref] = Preferences.cast(prefs[pref])
92 kwargs = {
93 'addons': self.getExtensionsToInstall(options),
94 'apps': self.webapps,
95 'locations': self.locations_file,
96 'preferences': prefs,
97 'proxy': {"remote": options.webServer}
98 }
100 if options.profile:
101 self.profile = Profile.clone(options.profile, **kwargs)
102 else:
103 self.profile = Profile(**kwargs)
105 options.profilePath = self.profile.profile
106 # TODO bug 839108 - mozprofile should probably handle this
107 manifest = self.addChromeToProfile(options)
108 self.copyExtraFilesToProfile(options)
109 return manifest
111 def run_tests(self, options):
112 """ Prepare, configure, run tests and cleanup """
114 self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")
115 manifest = self.build_profile(options)
117 self.startServers(options, None)
118 self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'})
119 self.test_script_args.append(not options.emulator)
120 self.test_script_args.append(options.wifi)
122 if options.debugger or not options.autorun:
123 timeout = None
124 else:
125 if not options.timeout:
126 if mozinfo.info['debug']:
127 options.timeout = 420
128 else:
129 options.timeout = 300
130 timeout = options.timeout + 30.0
132 log.info("runtestsb2g.py | Running tests: start.")
133 status = 0
134 try:
135 runner_args = { 'profile': self.profile,
136 'devicemanager': self._dm,
137 'marionette': self.marionette,
138 'remote_test_root': self.remote_test_root,
139 'symbols_path': options.symbolsPath,
140 'test_script': self.test_script,
141 'test_script_args': self.test_script_args }
142 self.runner = B2GRunner(**runner_args)
143 self.runner.start(outputTimeout=timeout)
144 status = self.runner.wait()
145 if status is None:
146 # the runner has timed out
147 status = 124
148 except KeyboardInterrupt:
149 log.info("runtests.py | Received keyboard interrupt.\n");
150 status = -1
151 except:
152 traceback.print_exc()
153 log.error("Automation Error: Received unexpected exception while running application\n")
154 self.runner.check_for_crashes()
155 status = 1
157 self.stopServers()
159 log.info("runtestsb2g.py | Running tests: end.")
161 if manifest is not None:
162 self.cleanup(manifest, options)
163 return status
166 class B2GDeviceMochitest(B2GMochitest):
168 _dm = None
170 def __init__(self, marionette, devicemanager, profile_data_dir,
171 local_binary_dir, remote_test_root=None, remote_log_file=None):
172 B2GMochitest.__init__(self, marionette, out_of_process=True, profile_data_dir=profile_data_dir)
173 self._dm = devicemanager
174 self.remote_test_root = remote_test_root or self._dm.getDeviceRoot()
175 self.remote_profile = posixpath.join(self.remote_test_root, 'profile')
176 self.remote_log = remote_log_file or posixpath.join(self.remote_test_root, 'log', 'mochitest.log')
177 self.local_log = None
178 self.local_binary_dir = local_binary_dir
180 if not self._dm.dirExists(posixpath.dirname(self.remote_log)):
181 self._dm.mkDirs(self.remote_log)
183 def cleanup(self, manifest, options):
184 if self.local_log:
185 self._dm.getFile(self.remote_log, self.local_log)
186 self._dm.removeFile(self.remote_log)
188 if options.pidFile != "":
189 try:
190 os.remove(options.pidFile)
191 os.remove(options.pidFile + ".xpcshell.pid")
192 except:
193 print "Warning: cleaning up pidfile '%s' was unsuccessful from the test harness" % options.pidFile
195 # stop and clean up the runner
196 if getattr(self, 'runner', False):
197 self.runner.cleanup()
198 self.runner = None
200 def startServers(self, options, debuggerInfo):
201 """ Create the servers on the host and start them up """
202 savedXre = options.xrePath
203 savedUtility = options.utilityPath
204 savedProfie = options.profilePath
205 options.xrePath = self.local_binary_dir
206 options.utilityPath = self.local_binary_dir
207 options.profilePath = tempfile.mkdtemp()
209 MochitestUtilsMixin.startServers(self, options, debuggerInfo)
211 options.xrePath = savedXre
212 options.utilityPath = savedUtility
213 options.profilePath = savedProfie
215 def buildURLOptions(self, options, env):
216 self.local_log = options.logFile
217 options.logFile = self.remote_log
218 options.profilePath = self.profile.profile
219 retVal = super(B2GDeviceMochitest, self).buildURLOptions(options, env)
221 self.setup_common_options(options)
223 options.profilePath = self.remote_profile
224 options.logFile = self.local_log
225 return retVal
228 class B2GDesktopMochitest(B2GMochitest, Mochitest):
230 def __init__(self, marionette, profile_data_dir):
231 B2GMochitest.__init__(self, marionette, out_of_process=False, profile_data_dir=profile_data_dir)
232 Mochitest.__init__(self)
233 self.certdbNew = True
235 def runMarionetteScript(self, marionette, test_script, test_script_args):
236 assert(marionette.wait_for_port())
237 marionette.start_session()
238 marionette.set_context(marionette.CONTEXT_CHROME)
240 if os.path.isfile(test_script):
241 f = open(test_script, 'r')
242 test_script = f.read()
243 f.close()
244 self.marionette.execute_script(test_script,
245 script_args=test_script_args)
247 def startTests(self):
248 # This is run in a separate thread because otherwise, the app's
249 # stdout buffer gets filled (which gets drained only after this
250 # function returns, by waitForFinish), which causes the app to hang.
251 thread = threading.Thread(target=self.runMarionetteScript,
252 args=(self.marionette,
253 self.test_script,
254 self.test_script_args))
255 thread.start()
257 def buildURLOptions(self, options, env):
258 retVal = super(B2GDesktopMochitest, self).buildURLOptions(options, env)
260 self.setup_common_options(options)
262 # Copy the extensions to the B2G bundles dir.
263 extensionDir = os.path.join(options.profilePath, 'extensions', 'staged')
264 bundlesDir = os.path.join(os.path.dirname(options.app),
265 'distribution', 'bundles')
267 for filename in os.listdir(extensionDir):
268 shutil.rmtree(os.path.join(bundlesDir, filename), True)
269 shutil.copytree(os.path.join(extensionDir, filename),
270 os.path.join(bundlesDir, filename))
272 return retVal
274 def buildProfile(self, options):
275 return self.build_profile(options)
278 def run_remote_mochitests(parser, options):
279 # create our Marionette instance
280 kwargs = {}
281 if options.emulator:
282 kwargs['emulator'] = options.emulator
283 if options.noWindow:
284 kwargs['noWindow'] = True
285 if options.geckoPath:
286 kwargs['gecko_path'] = options.geckoPath
287 if options.logcat_dir:
288 kwargs['logcat_dir'] = options.logcat_dir
289 if options.busybox:
290 kwargs['busybox'] = options.busybox
291 if options.symbolsPath:
292 kwargs['symbols_path'] = options.symbolsPath
293 # needless to say sdcard is only valid if using an emulator
294 if options.sdcard:
295 kwargs['sdcard'] = options.sdcard
296 if options.b2gPath:
297 kwargs['homedir'] = options.b2gPath
298 if options.marionette:
299 host, port = options.marionette.split(':')
300 kwargs['host'] = host
301 kwargs['port'] = int(port)
303 marionette = Marionette.getMarionetteOrExit(**kwargs)
305 if options.emulator:
306 dm = marionette.emulator.dm
307 else:
308 # create the DeviceManager
309 kwargs = {'adbPath': options.adbPath,
310 'deviceRoot': options.remoteTestRoot}
311 if options.deviceIP:
312 kwargs.update({'host': options.deviceIP,
313 'port': options.devicePort})
314 dm = DeviceManagerADB(**kwargs)
316 options = parser.verifyRemoteOptions(options)
317 if (options == None):
318 print "ERROR: Invalid options specified, use --help for a list of valid options"
319 sys.exit(1)
321 mochitest = B2GDeviceMochitest(marionette, dm, options.profile_data_dir, options.xrePath,
322 remote_test_root=options.remoteTestRoot,
323 remote_log_file=options.remoteLogFile)
325 options = parser.verifyOptions(options, mochitest)
326 if (options == None):
327 sys.exit(1)
329 retVal = 1
330 try:
331 mochitest.cleanup(None, options)
332 retVal = mochitest.run_tests(options)
333 except:
334 print "Automation Error: Exception caught while running tests"
335 traceback.print_exc()
336 mochitest.stopServers()
337 try:
338 mochitest.cleanup(None, options)
339 except:
340 pass
341 retVal = 1
343 sys.exit(retVal)
345 def run_desktop_mochitests(parser, options):
346 # create our Marionette instance
347 kwargs = {}
348 if options.marionette:
349 host, port = options.marionette.split(':')
350 kwargs['host'] = host
351 kwargs['port'] = int(port)
352 marionette = Marionette.getMarionetteOrExit(**kwargs)
353 mochitest = B2GDesktopMochitest(marionette, options.profile_data_dir)
355 # add a -bin suffix if b2g-bin exists, but just b2g was specified
356 if options.app[-4:] != '-bin':
357 if os.path.isfile("%s-bin" % options.app):
358 options.app = "%s-bin" % options.app
360 options = MochitestOptions.verifyOptions(parser, options, mochitest)
361 if options == None:
362 sys.exit(1)
364 if options.desktop and not options.profile:
365 raise Exception("must specify --profile when specifying --desktop")
367 options.browserArgs += ['-marionette']
369 sys.exit(mochitest.runTests(options, onLaunch=mochitest.startTests))
371 def main():
372 parser = B2GOptions()
373 options, args = parser.parse_args()
375 if options.desktop:
376 run_desktop_mochitests(parser, options)
377 else:
378 run_remote_mochitests(parser, options)
380 if __name__ == "__main__":
381 main()