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 com.android.compatibility.common.util;
18 
19 import org.xmlpull.v1.XmlSerializer;
20 
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.util.Objects;
24 import java.util.Set;
25 
26 /**
27  * Utility class to add test case result history to the report. This class records per-case history
28  * for CTS Verifier. If this field is used for large test suites like CTS, it may cause performance
29  * issues in APFE. Thus please do not use this class in other test suites.
30  */
31 public class TestResultHistory implements Serializable {
32 
33     private static final long serialVersionUID = 10L;
34 
35     private static final String ENCODING = "UTF-8";
36     private static final String TYPE = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
37 
38     // XML constants
39     private static final String SUB_TEST_ATTR = "subtest";
40     private static final String RUN_HISTORY_TAG = "RunHistory";
41     private static final String RUN_TAG = "Run";
42     private static final String START_TIME_ATTR = "start";
43     private static final String END_TIME_ATTR = "end";
44     private static final String IS_AUTOMATED_ATTR = "isAutomated";
45 
46     private final String mTestName;
47     private final Set<ExecutionRecord> mExecutionRecords;
48 
49     /**
50      * Constructor of test result history.
51      *
52      * @param testName a string of test name.
53      * @param executionRecords a Set of ExecutionRecords.
54      */
TestResultHistory(String testName, Set<ExecutionRecord> executionRecords)55     public TestResultHistory(String testName, Set<ExecutionRecord> executionRecords) {
56         this.mTestName = testName;
57         this.mExecutionRecords = executionRecords;
58     }
59 
60     /** Get test name */
getTestName()61     public String getTestName() {
62         return mTestName;
63     }
64 
65     /** Get a set of ExecutionRecords. */
getExecutionRecords()66     public Set<ExecutionRecord> getExecutionRecords() {
67         return mExecutionRecords;
68     }
69 
70     /** {@inheritDoc} */
71     @Override
equals(Object o)72     public boolean equals(Object o) {
73         if (this == o) {
74             return true;
75         }
76         if (o == null || getClass() != o.getClass()) {
77             return false;
78         }
79         TestResultHistory that = (TestResultHistory) o;
80         return Objects.equals(mTestName, that.mTestName)
81                 && Objects.equals(mExecutionRecords, that.mExecutionRecords);
82     }
83 
84     /** {@inheritDoc} */
85     @Override
hashCode()86     public int hashCode() {
87         return Objects.hash(mTestName, mExecutionRecords);
88     }
89 
90     /**
91      * Serializes a given {@link TestResultHistory} to XML.
92      *
93      * @param serializer given serializer.
94      * @param resultHistory test result history with test name and execution record.
95      * @param testName top-level test name.
96      * @throws IOException
97      */
serialize( XmlSerializer serializer, TestResultHistory resultHistory, String testName)98     public static void serialize(
99             XmlSerializer serializer, TestResultHistory resultHistory, String testName)
100             throws IOException {
101         if (resultHistory == null) {
102             throw new IllegalArgumentException("Test result history was null");
103         }
104 
105         serializer.startTag(null, RUN_HISTORY_TAG);
106         // Only show sub-test names in test attribute in run history node.
107         String name = getSubTestName(testName, resultHistory.getTestName());
108         if (!name.isEmpty() && !name.equalsIgnoreCase(testName)) {
109             serializer.attribute(null, SUB_TEST_ATTR, name);
110         }
111 
112         for (ExecutionRecord execRecord : resultHistory.getExecutionRecords()) {
113             serializer.startTag(null, RUN_TAG);
114             serializer.attribute(null, START_TIME_ATTR, String.valueOf(execRecord.getStartTime()));
115             serializer.attribute(null, END_TIME_ATTR, String.valueOf(execRecord.getEndTime()));
116             serializer.attribute(
117                     null, IS_AUTOMATED_ATTR, String.valueOf(execRecord.getIsAutomated()));
118             serializer.endTag(null, RUN_TAG);
119         }
120         serializer.endTag(null, RUN_HISTORY_TAG);
121     }
122 
123     /**
124      * Get subtest name by replacing top-level test name.
125      *
126      * @param testName top-level test name.
127      * @param fullTestName test name with the combination of top-level and subtest name.
128      * @return subtest name without top-level test name
129      */
getSubTestName(String testName, String fullTestName)130     protected static String getSubTestName(String testName, String fullTestName) {
131         // Handle test name with brackets, like [folded] as the suffix for foldable test plan.
132         testName = testName.replace("[", "\\[").replace("]", "\\]");
133         String subTestName = fullTestName.replaceFirst(testName + ":", "");
134         return subTestName;
135     }
136 
137     /** Execution Record about start time, end time and isAutomated */
138     public static class ExecutionRecord implements Serializable {
139 
140         private static final long serialVersionUID = 0L;
141         // Start time of test case.
142         private final long startTime;
143         // End time of test case.
144         private final long endTime;
145         // Whether test case was executed through automation.
146         private final boolean isAutomated;
147 
ExecutionRecord(long startTime, long endTime, boolean isAutomated)148         public ExecutionRecord(long startTime, long endTime, boolean isAutomated) {
149             this.startTime = startTime;
150             this.endTime = endTime;
151             this.isAutomated = isAutomated;
152         }
153 
getStartTime()154         public long getStartTime() {
155             return startTime;
156         }
157 
getEndTime()158         public long getEndTime() {
159             return endTime;
160         }
161 
getIsAutomated()162         public boolean getIsAutomated() {
163             return isAutomated;
164         }
165 
166         @Override
equals(Object o)167         public boolean equals(Object o) {
168             if (this == o) {
169                 return true;
170             }
171             if (o == null || getClass() != o.getClass()) {
172                 return false;
173             }
174             ExecutionRecord that = (ExecutionRecord) o;
175             return startTime == that.startTime
176                     && endTime == that.endTime
177                     && isAutomated == that.isAutomated;
178         }
179 
180         @Override
hashCode()181         public int hashCode() {
182             return Objects.hash(startTime, endTime, isAutomated);
183         }
184     }
185 }
186