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 com.android.tradefed.result;
17 
18 import com.android.ddmlib.testrunner.TestIdentifier;
19 
20 import java.io.Serializable;
21 import java.lang.annotation.Annotation;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.regex.Matcher;
25 import java.util.regex.Pattern;
26 
27 /** Class representing information about a test case. */
28 public final class TestDescription implements Serializable, Comparable<TestDescription> {
29 
30     /** Regex for method parameterized. For example: testName[0] */
31     public static final Pattern PARAMETERIZED_TEST_REGEX = Pattern.compile("([^\\[]+)\\[(.*)\\]$");
32 
33     private final String mClassName;
34     private final String mTestName;
35     private final String mTestNameNoParams;
36     private Annotation[] mAnnotations;
37 
38     /**
39      * Constructor
40      *
41      * @param className The name of the class holding the test.
42      * @param testName The test (method) name.
43      */
TestDescription(String className, String testName)44     public TestDescription(String className, String testName) {
45         if (className == null || testName == null) {
46             throw new IllegalArgumentException("className and testName must be non-null");
47         }
48         mClassName = className;
49         mTestName = testName;
50         mAnnotations = new Annotation[0];
51 
52         // If the method looks parameterized, track the base non-parameterized name.
53         Matcher m = PARAMETERIZED_TEST_REGEX.matcher(testName);
54         if (m.find()) {
55             mTestNameNoParams = m.group(1);
56         } else {
57             mTestNameNoParams = testName;
58         }
59     }
60 
61     /**
62      * Constructor
63      *
64      * @param className The name of the class holding the test.
65      * @param testName The test (method) name.
66      * @param annotations List of {@link Annotation} associated with the test case.
67      */
TestDescription(String className, String testName, Annotation... annotations)68     public TestDescription(String className, String testName, Annotation... annotations) {
69         this(className, testName);
70         mAnnotations = annotations;
71     }
72 
73     /**
74      * Constructor
75      *
76      * @param className The name of the class holding the test.
77      * @param testName The test (method) name.
78      * @param annotations Collection of {@link Annotation} associated with the test case.
79      */
TestDescription(String className, String testName, Collection<Annotation> annotations)80     public TestDescription(String className, String testName, Collection<Annotation> annotations) {
81         this(className, testName, annotations.toArray(new Annotation[annotations.size()]));
82     }
83 
84     /**
85      * @return the annotation of type annotationType that is attached to this description node, or
86      *     null if none exists
87      */
getAnnotation(Class<T> annotationType)88     public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
89         for (Annotation each : mAnnotations) {
90             if (each.annotationType().equals(annotationType)) {
91                 return annotationType.cast(each);
92             }
93         }
94         return null;
95     }
96 
97     /** @return all of the annotations attached to this description node */
getAnnotations()98     public Collection<Annotation> getAnnotations() {
99         return Arrays.asList(mAnnotations);
100     }
101 
102     /** Returns the fully qualified class name of the test. */
getClassName()103     public String getClassName() {
104         return mClassName;
105     }
106 
107     /**
108      * Returns the name of the test with the parameters, if it's parameterized test. Returns the
109      * regular test name if not a parameterized test.
110      */
getTestName()111     public String getTestName() {
112         return mTestName;
113     }
114 
115     /** Returns the name of the test without any parameters (if it's a parameterized method). */
getTestNameWithoutParams()116     public String getTestNameWithoutParams() {
117         return mTestNameNoParams;
118     }
119 
120     @Override
hashCode()121     public int hashCode() {
122         final int prime = 31;
123         int result = 1;
124         result = prime * result + ((mClassName == null) ? 0 : mClassName.hashCode());
125         result = prime * result + ((mTestName == null) ? 0 : mTestName.hashCode());
126         return result;
127     }
128 
129     @Override
equals(Object obj)130     public boolean equals(Object obj) {
131         if (this == obj) return true;
132         if (obj == null) return false;
133         if (getClass() != obj.getClass()) return false;
134         TestDescription other = (TestDescription) obj;
135 
136         if (!mClassName.equals(other.mClassName) || !mTestName.equals(other.mTestName)) {
137             return false;
138         }
139         return true;
140     }
141 
142     @Override
compareTo(TestDescription o)143     public int compareTo(TestDescription o) {
144         return toString().compareTo(o.toString());
145     }
146 
147     @Override
toString()148     public String toString() {
149         return String.format("%s#%s", getClassName(), getTestName());
150     }
151 
152     /**
153      * Create a {@link TestDescription} from its {@link #toString()}} representation.
154      *
155      * @param data the String representation. Expected format: classname#methodname
156      * @return the TestDescription or null if it could not be parsed
157      */
fromString(String data)158     public static TestDescription fromString(String data) {
159         String[] segments = data.split("#");
160         if (segments.length == 2) {
161             return new TestDescription(segments[0], segments[1]);
162         }
163         return null;
164     }
165 
166     /**
167      * Create a {@link TestDescription} from a {@link TestIdentifier}. Used for ease of conversion
168      * from one to another.
169      *
170      * @param testId The {@link TestIdentifier} to convert.
171      * @return the created {@link TestDescription} with the TestIdentifier values.
172      */
createFromTestIdentifier(TestIdentifier testId)173     public static TestDescription createFromTestIdentifier(TestIdentifier testId) {
174         return new TestDescription(testId.getClassName(), testId.getTestName());
175     }
176 
177     /**
178      * Create a {@link TestIdentifier} from a {@link TestDescription}. Useful for converting a
179      * description during testing.
180      *
181      * @param desc The {@link TestDescription} to convert.
182      * @return The created {@link TestIdentifier} with the TestDescription values.
183      */
convertToIdentifier(TestDescription desc)184     public static TestIdentifier convertToIdentifier(TestDescription desc) {
185         return new TestIdentifier(desc.getClassName(), desc.getTestName());
186     }
187 }
188