Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: build/android/pylib/linker/test_case.py

Issue 25525003: Add new Android test runner command to handle linker tests. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Trivial rebase + reupload Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # Copyright 2013 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.
4
5 """Base class for linker-specific test cases.
6
7 The custom dynamic linker can only be tested through a custom test case
8 for various technical reasons:
9
10 - It's an 'invisible feature', i.e. it doesn't expose a new API or
11 behaviour, all it does is save RAM when loading native libraries.
12
13 - Checking that it works correctly requires several things that do not
14 fit the existing GTest-based and instrumentation-based tests:
15
16 - Native test code needs to be run in both the browser and renderer
17 process at the same time just after loading native libraries, in
18 a completely asynchronous way.
19
20 - Each test case requires restarting a whole new application process
21 with a different command-line.
22
23 - Enabling test support in the Linker code requires building a special
24 APK with a flag to activate special test-only support code in the
25 Linker code itself.
26
27 Host-driven tests have also been tried, but since they're really
28 sub-classes of instrumentation tests, they didn't work well either.
29
30 To build and run the linker tests, do the following:
31
32 ninja -C out/Debug content_linker_test_apk
33 build/android/test_runner.py linker
34
35 The core of the checks performed here are pretty simple:
36
37 - Clear the logcat and start recording with an appropriate set of filters.
38 - Create the command-line appropriate for the test-case.
39 - Start the activity (always forcing a cold start).
40 - Every second, look at the current content of the filtered logcat lines
41 and look for instances of the following:
42
43 BROWSER_LINKER_TEST: <status>
44 RENDERER_LINKER_TEST: <status>
45
46 where <status> can be either FAIL or SUCCESS. These lines can appear
47 in any order in the logcat. Once both browser and renderer status are
48 found, stop the loop. Otherwise timeout after 30 seconds.
49
50 Note that there can be other lines beginning with BROWSER_LINKER_TEST:
51 and RENDERER_LINKER_TEST:, but are not followed by a <status> code.
52
53 - The test case passes if the <status> for both the browser and renderer
54 process are SUCCESS. Otherwise its a fail.
55 """
56
57 import logging
58 import os
59 import StringIO
60 import subprocess
61 import tempfile
62 import time
63
64 from pylib import android_commands
65 from pylib.base import base_test_result
66
67 # aka the parent of com.google.android
68 BASE_ROOT = 'src' + os.sep
bulach 2013/10/02 11:16:50 is this equivalent of: from pylib import constants
digit1 2013/10/02 14:01:52 Actually, this came from pylib/host_driven/test_ca
69
70 _LINKER_TAG = 'ContentLinkerTest'
71 _PACKAGE_NAME='org.chromium.content_linker_test_apk'
72 _ACTIVITY_NAME='.ContentLinkerTestActivity'
73 _COMMAND_LINE_FILE='/data/local/tmp/content-linker-test-command-line'
74
75 # Logcat filters used during each test. Only the 'chromium' one is really
76 # needed, but the logs are added to the TestResult in case of error, and
77 # it is handy to have the 'content_android_linker' ones as well when
78 # troubleshooting.
79 _LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'content_android_linker:v' ]
80
81 #_LOGCAT_FILTERS = [ '*:v' ] ## DEBUG
82
83
84 def _CheckPrefixedTestStatus(logcat, prefix):
85 """Parse the content of |logcat| for some text that begins
86 with |prefix| and is followed by 'FAIL' or 'SUCCESS'.
87
88 Args:
bulach 2013/10/02 11:16:50 truly nit: the Args: and Returns: are normally ali
digit1 2013/10/02 14:01:52 Oops, I've fixed this. Thanks.
89 logcat: A string to parse. Can include line separators.
90 prefix: The status prefix
91
92 Returns:
93 A tuple, result[0] is True is a line was found, then
94 result[1] will be True for 'SUCCESS' and False for 'Fail'
95 """
96 start = 0
97 while True:
98 n = logcat.find(prefix, start)
99 if n < 0:
100 return (False, None)
101
102 n += len(prefix)
103 if logcat.find('FAIL', n) == n:
104 return (True, False)
105 if logcat.find('SUCCESS', n) == n:
106 return (True, True)
107
108 start = n
109
110
111 def _CheckLinkerTestStatus(logcat):
112 """Parse the content of |logcat| and checks for both a browser and
113 renderer status line.
114
115 Args:
116 logcat: A string to parse. Can include line separators.
117
118 Returns:
119 A tuple, result[0] is True is there is a complete match, then
bulach 2013/10/02 11:16:50 nit: s/is there is/if there is/
digit1 2013/10/02 14:01:52 Done.
120 result[1] and result[2] will be True or False to reflect the
121 test status for the browser and renderer processes, respectively.
122 """
123 browser_found, browser_success = _CheckPrefixedTestStatus(
124 logcat, 'BROWSER_LINKER_TEST: ')
125 renderer_found, renderer_success = _CheckPrefixedTestStatus(
126 logcat, 'RENDERER_LINKER_TEST: ')
127
128 if browser_found and renderer_found:
129 return (True, browser_success, renderer_success)
130 return (False, None, None)
131
132 # Didn't find anything.
133 return (False, None)
bulach 2013/10/02 11:16:50 nit: , None so it always returns a 3-ary tuple?
digit1 2013/10/02 14:01:52 Done.
134
135
136 def _CreateCommandLineFileOnDevice(adb, cmd_line):
137 """Create a command-line file on the device.
138 Args:
139 adb: An AndroidCommands instance to communicate with the device.
140 cmd_line: The command-line as a string.
141 """
142 command_line_file = tempfile.NamedTemporaryFile()
143 command_line_file.write(cmd_line)
144 command_line_file.flush()
145 adb.PushIfNeeded(command_line_file.name, _COMMAND_LINE_FILE)
146
147
148 class LinkerTestCase(object):
149 """Base class for linker test cases."""
150
151 def __init__(self, test_name, is_low_memory=False):
152 """Create a test case initialized to run |test_name|.
153
154 Args:
155 test_name: The name of the method to run as the test.
156 is_low_memory: True to simulate a low-memory device, False otherwise.
157 """
158 self.test_name = test_name
159 class_name = self.__class__.__name__
160 self.qualified_name = '%s.%s' % (class_name, self.test_name)
161 # Use tagged_name when creating results, so that we can identify linker
162 # tests in the overall results.
163 self.tagged_name = '%s_%s' % (_LINKER_TAG, self.test_name)
164 self.is_low_memory = is_low_memory
165
166 def Run(self, device):
167 margin = 8
168 print "[ %-*s ] %s" % (margin, "RUN", self.tagged_name)
bulach 2013/10/02 11:16:50 nit: s/"/'/ everywhere
digit1 2013/10/02 14:01:52 Done.
169 logging.info('Running linker test: %s', self.tagged_name)
170 adb = android_commands.AndroidCommands(device)
171
172 # 1. Write command-line file with appropriate options.
173 command_line = ''
174 if self.is_low_memory:
175 command_line = '--low-memory-device'
176 _CreateCommandLineFileOnDevice(adb, command_line)
177
178 # 2. Start recording logcat with appropriate filters.
179 adb.StartRecordingLogcat(clear=True, filters=_LOGCAT_FILTERS)
180
181 try:
182 # 3. Force-start activity.
183 adb.StartActivity(package=_PACKAGE_NAME,
184 activity=_ACTIVITY_NAME,
185 force_stop=True)
186
187 # 4. Wait up to 30 seconds until the linker test status is in the logcat.
188 max_tries = 30
189 num_tries = 0
190 found = False
191 logcat = None
192 while num_tries < max_tries:
193 time.sleep(1)
194 num_tries += 1
195 found, browser_ok, renderer_ok = _CheckLinkerTestStatus(
196 adb.GetCurrentRecordedLogcat())
bulach 2013/10/02 11:16:50 hmm, would adb.StartMonitoringLogCat() (before Sta
digit1 2013/10/02 14:01:52 adb.WaitForLogMatch() isn't a good match here beca
197 if found:
198 break
199
200 finally:
201 # Ensure the ADB polling process is always killed when
202 # the script is interrupted by the user with Ctrl-C.
203 logs = adb.StopRecordingLogcat()
204
205 results = base_test_result.TestRunResults()
206
207 if num_tries >= max_tries:
208 # Timeout
209 print "[ %*s ] %s" % (margin, "TIMEOUT", self.tagged_name)
210 results.AddResult(
211 base_test_result.BaseTestResult(
212 self.test_name,
213 base_test_result.ResultType.TIMEOUT,
214 logs))
215 elif browser_ok and renderer_ok:
216 # Passed
217 logging.info(
218 "Logcat start ---------------------------------\n%s" + \
219 "Logcat end -----------------------------------", logs)
220 print "[ %*s ] %s" % (margin, "OK", self.tagged_name)
221 results.AddResult(
222 base_test_result.BaseTestResult(
223 self.test_name,
224 base_test_result.ResultType.PASS))
225 else:
226 print "[ %*s ] %s" % (margin, "FAILED", self.tagged_name)
227 # Failed
228 results.AddResult(
229 base_test_result.BaseTestResult(
230 self.test_name,
231 base_test_result.ResultType.FAIL,
232 logs))
233
234 return results
235
236 def __str__(self):
237 return self.tagged_name
238
239 def __repr__(self):
240 return self.tagged_name
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698