• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.server.wm;
18 
19 import android.graphics.Bitmap;
20 import android.util.Log;
21 
22 import com.android.compatibility.common.util.BitmapUtils;
23 
24 import org.junit.rules.TestRule;
25 import org.junit.runner.Description;
26 import org.junit.runners.model.Statement;
27 
28 import java.io.File;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /**
35  * A {@code TestRule} that allows dumping data on test failure.
36  *
37  * <p>Note: when using other {@code TestRule}s, make sure to use a {@code RuleChain} to ensure it
38  * is applied outside of other rules that can fail a test (otherwise this rule may not know that the
39  * test failed).
40  *
41  * <p>To capture the output of this rule, add the following to AndroidTest.xml:
42  * <pre>
43  *  <!-- Collect output of DumpOnFailure. -->
44  *  <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
45  *    <option name="directory-keys" value="/sdcard/DumpOnFailure" />
46  *    <option name="collect-on-run-ended-only" value="true" />
47  *  </metrics_collector>
48  * </pre>
49  * <p>And disable external storage isolation:
50  * <pre>
51  *  <application ... android:requestLegacyExternalStorage="true" ... >
52  * </pre>
53  */
54 public class DumpOnFailure implements TestRule {
55 
56     private static final String TAG = "DumpOnFailure";
57 
58     private final Map<String, Bitmap> mDumpOnFailureBitmaps = new HashMap<>();
59 
60     @Override
apply(Statement base, Description description)61     public Statement apply(Statement base, Description description) {
62         return new Statement() {
63             @Override
64             public void evaluate() throws Throwable {
65                 onTestSetup(description);
66                 try {
67                     base.evaluate();
68                 } catch (Throwable t) {
69                     onTestFailure(description, t);
70                     throw t;
71                 } finally {
72                     onTestTeardown(description);
73                 }
74             }
75         };
76     }
77 
78     private void onTestSetup(Description description) {
79         cleanDir(getDumpRoot(description).toFile());
80         mDumpOnFailureBitmaps.clear();
81     }
82 
83     private void onTestTeardown(Description description) {
84         mDumpOnFailureBitmaps.clear();
85     }
86 
87     private void onTestFailure(Description description, Throwable t) {
88         Path root = getDumpRoot(description);
89         File rootFile = root.toFile();
90         if (!rootFile.exists() && !rootFile.mkdirs()) {
91             throw new RuntimeException("Unable to create " + root);
92         }
93 
94         for (Map.Entry<String, Bitmap> entry : mDumpOnFailureBitmaps.entrySet()) {
95             String fileName = getFilename(description, entry.getKey(), "png");
96             Log.i(TAG, "Dumping " + root + "/" + fileName);
97             BitmapUtils.saveBitmap(entry.getValue(), root.toString(), fileName);
98         }
99     }
100 
101     private String getFilename(Description description, String name, String extension) {
102         return description.getTestClass().getSimpleName() + "_" + description.getMethodName()
103                 + "__" + name + "." + extension;
104     }
105 
106     private Path getDumpRoot(Description description) {
107         return Paths.get("/sdcard/DumpOnFailure/", description.getClassName()
108                 + "_" + description.getMethodName());
109     }
110 
111     private void cleanDir(File dir) {
112         final File[] files = dir.listFiles();
113         if (files == null) {
114             return;
115         }
116         for (File file : files) {
117             if (!file.isDirectory()) {
118                 if (!file.delete()) {
119                     throw new RuntimeException("Unable to delete " + file);
120                 }
121             }
122         }
123     }
124 
125     /**
126      * Dumps the Bitmap if the test fails.
127      */
128     public void dumpOnFailure(String name, Bitmap bitmap) {
129         if (mDumpOnFailureBitmaps.containsKey(name)) {
130             int i = 1;
131             while (mDumpOnFailureBitmaps.containsKey(name + "_" + i)) {
132                 ++i;
133             }
134             name += "_" + i;
135         }
136         mDumpOnFailureBitmaps.put(name, bitmap);
137     }
138 }
139