3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 import runcppunittests
as cppunittests
9 import mozcrash
, mozlog
12 from mozdevice
import devicemanager
, devicemanagerADB
, devicemanagerSUT
14 log
= mozlog
.getLogger('remotecppunittests')
16 class RemoteCPPUnitTests(cppunittests
.CPPUnitTests
):
17 def __init__(self
, devmgr
, options
, progs
):
18 cppunittests
.CPPUnitTests
.__init
__(self
)
19 self
.options
= options
21 self
.remote_test_root
= self
.device
.getDeviceRoot() + "/cppunittests"
22 self
.remote_bin_dir
= posixpath
.join(self
.remote_test_root
, "b")
23 self
.remote_tmp_dir
= posixpath
.join(self
.remote_test_root
, "tmp")
24 self
.remote_home_dir
= posixpath
.join(self
.remote_test_root
, "h")
28 def setup_bin(self
, progs
):
29 if not self
.device
.dirExists(self
.remote_test_root
):
30 self
.device
.mkDir(self
.remote_test_root
)
31 if self
.device
.dirExists(self
.remote_tmp_dir
):
32 self
.device
.removeDir(self
.remote_tmp_dir
)
33 self
.device
.mkDir(self
.remote_tmp_dir
)
34 if self
.device
.dirExists(self
.remote_bin_dir
):
35 self
.device
.removeDir(self
.remote_bin_dir
)
36 self
.device
.mkDir(self
.remote_bin_dir
)
37 if self
.device
.dirExists(self
.remote_home_dir
):
38 self
.device
.removeDir(self
.remote_home_dir
)
39 self
.device
.mkDir(self
.remote_home_dir
)
41 self
.push_progs(progs
)
42 self
.device
.chmodDir(self
.remote_bin_dir
)
45 for file in os
.listdir(self
.options
.local_lib
):
46 if file.endswith(".so"):
47 print >> sys
.stderr
, "Pushing %s.." % file
48 remote_file
= posixpath
.join(self
.remote_bin_dir
, file)
49 self
.device
.pushFile(os
.path
.join(self
.options
.local_lib
, file), remote_file
)
50 # Additional libraries may be found in a sub-directory such as "lib/armeabi-v7a"
51 local_arm_lib
= os
.path
.join(self
.options
.local_lib
, "lib")
52 if os
.path
.isdir(local_arm_lib
):
53 for root
, dirs
, files
in os
.walk(local_arm_lib
):
55 if (file.endswith(".so")):
56 remote_file
= posixpath
.join(self
.remote_bin_dir
, file)
57 self
.device
.pushFile(os
.path
.join(root
, file), remote_file
)
59 def push_progs(self
, progs
):
60 for local_file
in progs
:
61 remote_file
= posixpath
.join(self
.remote_bin_dir
, os
.path
.basename(local_file
))
62 self
.device
.pushFile(local_file
, remote_file
)
64 def build_environment(self
):
65 env
= self
.build_core_environment()
66 env
['LD_LIBRARY_PATH'] = self
.remote_bin_dir
67 env
["TMPDIR"]=self
.remote_tmp_dir
68 env
["HOME"]=self
.remote_home_dir
69 env
["MOZILLA_FIVE_HOME"] = self
.remote_home_dir
70 env
["MOZ_XRE_DIR"] = self
.remote_bin_dir
71 if self
.options
.add_env
:
72 for envdef
in self
.options
.add_env
:
73 envdef_parts
= envdef
.split("=", 1)
74 if len(envdef_parts
) == 2:
75 env
[envdef_parts
[0]] = envdef_parts
[1]
76 elif len(envdef_parts
) == 1:
77 env
[envdef_parts
[0]] = ""
79 print >> sys
.stderr
, "warning: invalid --addEnv option skipped: "+envdef
83 def run_one_test(self
, prog
, env
, symbols_path
=None):
85 Run a single C++ unit test program remotely.
88 * prog: The path to the test program to run.
89 * env: The environment to use for running the program.
90 * symbols_path: A path to a directory containing Breakpad-formatted
91 symbol files for producing stack traces on crash.
93 Return True if the program exits with a zero status, False otherwise.
95 basename
= os
.path
.basename(prog
)
96 remote_bin
= posixpath
.join(self
.remote_bin_dir
, basename
)
97 log
.info("Running test %s", basename
)
98 buf
= StringIO
.StringIO()
99 returncode
= self
.device
.shell([remote_bin
], buf
, env
=env
, cwd
=self
.remote_home_dir
,
100 timeout
=cppunittests
.CPPUnitTests
.TEST_PROC_TIMEOUT
)
101 print >> sys
.stdout
, buf
.getvalue()
102 with cppunittests
.TemporaryDirectory() as tempdir
:
103 self
.device
.getDirectory(self
.remote_home_dir
, tempdir
)
104 if mozcrash
.check_for_crashes(tempdir
, symbols_path
,
106 log
.testFail("%s | test crashed", basename
)
108 result
= returncode
== 0
110 log
.testFail("%s | test failed with return code %s",
111 basename
, returncode
)
114 class RemoteCPPUnittestOptions(cppunittests
.CPPUnittestOptions
):
116 cppunittests
.CPPUnittestOptions
.__init
__(self
)
119 self
.add_option("--deviceIP", action
="store",
120 type = "string", dest
= "device_ip",
121 help = "ip address of remote device to test")
122 defaults
["device_ip"] = None
124 self
.add_option("--devicePort", action
="store",
125 type = "string", dest
= "device_port",
126 help = "port of remote device to test")
127 defaults
["device_port"] = 20701
129 self
.add_option("--dm_trans", action
="store",
130 type = "string", dest
= "dm_trans",
131 help = "the transport to use to communicate with device: [adb|sut]; default=sut")
132 defaults
["dm_trans"] = "sut"
134 self
.add_option("--noSetup", action
="store_false",
136 help = "do not copy any files to device (to be used only if device is already setup)")
137 defaults
["setup"] = True
139 self
.add_option("--localLib", action
="store",
140 type = "string", dest
= "local_lib",
141 help = "location of libraries to push -- preferably stripped")
142 defaults
["local_lib"] = None
144 self
.add_option("--remoteTestRoot", action
= "store",
145 type = "string", dest
= "remote_test_root",
146 help = "remote directory to use as test root (eg. /data/local/tests)")
147 # /data/local/tests is used because it is usually not possible to set +x permissions
148 # on binaries on /mnt/sdcard
149 defaults
["remote_test_root"] = "/data/local/tests"
151 self
.add_option("--addEnv", action
= "append",
152 type = "string", dest
= "add_env",
153 help = "additional remote environment variable definitions (eg. --addEnv \"somevar=something\")")
154 defaults
["add_env"] = None
156 self
.set_defaults(**defaults
)
159 parser
= RemoteCPPUnittestOptions()
160 options
, args
= parser
.parse_args()
162 print >>sys
.stderr
, """Usage: %s <test binary> [<test binary>...]""" % sys
.argv
[0]
164 if not options
.local_lib
:
165 print >>sys
.stderr
, """Error: --localLib is required"""
167 if not os
.path
.isdir(options
.local_lib
):
168 print >>sys
.stderr
, """Error: --localLib directory %s not found""" % options
.local_lib
170 if not options
.xre_path
:
171 print >>sys
.stderr
, """Error: --xre-path is required"""
173 if options
.dm_trans
== "adb":
174 if options
.device_ip
:
175 dm
= devicemanagerADB
.DeviceManagerADB(options
.device_ip
, options
.device_port
, packageName
=None, deviceRoot
=options
.remote_test_root
)
177 dm
= devicemanagerADB
.DeviceManagerADB(packageName
=None, deviceRoot
=options
.remote_test_root
)
179 dm
= devicemanagerSUT
.DeviceManagerSUT(options
.device_ip
, options
.device_port
, deviceRoot
=options
.remote_test_root
)
180 if not options
.device_ip
:
181 print "Error: you must provide a device IP to connect to via the --deviceIP option"
183 options
.xre_path
= os
.path
.abspath(options
.xre_path
)
184 progs
= [os
.path
.abspath(p
) for p
in args
]
185 tester
= RemoteCPPUnitTests(dm
, options
, progs
)
187 result
= tester
.run_tests(progs
, options
.xre_path
, options
.symbols_path
)
191 sys
.exit(0 if result
else 1)
193 if __name__
== '__main__':