1 /*
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 
17 package com.android.compatibility.testtype;
18 
19 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
20 import com.android.ddmlib.IShellOutputReceiver;
21 import com.android.ddmlib.Log;
22 import com.android.ddmlib.Log.LogLevel;
23 import com.android.ddmlib.MultiLineReceiver;
24 import com.android.tradefed.build.IBuildInfo;
25 import com.android.tradefed.config.Option;
26 import com.android.tradefed.config.OptionCopier;
27 import com.android.tradefed.device.DeviceNotAvailableException;
28 import com.android.tradefed.device.ITestDevice;
29 import com.android.tradefed.log.LogUtil.CLog;
30 import com.android.tradefed.result.ITestInvocationListener;
31 import com.android.tradefed.result.TestDescription;
32 import com.android.tradefed.testtype.IAbi;
33 import com.android.tradefed.testtype.IAbiReceiver;
34 import com.android.tradefed.testtype.IBuildReceiver;
35 import com.android.tradefed.testtype.IDeviceTest;
36 import com.android.tradefed.testtype.IRemoteTest;
37 import com.android.tradefed.testtype.IRuntimeHintProvider;
38 import com.android.tradefed.testtype.IShardableTest;
39 import com.android.tradefed.testtype.ITestCollector;
40 import com.android.tradefed.testtype.ITestFileFilterReceiver;
41 import com.android.tradefed.testtype.ITestFilterReceiver;
42 import com.android.tradefed.util.AbiUtils;
43 import com.android.tradefed.util.ArrayUtil;
44 import com.android.tradefed.util.FileUtil;
45 
46 import com.google.common.base.Splitter;
47 
48 import java.io.BufferedReader;
49 import java.io.File;
50 import java.io.FileReader;
51 import java.io.FilenameFilter;
52 import java.io.IOException;
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collection;
57 import java.util.Collections;
58 import java.util.HashSet;
59 import java.util.LinkedHashSet;
60 import java.util.List;
61 import java.util.Set;
62 import java.util.concurrent.TimeUnit;
63 
64 import vogar.expect.ExpectationStore;
65 import vogar.expect.ModeId;
66 
67 /**
68  * A wrapper to run tests against Dalvik.
69  */
70 public class DalvikTest implements IAbiReceiver, IBuildReceiver, IDeviceTest, IRemoteTest,
71         IRuntimeHintProvider, IShardableTest, ITestCollector, ITestFileFilterReceiver,
72         ITestFilterReceiver {
73 
74     private static final String TAG = DalvikTest.class.getSimpleName();
75 
76     /**
77      * TEST_PACKAGES is a Set containing the names of packages on the classpath known to contain
78      * tests to be run under DalvikTest. The TEST_PACKAGES set is used to shard DalvikTest into
79      * multiple DalvikTests, each responsible for running one of these packages' tests.
80      */
81     private static final Set<String> TEST_PACKAGES = new HashSet<>();
82     private static final String JDWP_PACKAGE_BASE = "org.apache.harmony.jpda.tests.jdwp.%s";
83     static {
84         // Though uppercase, these are package names, not class names
String.format(JDWP_PACKAGE_BASE, "ArrayReference")85         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ArrayReference"));
String.format(JDWP_PACKAGE_BASE, "ArrayType")86         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ArrayType"));
String.format(JDWP_PACKAGE_BASE, "ClassLoaderReference")87         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassLoaderReference"));
String.format(JDWP_PACKAGE_BASE, "ClassObjectReference")88         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassObjectReference"));
String.format(JDWP_PACKAGE_BASE, "ClassType")89         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ClassType"));
String.format(JDWP_PACKAGE_BASE, "DebuggerOnDemand")90         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "DebuggerOnDemand"));
String.format(JDWP_PACKAGE_BASE, "Deoptimization")91         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Deoptimization"));
String.format(JDWP_PACKAGE_BASE, "EventModifiers")92         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "EventModifiers"));
String.format(JDWP_PACKAGE_BASE, "Events")93         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Events"));
String.format(JDWP_PACKAGE_BASE, "InterfaceType")94         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "InterfaceType"));
String.format(JDWP_PACKAGE_BASE, "Method")95         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "Method"));
String.format(JDWP_PACKAGE_BASE, "MultiSession")96         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "MultiSession"));
String.format(JDWP_PACKAGE_BASE, "ObjectReference")97         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ObjectReference"));
String.format(JDWP_PACKAGE_BASE, "ReferenceType")98         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ReferenceType"));
String.format(JDWP_PACKAGE_BASE, "StackFrame")99         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "StackFrame"));
String.format(JDWP_PACKAGE_BASE, "StringReference")100         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "StringReference"));
String.format(JDWP_PACKAGE_BASE, "ThreadGroupReference")101         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ThreadGroupReference"));
String.format(JDWP_PACKAGE_BASE, "ThreadReference")102         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "ThreadReference"));
String.format(JDWP_PACKAGE_BASE, "VirtualMachine")103         TEST_PACKAGES.add(String.format(JDWP_PACKAGE_BASE, "VirtualMachine"));
104     }
105 
106     private static final String EXPECTATIONS_EXT = ".expectations";
107     // Command to run the VM, args are bitness, classpath, dalvik-args, abi, runner-args,
108     // include and exclude filters, and exclude filters file.
109     private static final String COMMAND = "dalvikvm%s -classpath %s %s "
110             + "com.android.compatibility.dalvik.DalvikTestRunner --abi=%s %s %s %s %s %s %s";
111     private static final String INCLUDE_FILE = "/data/local/tmp/dalvik/includes";
112     private static final String EXCLUDE_FILE = "/data/local/tmp/dalvik/excludes";
113     private static String START_RUN = "start-run";
114     private static String END_RUN = "end-run";
115     private static String START_TEST = "start-test";
116     private static String END_TEST = "end-test";
117     private static String FAILURE = "failure";
118 
119     // If we are running with adbconnection jdwp provider (hence a libjdwp agent).
120     private boolean mIsAdbConnection = true;
121 
122     @Option(name = "run-name", description = "The name to use when reporting results")
123     private String mRunName;
124 
125     @Option(name = "classpath", description = "Holds the paths to search when loading tests")
126     private List<String> mClasspath = new ArrayList<>();
127 
128     @Option(name = "dalvik-arg", description = "Holds arguments to pass to Dalvik")
129     private List<String> mDalvikArgs = new ArrayList<>();
130 
131     @Option(name = "dalvik-arg-adbconnection",
132             description = "Holds arguments to pass to Dalvik when " +
133                           "dalvik.vm.jdwp-provider == adbconnection or default or is empty")
134     private List<String> mDalvikArgsAdbconnection = new ArrayList<>();
135 
136     @Option(name = "dalvik-arg-internal",
137             description = "Holds arguments to pass to Dalvik only when " +
138                           "dalvik.vm.jdwp-provider == internal")
139     private List<String> mDalvikArgsInternal = new ArrayList<>();
140 
141     @Option(name = "runner-arg",
142             description = "Holds arguments to pass to the device-side test runner")
143     private List<String> mRunnerArgs = new ArrayList<>();
144 
145     @Option(name = "include-filter",
146             description = "The include filters of the test name to run.")
147     private Set<String> mIncludeFilters = new LinkedHashSet<>();
148 
149     @Option(name = "exclude-filter",
150             description = "The exclude filters of the test name to run.")
151     private Set<String> mExcludeFilters = new LinkedHashSet<>();
152 
153     @Option(name = "test-file-include-filter",
154             description="A file containing a list of line separated test classes and optionally"
155             + " methods to include")
156     private File mIncludeTestFile = null;
157 
158     @Option(name = "test-file-exclude-filter",
159             description="A file containing a list of line separated test classes and optionally"
160             + " methods to exclude")
161     private File mExcludeTestFile = null;
162 
163     @Option(name = "runtime-hint",
164             isTimeVal = true,
165             description="The hint about the test's runtime.")
166     private long mRuntimeHint = 60000;// 1 minute
167 
168     @Option(name = "known-failures-adbconnection",
169             description = "Comma-separated list of files specifying known-failures to be skipped")
170     private String mKnownFailuresAdbconnection;
171     @Option(name = "known-failures-internal",
172             description = "Comma-separated list of files specifying known-failures to be skipped")
173     private String mKnownFailuresInternal;
174 
175     @Option(name = "known-failures",
176             description = "Comma-separated list of files specifying known-failures to be skipped")
177     private String mKnownFailures;
178 
179     @Option(name = "collect-tests-only",
180             description = "Only invoke the instrumentation to collect list of applicable test "
181                     + "cases. All test run callbacks will be triggered, but test execution will "
182                     + "not be actually carried out.")
183     private boolean mCollectTestsOnly = false;
184 
185     @Option(name = "per-test-timeout",
186             description = "The maximum amount of time during which the DalvikTestRunner may "
187                     + "yield no output. Because the runner outputs results for each test, this "
188                     + "is essentially a per-test timeout")
189     private long mPerTestTimeout = 10; // 10 minutes
190 
191     private IAbi mAbi;
192     private CompatibilityBuildHelper mBuildHelper;
193     private ITestDevice mDevice;
194 
195     /**
196      * {@inheritDoc}
197      */
198     @Override
setAbi(IAbi abi)199     public void setAbi(IAbi abi) {
200         mAbi = abi;
201     }
202 
203     /**
204      * {@inheritDoc}
205      */
206     @Override
getAbi()207     public IAbi getAbi() {
208         return mAbi;
209     }
210 
211     /**
212      * {@inheritDoc}
213      */
214     @Override
setBuild(IBuildInfo build)215     public void setBuild(IBuildInfo build) {
216         mBuildHelper = new CompatibilityBuildHelper(build);
217     }
218 
219     /**
220      * {@inheritDoc}
221      */
222     @Override
setDevice(ITestDevice device)223     public void setDevice(ITestDevice device) {
224         mDevice = device;
225     }
226 
227     /**
228      * {@inheritDoc}
229      */
230     @Override
getDevice()231     public ITestDevice getDevice() {
232         return mDevice;
233     }
234 
235     /**
236      * {@inheritDoc}
237      */
238     @Override
addIncludeFilter(String filter)239     public void addIncludeFilter(String filter) {
240         mIncludeFilters.add(filter);
241     }
242 
243     /**
244      * {@inheritDoc}
245      */
246     @Override
addAllIncludeFilters(Set<String> filters)247     public void addAllIncludeFilters(Set<String> filters) {
248         mIncludeFilters.addAll(filters);
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     @Override
addExcludeFilter(String filter)255     public void addExcludeFilter(String filter) {
256         mExcludeFilters.add(filter);
257     }
258 
259     /**
260      * {@inheritDoc}
261      */
262     @Override
addAllExcludeFilters(Set<String> filters)263     public void addAllExcludeFilters(Set<String> filters) {
264         mExcludeFilters.addAll(filters);
265     }
266 
267     /**
268      * {@inheritDoc}
269      */
270     @Override
getIncludeFilters()271     public Set<String> getIncludeFilters() {
272         return mIncludeFilters;
273     }
274 
275     /**
276      * {@inheritDoc}
277      */
278     @Override
getExcludeFilters()279     public Set<String> getExcludeFilters() {
280         return mExcludeFilters;
281     }
282 
283     /**
284      * {@inheritDoc}
285      */
286     @Override
clearIncludeFilters()287     public void clearIncludeFilters() {
288         mIncludeFilters.clear();
289     }
290 
291     /**
292      * {@inheritDoc}
293      */
294     @Override
clearExcludeFilters()295     public void clearExcludeFilters() {
296         mExcludeFilters.clear();
297     }
298 
299     /**
300      * {@inheritDoc}
301      */
302     @Override
setIncludeTestFile(File testFile)303     public void setIncludeTestFile(File testFile) {
304         mIncludeTestFile = testFile;
305     }
306 
307     /**
308      * {@inheritDoc}
309      */
310     @Override
setExcludeTestFile(File testFile)311     public void setExcludeTestFile(File testFile) {
312         mExcludeTestFile = testFile;
313     }
314 
315     /**
316      * {@inheritDoc}
317      */
318     @Override
getRuntimeHint()319     public long getRuntimeHint() {
320         return mRuntimeHint;
321     }
322 
323     /**
324      * {@inheritDoc}
325      */
326     @Override
setCollectTestsOnly(boolean shouldCollectTest)327     public void setCollectTestsOnly(boolean shouldCollectTest) {
328         mCollectTestsOnly = shouldCollectTest;
329     }
330 
isAdbconnection(ITestDevice device)331     private static boolean isAdbconnection(ITestDevice device) throws DeviceNotAvailableException {
332         String provider = device.getProperty("dalvik.vm.jdwp-provider");
333         if (provider == null || provider.equals("default") || provider.equals("adbconnection")) {
334             return true;
335         } else if (provider.equals("internal")) {
336             return false;
337         } else {
338             throw new RuntimeException("Unknown dalvik.vm.jdwp-provider = " + provider);
339         }
340     }
341 
342     /**
343      * {@inheritDoc}
344      */
345     @Override
run(final ITestInvocationListener listener)346     public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException {
347         String abiName = mAbi.getName();
348         String bitness = AbiUtils.getBitness(abiName);
349         mIsAdbConnection = isAdbconnection(getDevice());
350 
351         File tmpExcludeFile = null;
352         String excludeFile = "";
353         try {
354             // push one file of exclude filters to the device
355             tmpExcludeFile = getExcludeFile();
356             if (!mDevice.pushFile(tmpExcludeFile, EXCLUDE_FILE)) {
357                 Log.logAndDisplay(LogLevel.ERROR, TAG, "Couldn't push file: " + tmpExcludeFile);
358             } else {
359                 CLog.d("exclude-filter-file: %s", safeReadContentFromFile(tmpExcludeFile));
360                 // If sucessfully pushed then add it as a filter.
361                 excludeFile = String.format("--exclude-filter-file=%s", EXCLUDE_FILE);
362             }
363         } catch (IOException e) {
364             throw new RuntimeException("Failed to parse expectations", e);
365         } finally {
366             FileUtil.deleteFile(tmpExcludeFile);
367         }
368 
369         // push one file of include filters to the device, if file exists
370         String includeFile = "";
371         if (mIncludeTestFile != null) {
372             String path = mIncludeTestFile.getAbsolutePath();
373             if (!mIncludeTestFile.isFile() || !mIncludeTestFile.canRead()) {
374                 throw new RuntimeException(String.format("Failed to read include file %s", path));
375             }
376             if (!mDevice.pushFile(mIncludeTestFile, INCLUDE_FILE)) {
377                 Log.logAndDisplay(LogLevel.ERROR, TAG, "Couldn't push file: " + path);
378             } else {
379                 CLog.d("include-filter-file: %s", safeReadContentFromFile(mIncludeTestFile));
380                 // If sucessfully pushed then add it as a filter.
381                 includeFile = String.format("--include-filter-file=%s", INCLUDE_FILE);
382             }
383         }
384 
385         if (mIsAdbConnection) {
386             Log.logAndDisplay(LogLevel.INFO, TAG, "Running with ADBConnection/libjdwp agent");
387             mDalvikArgs.addAll(mDalvikArgsAdbconnection);
388         } else {
389             Log.logAndDisplay(LogLevel.INFO, TAG, "Running with internal jdwp implementation");
390             mDalvikArgs.addAll(mDalvikArgsInternal);
391         }
392 
393         // Create command
394         mDalvikArgs.add("-Duser.name=shell");
395         mDalvikArgs.add("-Duser.language=en");
396         mDalvikArgs.add("-Duser.region=US");
397         mDalvikArgs.add("-Xcheck:jni");
398         mDalvikArgs.add("-Xjnigreflimit:2000");
399 
400         String dalvikArgs = ArrayUtil.join(" ", mDalvikArgs);
401         dalvikArgs = dalvikArgs.replace("|#ABI#|", bitness);
402 
403         String runnerArgs = ArrayUtil.join(" ", mRunnerArgs);
404         // Filters
405         StringBuilder includeFilters = new StringBuilder();
406         if (!mIncludeFilters.isEmpty()) {
407             includeFilters.append("--include-filter=");
408             includeFilters.append(ArrayUtil.join(",", mIncludeFilters));
409         }
410         StringBuilder excludeFilters = new StringBuilder();
411         if (!mExcludeFilters.isEmpty()) {
412             excludeFilters.append("--exclude-filter=");
413             excludeFilters.append(ArrayUtil.join(",", mExcludeFilters));
414         }
415 
416         // Communicate with DalvikTestRunner if tests should only be collected
417         String collectTestsOnlyString = (mCollectTestsOnly) ? "--collect-tests-only" : "";
418         final String command = String.format(COMMAND, bitness,
419                 ArrayUtil.join(File.pathSeparator, mClasspath),
420                 dalvikArgs, abiName, runnerArgs,
421                 includeFilters, excludeFilters, includeFile, excludeFile, collectTestsOnlyString);
422         IShellOutputReceiver receiver = new MultiLineReceiver() {
423             private TestDescription test;
424 
425             @Override
426             public boolean isCancelled() {
427                 return false;
428             }
429 
430             @Override
431             public void processNewLines(String[] lines) {
432                 for (String line : lines) {
433                     String[] parts = line.split(":", 2);
434                     String tag = parts[0];
435                     if (tag.equals(START_RUN)) {
436                         listener.testRunStarted(mRunName, Integer.parseInt(parts[1]));
437                         Log.logAndDisplay(LogLevel.INFO, TAG, command);
438                     } else if (tag.equals(END_RUN)) {
439                         listener.testRunEnded(Integer.parseInt(parts[1]),
440                                 Collections.<String, String>emptyMap());
441                     } else if (tag.equals(START_TEST)) {
442                         test = getTestDescription(parts[1]);
443                         listener.testStarted(test);
444                     } else if (tag.equals(FAILURE)) {
445                         listener.testFailed(test, processSerializedValue(parts[1]));
446                     } else if (tag.equals(END_TEST)) {
447                         listener.testEnded(getTestDescription(parts[1]),
448                                 Collections.<String, String>emptyMap());
449                     }
450                     // Always log the output for debugging
451                     CLog.d(line);
452                 }
453             }
454 
455             private String processSerializedValue(String input) {
456                 // Opposite of stringify.
457                 return input.replace("^~^", "\n");
458             }
459 
460             private TestDescription getTestDescription(String name) {
461                 String[] parts = name.split("#");
462                 String className = parts[0];
463                 String testName = "";
464                 if (parts.length > 1) {
465                     testName = parts[1];
466                 }
467                 return new TestDescription(className, testName);
468             }
469 
470         };
471         mDevice.executeShellCommand(command, receiver, mPerTestTimeout, TimeUnit.MINUTES, 1);
472     }
473 
474     /*
475      * Due to known failures, there are typically too many excludes to pass via command line.
476      * Collect excludes from .expectation files in the testcases directory, from files in the
477      * module's resources directory, and from mExcludeTestFile, if set.
478      */
getExcludeFile()479     private File getExcludeFile() throws IOException {
480         File excludeFile = null;
481         PrintWriter out = null;
482 
483         try {
484             excludeFile = File.createTempFile("excludes", "txt");
485             out = new PrintWriter(excludeFile);
486             // create expectation store from set of expectation files found in testcases dir
487             Set<File> expectationFiles = new HashSet<>();
488             for (File f : mBuildHelper.getTestsDir().listFiles(
489                     new ExpectationFileFilter(mRunName))) {
490                 expectationFiles.add(f);
491             }
492             ExpectationStore testsDirStore =
493                     ExpectationStore.parse(expectationFiles, ModeId.DEVICE);
494             // create expectation store from expectation files found in module resources dir
495             ExpectationStore resourceStore = null;
496             Set<String> knownFailuresFiles = new HashSet<String>();
497             Splitter splitter = Splitter.on(',').trimResults();
498             if (mKnownFailures != null) {
499                 knownFailuresFiles.addAll(splitter.splitToList(mKnownFailures));
500             }
501             if (mIsAdbConnection && mKnownFailuresAdbconnection != null) {
502                 knownFailuresFiles.addAll(splitter.splitToList(mKnownFailuresAdbconnection));
503             }
504             if (!mIsAdbConnection && mKnownFailuresInternal != null) {
505                 knownFailuresFiles.addAll(splitter.splitToList(mKnownFailuresInternal));
506             }
507             if (knownFailuresFiles.size() != 0) {
508                 resourceStore = ExpectationStore.parseResources(
509                         getClass(), knownFailuresFiles, ModeId.DEVICE);
510             }
511             // Add expectations from testcases dir
512             for (String exclude : testsDirStore.getAllFailures().keySet()) {
513                 out.println(exclude);
514             }
515             for (String exclude : testsDirStore.getAllOutComes().keySet()) {
516                 out.println(exclude);
517             }
518             // Add expectations from resources dir
519             if (resourceStore != null) {
520                 for (String exclude : resourceStore.getAllFailures().keySet()) {
521                     out.println(exclude);
522                 }
523                 for (String exclude : resourceStore.getAllOutComes().keySet()) {
524                     out.println(exclude);
525                 }
526             }
527             // Add excludes from test-file-exclude-filter option
528             for (String exclude : getFiltersFromFile(mExcludeTestFile)) {
529                 out.println(exclude);
530             }
531             out.flush();
532         } finally {
533             if (out != null) {
534                 out.close();
535             }
536         }
537         return excludeFile;
538     }
539 
540 
541     /*
542      * Helper method that reads filters from a file into a set.
543      * Returns an empty set given a null file
544      */
getFiltersFromFile(File f)545     private static Set<String> getFiltersFromFile(File f) throws IOException {
546         Set<String> filters = new HashSet<String>();
547         if (f != null) {
548             BufferedReader reader = new BufferedReader(new FileReader(f));
549             String filter = null;
550             while ((filter = reader.readLine()) != null) {
551                 filters.add(filter);
552             }
553             reader.close();
554         }
555         return filters;
556     }
557 
558     /**
559      * {@inheritDoc}
560      */
561     @Override
split()562     public Collection<IRemoteTest> split() {
563         List<IRemoteTest> shards = new ArrayList<>();
564         // A DalvikTest to run any tests not contained in packages from TEST_PACKAGES, may be empty
565         DalvikTest catchAll = new DalvikTest();
566         OptionCopier.copyOptionsNoThrow(this, catchAll);
567         catchAll.mAbi = mAbi;
568         shards.add(catchAll);
569         // estimate catchAll's runtime to be that of a single package in TEST_PACKAGES
570         long runtimeHint = mRuntimeHint / TEST_PACKAGES.size();
571         catchAll.mRuntimeHint = runtimeHint;
572         for (String packageName: TEST_PACKAGES) {
573             catchAll.addExcludeFilter(packageName);
574             // create one shard for package 'packageName'
575             DalvikTest test = new DalvikTest();
576             OptionCopier.copyOptionsNoThrow(this, test);
577             test.addIncludeFilter(packageName);
578             test.mRuntimeHint = runtimeHint / TEST_PACKAGES.size();
579             test.mAbi = mAbi;
580             shards.add(test);
581         }
582         // return a shard for each package in TEST_PACKAGE, plus a shard for any other tests
583         return shards;
584     }
585 
586     /**
587      * A {@link FilenameFilter} to find all the expectation files in a directory.
588      */
589     public static class ExpectationFileFilter implements FilenameFilter {
590 
591         private String mName;
592 
ExpectationFileFilter(String name)593         public ExpectationFileFilter(String name) {
594             mName = name;
595         }
596         /**
597          * {@inheritDoc}
598          */
599         @Override
accept(File dir, String name)600         public boolean accept(File dir, String name) {
601             return name.startsWith(mName) && name.endsWith(EXPECTATIONS_EXT);
602         }
603     }
604 
safeReadContentFromFile(File file)605     private String safeReadContentFromFile(File file) {
606         try {
607             return FileUtil.readStringFromFile(file);
608         } catch (IOException e) {
609             CLog.e(e);
610             return null;
611         }
612     }
613 }
614