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

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: Address marcus' issues. 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 _LINKER_TAG = 'ContentLinkerTest'
68 _PACKAGE_NAME='org.chromium.content_linker_test_apk'
69 _ACTIVITY_NAME='.ContentLinkerTestActivity'
70 _COMMAND_LINE_FILE='/data/local/tmp/content-linker-test-command-line'
71
72 # Logcat filters used during each test. Only the 'chromium' one is really
73 # needed, but the logs are added to the TestResult in case of error, and
74 # it is handy to have the 'content_android_linker' ones as well when
75 # troubleshooting.
76 _LOGCAT_FILTERS = [ '*:s', 'chromium:v', 'content_android_linker:v' ]
77
78
79 def _CheckPrefixedTestStatus(logcat, prefix):
frankf 2013/10/02 18:16:02 Are you not using regex in case logcat is very lar
digit1 2013/10/03 09:16:00 The logcat won't be very large, due to the filters
80 """Parse the content of |logcat| for some text that begins
81 with |prefix| and is followed by 'FAIL' or 'SUCCESS'.
82
83 Args:
84 logcat: A string to parse. Can include line separators.
85 prefix: The status prefix
86
87 Returns:
88 A tuple, result[0] is True is a line was found, then
frankf 2013/10/02 18:16:02 is -> if
digit1 2013/10/03 09:16:00 Done.
89 result[1] will be True for 'SUCCESS' and False for 'Fail'
90 """
91 start = 0
92 while True:
93 n = logcat.find(prefix, start)
94 if n < 0:
95 return (False, None)
96
97 n += len(prefix)
98 if logcat.find('FAIL', n) == n:
99 return (True, False)
100 if logcat.find('SUCCESS', n) == n:
101 return (True, True)
102
103 start = n
104
105
106 def _CheckLinkerTestStatus(logcat):
107 """Parse the content of |logcat| and checks for both a browser and
108 renderer status line.
109
110 Args:
111 logcat: A string to parse. Can include line separators.
112
113 Returns:
114 A tuple, result[0] is True if there is a complete match, then
115 result[1] and result[2] will be True or False to reflect the
116 test status for the browser and renderer processes, respectively.
117 """
118 browser_found, browser_success = _CheckPrefixedTestStatus(
119 logcat, 'BROWSER_LINKER_TEST: ')
120 renderer_found, renderer_success = _CheckPrefixedTestStatus(
121 logcat, 'RENDERER_LINKER_TEST: ')
122
123 if browser_found and renderer_found:
124 return (True, browser_success, renderer_success)
125 return (False, None, None)
frankf 2013/10/02 18:16:02 duplicate of line 128
digit1 2013/10/03 09:16:00 Done.
126
127 # Didn't find anything.
128 return (False, None, None)
129
130
131 def _CreateCommandLineFileOnDevice(adb, cmd_line):
frankf 2013/10/02 18:16:02 you can use flag_changer.py
digit1 2013/10/03 09:16:00 Done.
132 """Create a command-line file on the device.
133 Args:
134 adb: An AndroidCommands instance to communicate with the device.
135 cmd_line: The command-line as a string.
136 """
137 command_line_file = tempfile.NamedTemporaryFile()
138 command_line_file.write(cmd_line)
139 command_line_file.flush()
140 adb.PushIfNeeded(command_line_file.name, _COMMAND_LINE_FILE)
141
142
143 class LinkerTestCase(object):
144 """Base class for linker test cases."""
145
146 def __init__(self, test_name, is_low_memory=False):
147 """Create a test case initialized to run |test_name|.
148
149 Args:
150 test_name: The name of the method to run as the test.
151 is_low_memory: True to simulate a low-memory device, False otherwise.
152 """
153 self.test_name = test_name
154 class_name = self.__class__.__name__
155 self.qualified_name = '%s.%s' % (class_name, self.test_name)
156 # Use tagged_name when creating results, so that we can identify linker
157 # tests in the overall results.
frankf 2013/10/02 18:16:02 isn't class_name sufficient for this. Note the lin
digit1 2013/10/03 09:16:00 The class name itself won't be sufficient, but sel
158 self.tagged_name = '%s_%s' % (_LINKER_TAG, self.test_name)
159 self.is_low_memory = is_low_memory
160
161 def Run(self, device):
162 margin = 8
163 print "[ %-*s ] %s" % (margin, 'RUN', self.tagged_name)
frankf 2013/10/02 18:16:02 single quotes for consistancy
digit1 2013/10/03 09:16:00 Done.
164 logging.info('Running linker test: %s', self.tagged_name)
165 adb = android_commands.AndroidCommands(device)
166
167 # 1. Write command-line file with appropriate options.
168 command_line = ''
169 if self.is_low_memory:
170 command_line = '--low-memory-device'
171 _CreateCommandLineFileOnDevice(adb, command_line)
172
173 # 2. Start recording logcat with appropriate filters.
174 adb.StartRecordingLogcat(clear=True, filters=_LOGCAT_FILTERS)
175
176 try:
177 # 3. Force-start activity.
178 adb.StartActivity(package=_PACKAGE_NAME,
179 activity=_ACTIVITY_NAME,
180 force_stop=True)
181
182 # 4. Wait up to 30 seconds until the linker test status is in the logcat.
183 max_tries = 30
184 num_tries = 0
185 found = False
186 logcat = None
187 while num_tries < max_tries:
188 time.sleep(1)
189 num_tries += 1
190 found, browser_ok, renderer_ok = _CheckLinkerTestStatus(
191 adb.GetCurrentRecordedLogcat())
192 if found:
193 break
194
195 finally:
196 # Ensure the ADB polling process is always killed when
197 # the script is interrupted by the user with Ctrl-C.
198 logs = adb.StopRecordingLogcat()
199
200 results = base_test_result.TestRunResults()
201
202 if num_tries >= max_tries:
203 # Timeout
204 print '[ %*s ] %s' % (margin, 'TIMEOUT', self.tagged_name)
205 results.AddResult(
206 base_test_result.BaseTestResult(
207 self.test_name,
208 base_test_result.ResultType.TIMEOUT,
209 logs))
210 elif browser_ok and renderer_ok:
211 # Passed
212 logging.info(
213 'Logcat start ---------------------------------\n%s' +
214 'Logcat end -----------------------------------', logs)
215 print '[ %*s ] %s' % (margin, 'OK', self.tagged_name)
216 results.AddResult(
217 base_test_result.BaseTestResult(
218 self.test_name,
219 base_test_result.ResultType.PASS))
220 else:
221 print '[ %*s ] %s' % (margin, 'FAILED', self.tagged_name)
222 # Failed
223 results.AddResult(
224 base_test_result.BaseTestResult(
225 self.test_name,
226 base_test_result.ResultType.FAIL,
227 logs))
228
229 return results
230
231 def __str__(self):
232 return self.tagged_name
233
234 def __repr__(self):
235 return self.tagged_name
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698