blob: 4daabb7985b5bc19daf737be74f07e9b19c93430 [file] [log] [blame]
Aaron Holden62198022016-04-18 16:37:28 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.compatibility.testtype;
18
19import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
20import com.android.compatibility.common.util.AbiUtils;
21import com.android.ddmlib.IShellOutputReceiver;
22import com.android.ddmlib.Log;
23import com.android.ddmlib.Log.LogLevel;
24import com.android.ddmlib.MultiLineReceiver;
25import com.android.ddmlib.testrunner.TestIdentifier;
26import com.android.tradefed.build.IBuildInfo;
27import com.android.tradefed.config.Option;
28import com.android.tradefed.config.OptionCopier;
29import com.android.tradefed.device.DeviceNotAvailableException;
30import com.android.tradefed.device.ITestDevice;
31import com.android.tradefed.result.ITestInvocationListener;
32import com.android.tradefed.testtype.IAbi;
33import com.android.tradefed.testtype.IAbiReceiver;
34import com.android.tradefed.testtype.IBuildReceiver;
35import com.android.tradefed.testtype.IDeviceTest;
36import com.android.tradefed.testtype.IRemoteTest;
37import com.android.tradefed.testtype.IRuntimeHintProvider;
38import com.android.tradefed.testtype.IShardableTest;
39import com.android.tradefed.testtype.ITestCollector;
40import com.android.tradefed.testtype.ITestFilterReceiver;
41import com.android.tradefed.util.ArrayUtil;
42import com.android.tradefed.util.TimeVal;
43import com.google.common.base.Splitter;
44
45import vogar.ExpectationStore;
46import vogar.ModeId;
47
48import java.io.File;
49import java.io.FilenameFilter;
50import java.io.IOException;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.Arrays;
54import java.util.Collection;
55import java.util.Collections;
56import java.util.HashSet;
57import java.util.LinkedHashSet;
58import java.util.List;
59import java.util.Set;
60import java.util.concurrent.TimeUnit;
61
62/**
63 * A wrapper to run tests against Dalvik.
64 */
65public class DalvikTest implements IAbiReceiver, IBuildReceiver, IDeviceTest, IRemoteTest,
66 IRuntimeHintProvider, IShardableTest, ITestCollector, ITestFilterReceiver {
67
68 private static final String TAG = DalvikTest.class.getSimpleName();
69
70 /**
71 * TEST_PACKAGES is a Set containing the names of packages on the classpath known to contain
72 * tests to be run under DalvikTest. The TEST_PACKAGES set is used to shard DalvikTest into
73 * multiple DalvikTests, each responsible for running one of these packages' tests.
74 */
75 private static final Set<String> TEST_PACKAGES = new HashSet<>();
76 private static final String JDWP_PACKAGE_BASE = "org.apache.harmony.jpda.tests.jdwp.%s";
77 static {
78 // Though uppercase, these are package names, not class names
79 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ArrayReference"));
80 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ArrayType"));
81 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassLoaderReference"));
82 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassObjectReference"));
83 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassType"));
84 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "DebuggerOnDemand"));
85 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Deoptimization"));
86 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "EventModifiers"));
87 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Events"));
88 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "InterfaceType"));
89 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Method"));
90 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "MultiSession"));
91 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ObjectReference"));
92 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ReferenceType"));
93 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "StackFrame"));
94 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "StringReference"));
95 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ThreadGroupReference"));
96 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ThreadReference"));
97 TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "VirtualMachine"));
98 }
99
100 private static final String EXPECTATIONS_EXT = ".expectations";
101 // Command to run the VM, args are bitness, classpath, dalvik-args, abi, runner-args,
102 // include and exclude filters, and exclude filters file.
103 private static final String COMMAND = "dalvikvm%s -classpath %s %s "
104 + "com.android.compatibility.dalvik.DalvikTestRunner --abi=%s %s %s %s %s %s %s";
105 private static final String INCLUDE_FILE = "/data/local/tmp/ctsjdwp/includes";
106 private static final String EXCLUDE_FILE = "/data/local/tmp/ctsjdwp/excludes";
107 private static String START_RUN = "start-run";
108 private static String END_RUN = "end-run";
109 private static String START_TEST = "start-test";
110 private static String END_TEST = "end-test";
111 private static String FAILURE = "failure";
112
113 @Option(name = "run-name", description = "The name to use when reporting results")
114 private String mRunName;
115
116 @Option(name = "classpath", description = "Holds the paths to search when loading tests")
117 private List<String> mClasspath = new ArrayList<>();
118
119 @Option(name = "dalvik-arg", description = "Holds arguments to pass to Dalvik")
120 private List<String> mDalvikArgs = new ArrayList<>();
121
122 @Option(name = "runner-arg",
123 description = "Holds arguments to pass to the device-side test runner")
124 private List<String> mRunnerArgs = new ArrayList<>();
125
126 @Option(name = "include-filter",
127 description = "The include filters of the test name to run.")
128 private List<String> mIncludeFilters = new ArrayList<>();
129
130 @Option(name = "exclude-filter",
131 description = "The exclude filters of the test name to run.")
132 private List<String> mExcludeFilters = new ArrayList<>();
133
134 @Option(name = "runtime-hint",
135 isTimeVal = true,
136 description="The hint about the test's runtime.")
137 private long mRuntimeHint = 60000;// 1 minute
138
139 @Option(name = "known-failures",
140 description = "Comma-separated list of files specifying known-failures to be skipped")
141 private String mKnownFailures;
142
143 @Option(name = "collect-tests-only",
144 description = "Only invoke the instrumentation to collect list of applicable test "
145 + "cases. All test run callbacks will be triggered, but test execution will "
146 + "not be actually carried out.")
147 private boolean mCollectTestsOnly = false;
148
149 @Option(name = "per-test-timeout",
150 description = "The maximum amount of time during which the DalvikTestRunner may "
151 + "yield no output. Because the runner outputs results for each test, this "
152 + "is essentially a per-test timeout")
153 private long mPerTestTimeout = 10; // 10 minutes
154
155 private IAbi mAbi;
156 private CompatibilityBuildHelper mBuildHelper;
157 private ITestDevice mDevice;
158
159 /**
160 * {@inheritDoc}
161 */
162 @Override
163 public void setAbi(IAbi abi) {
164 mAbi = abi;
165 }
166
167 /**
168 * {@inheritDoc}
169 */
170 @Override
171 public void setBuild(IBuildInfo build) {
172 mBuildHelper = new CompatibilityBuildHelper(build);
173 }
174
175 /**
176 * {@inheritDoc}
177 */
178 @Override
179 public void setDevice(ITestDevice device) {
180 mDevice = device;
181 }
182
183 /**
184 * {@inheritDoc}
185 */
186 @Override
187 public ITestDevice getDevice() {
188 return mDevice;
189 }
190
191 /**
192 * {@inheritDoc}
193 */
194 @Override
195 public void addIncludeFilter(String filter) {
196 mIncludeFilters.add(filter);
197 }
198
199 /**
200 * {@inheritDoc}
201 */
202 @Override
203 public void addAllIncludeFilters(List<String> filters) {
204 mIncludeFilters.addAll(filters);
205 }
206
207 /**
208 * {@inheritDoc}
209 */
210 @Override
211 public void addExcludeFilter(String filter) {
212 mExcludeFilters.add(filter);
213 }
214
215 /**
216 * {@inheritDoc}
217 */
218 @Override
219 public void addAllExcludeFilters(List<String> filters) {
220 mExcludeFilters.addAll(filters);
221 }
222
223 /**
224 * {@inheritDoc}
225 */
226 @Override
227 public long getRuntimeHint() {
228 return mRuntimeHint;
229 }
230
231 /**
232 * {@inheritDoc}
233 */
234 @Override
235 public void setCollectTestsOnly(boolean shouldCollectTest) {
236 mCollectTestsOnly = shouldCollectTest;
237 }
238
239 /**
240 * {@inheritDoc}
241 */
242 @Override
243 public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException {
244 String abiName = mAbi.getName();
245 String bitness = AbiUtils.getBitness(abiName);
246
247 File temp = null;
248 PrintWriter out = null;
249 try {
250 Set<File> expectationFiles = new HashSet<>();
251 for (File f : mBuildHelper.getTestsDir().listFiles(
252 new ExpectationFileFilter(mRunName))) {
253 expectationFiles.add(f);
254 }
255 ExpectationStore store = ExpectationStore.parse(expectationFiles, ModeId.DEVICE);
256
257 ExpectationStore resourceStore = null;
258 if (mKnownFailures != null) {
259 Splitter splitter = Splitter.on(',').trimResults();
260 Set<String> knownFailuresFileList =
261 new LinkedHashSet<>(splitter.splitToList(mKnownFailures));
262 resourceStore = ExpectationStore.parseResources(
263 getClass(), knownFailuresFileList, ModeId.DEVICE);
264 }
265
266 // Work around because there are to many expectations to pass via command line
267 temp = File.createTempFile("excludes", "txt");
268 out = new PrintWriter(temp);
269 for (String exclude : store.getAllFailures().keySet()) {
270 out.println(exclude);
271 }
272 for (String exclude : store.getAllOutComes().keySet()) {
273 out.println(exclude);
274 }
275 // Add expectations from resources
276 if (resourceStore != null) {
277 for (String exclude : resourceStore.getAllFailures().keySet()) {
278 out.println(exclude);
279 }
280 for (String exclude : resourceStore.getAllOutComes().keySet()) {
281 out.println(exclude);
282 }
283 }
284 out.flush();
285 if (!mDevice.pushFile(temp, EXCLUDE_FILE)) {
286 Log.logAndDisplay(LogLevel.ERROR, TAG, "Couldn't push file: " + temp);
287 }
288 } catch (IOException e) {
289 throw new RuntimeException("Failed to parse expectations");
290 } finally {
291 if (out != null) {
292 out.close();
293 }
294 temp.delete();
295 }
296
297 // Create command
298 mDalvikArgs.add("-Duser.name=shell");
299 mDalvikArgs.add("-Duser.language=en");
300 mDalvikArgs.add("-Duser.region=US");
301 mDalvikArgs.add("-Xcheck:jni");
302 mDalvikArgs.add("-Xjnigreflimit:2000");
303
304 String dalvikArgs = ArrayUtil.join(" ", mDalvikArgs);
305 dalvikArgs = dalvikArgs.replace("|#ABI#|", bitness);
306
307 String runnerArgs = ArrayUtil.join(" ", mRunnerArgs);
308 // Filters
309 StringBuilder includeFilters = new StringBuilder();
310 if (!mIncludeFilters.isEmpty()) {
311 includeFilters.append("--include-filter=");
312 includeFilters.append(ArrayUtil.join(",", mIncludeFilters));
313 }
314 StringBuilder excludeFilters = new StringBuilder();
315 if (!mExcludeFilters.isEmpty()) {
316 excludeFilters.append("--exclude-filter=");
317 excludeFilters.append(ArrayUtil.join(",", mExcludeFilters));
318 }
319 // Filter files
320 String includeFile = String.format("--include-filter-file=%s", INCLUDE_FILE);
321 String excludeFile = String.format("--exclude-filter-file=%s", EXCLUDE_FILE);
322 // Communicate with DalvikTestRunner if tests should only be collected
323 String collectTestsOnlyString = (mCollectTestsOnly) ? "--collect-tests-only" : "";
324 final String command = String.format(COMMAND, bitness,
325 ArrayUtil.join(File.pathSeparator, mClasspath),
326 dalvikArgs, abiName, runnerArgs,
327 includeFilters, excludeFilters, includeFile, excludeFile, collectTestsOnlyString);
328 IShellOutputReceiver receiver = new MultiLineReceiver() {
329 private TestIdentifier test;
330
331 @Override
332 public boolean isCancelled() {
333 return false;
334 }
335
336 @Override
337 public void processNewLines(String[] lines) {
338 for (String line : lines) {
339 String[] parts = line.split(":", 2);
340 String tag = parts[0];
341 if (tag.equals(START_RUN)) {
342 listener.testRunStarted(mRunName, Integer.parseInt(parts[1]));
343 Log.logAndDisplay(LogLevel.INFO, TAG, command);
344 Log.logAndDisplay(LogLevel.INFO, TAG, line);
345 } else if (tag.equals(END_RUN)) {
346 listener.testRunEnded(Integer.parseInt(parts[1]),
347 Collections.<String, String>emptyMap());
348 Log.logAndDisplay(LogLevel.INFO, TAG, line);
349 } else if (tag.equals(START_TEST)) {
350 test = getTestIdentifier(parts[1]);
351 listener.testStarted(test);
352 } else if (tag.equals(FAILURE)) {
353 listener.testFailed(test, parts[1]);
354 } else if (tag.equals(END_TEST)) {
355 listener.testEnded(getTestIdentifier(parts[1]),
356 Collections.<String, String>emptyMap());
357 } else {
358 Log.logAndDisplay(LogLevel.INFO, TAG, line);
359 }
360 }
361 }
362
363 private TestIdentifier getTestIdentifier(String name) {
364 String[] parts = name.split("#");
365 String className = parts[0];
366 String testName = "";
367 if (parts.length > 1) {
368 testName = parts[1];
369 }
370 return new TestIdentifier(className, testName);
371 }
372
373 };
374 mDevice.executeShellCommand(command, receiver, mPerTestTimeout, TimeUnit.MINUTES, 1);
375 }
376
377 /**
378 * {@inheritDoc}
379 */
380 @Override
381 public Collection<IRemoteTest> split() {
382 List<IRemoteTest> shards = new ArrayList<>();
383 // A DalvikTest to run any tests not contained in packages from TEST_PACKAGES, may be empty
384 DalvikTest catchAll = new DalvikTest();
385 OptionCopier.copyOptionsNoThrow(this, catchAll);
386 shards.add(catchAll);
387 // estimate catchAll's runtime to be that of a single package in TEST_PACKAGES
388 long runtimeHint = mRuntimeHint / TEST_PACKAGES.size();
389 catchAll.mRuntimeHint = runtimeHint;
390 for (String packageName: TEST_PACKAGES) {
391 catchAll.addExcludeFilter(packageName);
392 // create one shard for package 'packageName'
393 DalvikTest test = new DalvikTest();
394 OptionCopier.copyOptionsNoThrow(this, test);
395 test.addIncludeFilter(packageName);
396 test.mRuntimeHint = runtimeHint;
397 shards.add(test);
398 }
399 // return a shard for each package in TEST_PACKAGE, plus a shard for any other tests
400 return shards;
401 }
402
403 /**
404 * A {@link FilenameFilter} to find all the expectation files in a directory.
405 */
406 public static class ExpectationFileFilter implements FilenameFilter {
407
408 private String mName;
409
410 public ExpectationFileFilter(String name) {
411 mName = name;
412 }
413 /**
414 * {@inheritDoc}
415 */
416 @Override
417 public boolean accept(File dir, String name) {
418 return name.startsWith(mName) && name.endsWith(EXPECTATIONS_EXT);
419 }
420 }
421}