1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
10 from devil
.android
import device_errors
11 from devil
.android
import device_temp_file
12 from devil
.android
import ports
13 from pylib
import constants
14 from pylib
.gtest
import gtest_test_instance
15 from pylib
.local
import local_test_server_spawner
16 from pylib
.local
.device
import local_device_environment
17 from pylib
.local
.device
import local_device_test_run
19 _COMMAND_LINE_FLAGS_SUPPORTED
= True
21 _EXTRA_COMMAND_LINE_FILE
= (
22 'org.chromium.native_test.NativeTestActivity.CommandLineFile')
23 _EXTRA_COMMAND_LINE_FLAGS
= (
24 'org.chromium.native_test.NativeTestActivity.CommandLineFlags')
26 'org.chromium.native_test.NativeTestInstrumentationTestRunner'
31 # TODO(jbudorick): Move this up to the test instance if the net test server is
32 # handled outside of the APK for the remote_device environment.
33 _SUITE_REQUIRES_TEST_SERVER_SPAWNER
= [
34 'components_browsertests', 'content_unittests', 'content_browsertests',
35 'net_unittests', 'unit_tests'
38 # TODO(jbudorick): Move this inside _ApkDelegate once TestPackageApk is gone.
39 def PullAppFilesImpl(device
, package
, files
, directory
):
40 device_dir
= device
.GetApplicationDataDirectory(package
)
41 host_dir
= os
.path
.join(directory
, str(device
))
43 device_file
= posixpath
.join(device_dir
, f
)
44 host_file
= os
.path
.join(host_dir
, *f
.split(posixpath
.sep
))
45 host_file_base
, ext
= os
.path
.splitext(host_file
)
46 for i
in itertools
.count():
47 host_file
= '%s_%d%s' % (host_file_base
, i
, ext
)
48 if not os
.path
.exists(host_file
):
50 device
.PullFile(device_file
, host_file
)
52 class _ApkDelegate(object):
53 def __init__(self
, test_instance
):
54 self
._activity
= test_instance
.activity
55 self
._apk
= test_instance
.apk
56 self
._package
= test_instance
.package
57 self
._runner
= test_instance
.runner
58 self
._permissions
= test_instance
.permissions
60 self
._component
= '%s/%s' % (self
._package
, self
._runner
)
61 self
._extras
= test_instance
.extras
63 def Install(self
, device
):
64 device
.Install(self
._apk
, permissions
=self
._permissions
)
66 def Run(self
, test
, device
, flags
=None, **kwargs
):
67 extras
= dict(self
._extras
)
69 with device_temp_file
.DeviceTempFile(device
.adb
) as command_line_file
:
70 device
.WriteFile(command_line_file
.name
, '_ %s' % flags
if flags
else '_')
71 extras
[_EXTRA_COMMAND_LINE_FILE
] = command_line_file
.name
73 with device_temp_file
.DeviceTempFile(device
.adb
) as test_list_file
:
75 device
.WriteFile(test_list_file
.name
, '\n'.join(test
))
76 extras
[_EXTRA_TEST_LIST
] = test_list_file
.name
78 return device
.StartInstrumentation(
79 self
._component
, extras
=extras
, raw
=False, **kwargs
)
81 def PullAppFiles(self
, device
, files
, directory
):
82 PullAppFilesImpl(device
, self
._package
, files
, directory
)
84 def Clear(self
, device
):
85 device
.ClearApplicationState(self
._package
)
88 class _ExeDelegate(object):
89 def __init__(self
, tr
, exe
):
90 self
._exe
_host
_path
= exe
91 self
._exe
_file
_name
= os
.path
.split(exe
)[-1]
92 self
._exe
_device
_path
= '%s/%s' % (
93 constants
.TEST_EXECUTABLE_DIR
, self
._exe
_file
_name
)
94 deps_host_path
= self
._exe
_host
_path
+ '_deps'
95 if os
.path
.exists(deps_host_path
):
96 self
._deps
_host
_path
= deps_host_path
97 self
._deps
_device
_path
= self
._exe
_device
_path
+ '_deps'
99 self
._deps
_host
_path
= None
102 def Install(self
, device
):
103 # TODO(jbudorick): Look into merging this with normal data deps pushing if
104 # executables become supported on nonlocal environments.
105 host_device_tuples
= [(self
._exe
_host
_path
, self
._exe
_device
_path
)]
106 if self
._deps
_host
_path
:
107 host_device_tuples
.append((self
._deps
_host
_path
, self
._deps
_device
_path
))
108 device
.PushChangedFiles(host_device_tuples
)
110 def Run(self
, test
, device
, flags
=None, **kwargs
):
112 self
._test
_run
.GetTool(device
).GetTestWrapper(),
113 self
._exe
_device
_path
,
116 cmd
.append('--gtest_filter=%s' % ':'.join(test
))
119 cwd
= constants
.TEST_EXECUTABLE_DIR
123 '%s/%s_deps' % (constants
.TEST_EXECUTABLE_DIR
, self
._exe
_file
_name
),
126 gcov_strip_depth
= os
.environ
['NATIVE_COVERAGE_DEPTH_STRIP']
127 external
= device
.GetExternalStoragePath()
128 env
['GCOV_PREFIX'] = '%s/gcov' % external
129 env
['GCOV_PREFIX_STRIP'] = gcov_strip_depth
130 except (device_errors
.CommandFailedError
, KeyError):
133 # TODO(jbudorick): Switch to just RunShellCommand once perezju@'s CL
134 # for long shell commands lands.
135 with device_temp_file
.DeviceTempFile(device
.adb
) as script_file
:
136 script_contents
= ' '.join(cmd
)
137 logging
.info('script contents: %r', script_contents
)
138 device
.WriteFile(script_file
.name
, script_contents
)
139 output
= device
.RunShellCommand(['sh', script_file
.name
], cwd
=cwd
,
143 def PullAppFiles(self
, device
, files
, directory
):
146 def Clear(self
, device
):
147 device
.KillAll(self
._exe
_file
_name
, blocking
=True, timeout
=30, quiet
=True)
150 class LocalDeviceGtestRun(local_device_test_run
.LocalDeviceTestRun
):
152 def __init__(self
, env
, test_instance
):
153 assert isinstance(env
, local_device_environment
.LocalDeviceEnvironment
)
154 assert isinstance(test_instance
, gtest_test_instance
.GtestTestInstance
)
155 super(LocalDeviceGtestRun
, self
).__init
__(env
, test_instance
)
157 if self
._test
_instance
.apk
:
158 self
._delegate
= _ApkDelegate(self
._test
_instance
)
159 elif self
._test
_instance
.exe
:
160 self
._delegate
= _ExeDelegate(self
, self
._test
_instance
.exe
)
165 def TestPackage(self
):
166 return self
._test
_instance
.suite
171 def individual_device_set_up(dev
, host_device_tuples
):
173 self
._delegate
.Install(dev
)
175 # Push data dependencies.
176 external_storage
= dev
.GetExternalStoragePath()
177 host_device_tuples
= [
178 (h
, d
if d
is not None else external_storage
)
179 for h
, d
in host_device_tuples
]
180 dev
.PushChangedFiles(host_device_tuples
)
182 self
._servers
[str(dev
)] = []
183 if self
.TestPackage() in _SUITE_REQUIRES_TEST_SERVER_SPAWNER
:
184 self
._servers
[str(dev
)].append(
185 local_test_server_spawner
.LocalTestServerSpawner(
186 ports
.AllocateTestServerPort(), dev
, self
.GetTool(dev
)))
188 for s
in self
._servers
[str(dev
)]:
191 self
._env
.parallel_devices
.pMap(individual_device_set_up
,
192 self
._test
_instance
.GetDataDependencies())
195 def _ShouldShard(self
):
199 def _CreateShards(self
, tests
):
200 device_count
= len(self
._env
.devices
)
202 for i
in xrange(0, device_count
):
203 unbounded_shard
= tests
[i
::device_count
]
204 shards
+= [unbounded_shard
[j
:j
+_MAX_SHARD_SIZE
]
205 for j
in xrange(0, len(unbounded_shard
), _MAX_SHARD_SIZE
)]
210 tests
= self
._delegate
.Run(
211 None, self
._env
.devices
[0], flags
='--gtest_list_tests')
212 tests
= gtest_test_instance
.ParseGTestListTests(tests
)
213 tests
= self
._test
_instance
.FilterTests(tests
)
217 def _RunTest(self
, device
, test
):
219 timeout
= 900 * self
.GetTool(device
).GetTimeoutScale()
220 output
= self
._delegate
.Run(
221 test
, device
, timeout
=timeout
, retries
=0)
222 for s
in self
._servers
[str(device
)]:
224 if self
._test
_instance
.app_files
:
225 self
._delegate
.PullAppFiles(device
, self
._test
_instance
.app_files
,
226 self
._test
_instance
.app_file_dir
)
227 self
._delegate
.Clear(device
)
230 # TODO(jbudorick): Transition test scripts away from parsing stdout.
231 results
= self
._test
_instance
.ParseGTestOutput(output
)
236 def individual_device_tear_down(dev
):
237 for s
in self
._servers
[str(dev
)]:
240 self
._env
.parallel_devices
.pMap(individual_device_tear_down
)