1 # Copyright 2015 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.
9 from devil
.android
import device_errors
10 from pylib
import flag_changer
11 from pylib
.base
import base_test_result
12 from pylib
.local
.device
import local_device_test_run
15 TIMEOUT_ANNOTATIONS
= [
16 ('Manual', 10 * 60 * 60),
17 ('IntegrationTest', 30 * 60),
18 ('External', 10 * 60),
19 ('EnormousTest', 10 * 60),
20 ('LargeTest', 5 * 60),
21 ('MediumTest', 3 * 60),
22 ('SmallTest', 1 * 60),
26 # TODO(jbudorick): Make this private once the instrumentation test_runner is
28 def DidPackageCrashOnDevice(package_name
, device
):
29 # Dismiss any error dialogs. Limit the number in case we have an error
30 # loop or we are failing to dismiss.
33 package
= device
.DismissCrashDialogIfNeeded()
36 # Assume test package convention of ".test" suffix
37 if package
in package_name
:
39 except device_errors
.CommandFailedError
:
40 logging
.exception('Error while attempting to dismiss crash dialog.')
44 _CURRENT_FOCUS_CRASH_RE
= re
.compile(
45 r
'\s*mCurrentFocus.*Application (Error|Not Responding): (\S+)}')
48 class LocalDeviceInstrumentationTestRun(
49 local_device_test_run
.LocalDeviceTestRun
):
50 def __init__(self
, env
, test_instance
):
51 super(LocalDeviceInstrumentationTestRun
, self
).__init
__(env
, test_instance
)
52 self
._flag
_changers
= {}
54 def TestPackage(self
):
58 def substitute_external_storage(d
, external_storage
):
60 return external_storage
61 elif isinstance(d
, list):
62 return '/'.join(p
if p
else external_storage
for p
in d
)
66 def individual_device_set_up(dev
, host_device_tuples
):
67 dev
.Install(self
._test
_instance
.apk_under_test
,
68 permissions
=self
._test
_instance
.apk_under_test_permissions
)
69 dev
.Install(self
._test
_instance
.test_apk
,
70 permissions
=self
._test
_instance
.test_permissions
)
72 external_storage
= dev
.GetExternalStoragePath()
73 host_device_tuples
= [
74 (h
, substitute_external_storage(d
, external_storage
))
75 for h
, d
in host_device_tuples
]
76 logging
.info('instrumentation data deps:')
77 for h
, d
in host_device_tuples
:
78 logging
.info('%r -> %r', h
, d
)
79 dev
.PushChangedFiles(host_device_tuples
)
80 if self
._test
_instance
.flags
:
81 if not self
._test
_instance
.package_info
:
82 logging
.error("Couldn't set flags: no package info")
83 elif not self
._test
_instance
.package_info
.cmdline_file
:
84 logging
.error("Couldn't set flags: no cmdline_file")
86 self
._flag
_changers
[str(dev
)] = flag_changer
.FlagChanger(
87 dev
, self
._test
_instance
.package_info
.cmdline_file
)
88 logging
.debug('Attempting to set flags: %r',
89 self
._test
_instance
.flags
)
90 self
._flag
_changers
[str(dev
)].AddFlags(self
._test
_instance
.flags
)
92 self
._env
.parallel_devices
.pMap(
93 individual_device_set_up
,
94 self
._test
_instance
.GetDataDependencies())
97 def individual_device_tear_down(dev
):
98 if str(dev
) in self
._flag
_changers
:
99 self
._flag
_changers
[str(dev
)].Restore()
101 self
._env
.parallel_devices
.pMap(individual_device_tear_down
)
104 def _CreateShards(self
, tests
):
109 return self
._test
_instance
.GetTests()
112 def _GetTestName(self
, test
):
113 return '%s#%s' % (test
['class'], test
['method'])
116 def _RunTest(self
, device
, test
):
117 extras
= self
._test
_instance
.GetHttpServerEnvironmentVars()
119 if isinstance(test
, list):
120 if not self
._test
_instance
.driver_apk
:
121 raise Exception('driver_apk does not exist. '
122 'Please build it and try again.')
124 def name_and_timeout(t
):
125 n
= self
._GetTestName
(t
)
126 i
= self
._GetTimeoutFromAnnotations
(t
['annotations'], n
)
129 test_names
, timeouts
= zip(*(name_and_timeout(t
) for t
in test
))
131 test_name
= ','.join(test_names
)
133 self
._test
_instance
.driver_package
,
134 self
._test
_instance
.driver_name
)
136 self
._test
_instance
.GetDriverEnvironmentVars(
137 test_list
=test_names
))
138 timeout
= sum(timeouts
)
140 test_name
= self
._GetTestName
(test
)
142 self
._test
_instance
.test_package
, self
._test
_instance
.test_runner
)
143 extras
['class'] = test_name
144 timeout
= self
._GetTimeoutFromAnnotations
(test
['annotations'], test_name
)
146 logging
.info('preparing to run %s: %s', test_name
, test
)
148 time_ms
= lambda: int(time
.time() * 1e3
)
150 output
= device
.StartInstrumentation(
151 target
, raw
=True, extras
=extras
, timeout
=timeout
, retries
=0)
152 duration_ms
= time_ms() - start_ms
154 # TODO(jbudorick): Make instrumentation tests output a JSON so this
155 # doesn't have to parse the output.
156 logging
.debug('output from %s:', test_name
)
158 logging
.debug(' %s', l
)
160 result_code
, result_bundle
, statuses
= (
161 self
._test
_instance
.ParseAmInstrumentRawOutput(output
))
162 results
= self
._test
_instance
.GenerateTestResults(
163 result_code
, result_bundle
, statuses
, start_ms
, duration_ms
)
164 if DidPackageCrashOnDevice(self
._test
_instance
.test_package
, device
):
166 if r
.GetType() == base_test_result
.ResultType
.UNKNOWN
:
167 r
.SetType(base_test_result
.ResultType
.CRASH
)
171 def _ShouldShard(self
):
175 def _GetTimeoutFromAnnotations(annotations
, test_name
):
176 for k
, v
in TIMEOUT_ANNOTATIONS
:
181 logging
.warning('Using default 1 minute timeout for %s', test_name
)
185 scale
= int(annotations
.get('TimeoutScale', 1))
186 except ValueError as e
:
187 logging
.warning("Non-integer value of TimeoutScale ignored. (%s)", str(e
))