1 /*
2  * Copyright (C) 2018 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 package android.device.loggers;
17 
18 import org.junit.rules.ExternalResource;
19 import org.junit.rules.TestRule;
20 import org.junit.runner.Description;
21 import org.junit.runners.model.Statement;
22 
23 import java.io.File;
24 import java.lang.annotation.Annotation;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.Objects;
28 
29 /**
30  * Implementation of {@link ExternalResource} and {@link TestRule}. This rule allows to log logs
31  * during a test case (inside @Test). It guarantees that the log list is cleaned between tests,
32  * so the same rule object can be re-used.
33  *
34  * <pre>Example:
35  * &#064;Rule
36  * public TestLogData logs = new TestLogData();
37  *
38  * &#064;Test
39  * public void testFoo() {
40  *     logs.addTestLog("logcat", LogDataType.LOGCAT, new FileInputStreamSource(logcatFile));
41  * }
42  *
43  * &#064;Test
44  * public void testFoo2() {
45  *     logs.addTestLog("logcat2", LogDataType.LOGCAT, new FileInputStreamSource(logcatFile2));
46  * }
47  * </pre>
48  *
49  * TODO: The naming is in sync with the Tradefed host-side one, but they should be refactored in a
50  * single place.
51  */
52 public class TestLogData extends ExternalResource {
53     private Description mDescription;
54     private List<LogHolder> mLogs = new ArrayList<>();
55 
56     @Override
apply(Statement base, Description description)57     public Statement apply(Statement base, Description description) {
58         mDescription = description;
59         return super.apply(base, description);
60     }
61 
addTestLog(String dataName, File logFile)62     public final void addTestLog(String dataName, File logFile) {
63         mLogs.add(new LogHolder(dataName, logFile));
64     }
65 
66     @Override
after()67     protected void after() {
68         // we inject a Description with an annotation carrying metrics.
69         // Since Description cannot be extended and RunNotifier does not give us a lot of
70         // flexibility to find our metrics back, we have to pass the information via a placeholder
71         // annotation in the Description.
72         mDescription.addChild(
73                 Description.createTestDescription("LOGS", "LOGS", new LogAnnotation(mLogs)));
74     }
75 
76     /** Fake annotation meant to carry logs to the reporters. */
77     static class LogAnnotation implements Annotation {
78 
79         public List<LogHolder> mLogs = new ArrayList<>();
80 
LogAnnotation(List<LogHolder> logs)81         public LogAnnotation(List<LogHolder> logs) {
82             mLogs.addAll(logs);
83         }
84 
85         @Override
annotationType()86         public Class<? extends Annotation> annotationType() {
87             return null;
88         }
89 
90         @Override
equals(Object other)91         public boolean equals(Object other) {
92             if (other == this) {
93                 return true;
94             }
95             if (!(other instanceof LogAnnotation)) {
96                 return false;
97             }
98             LogAnnotation o = (LogAnnotation) other;
99             return Objects.equals(mLogs, o.mLogs);
100         }
101 
102         @Override
hashCode()103         public int hashCode() {
104             return Objects.hashCode(mLogs);
105         }
106     }
107 
108     /** Structure to hold a log file to be reported. */
109     static class LogHolder {
110         public final String mDataName;
111         public final File mLogFile;
112 
LogHolder(String dataName, File logFile)113         public LogHolder(String dataName, File logFile) {
114             mDataName = dataName;
115             mLogFile = logFile;
116         }
117 
118         @Override
equals(Object other)119         public boolean equals(Object other) {
120             if (other == this) {
121                 return true;
122             }
123             if (!(other instanceof LogHolder)) {
124                 return false;
125             }
126             LogHolder o = (LogHolder) other;
127             return Objects.equals(mDataName, o.mDataName) && Objects.equals(mLogFile, o.mLogFile);
128         }
129 
130         @Override
hashCode()131         public int hashCode() {
132             return Objects.hash(mDataName, mLogFile);
133         }
134     }
135 }
136