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 package com.android.compatibility.common.tradefed.testtype;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertTrue;
20 import static org.junit.Assert.fail;
21 
22 import com.android.tradefed.build.DeviceBuildInfo;
23 import com.android.tradefed.config.OptionSetter;
24 import com.android.tradefed.log.LogUtil.CLog;
25 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
26 import com.android.tradefed.result.ITestInvocationListener;
27 import com.android.tradefed.result.TestDescription;
28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
29 import com.android.tradefed.testtype.HostTest;
30 import com.android.tradefed.testtype.IRemoteTest;
31 import com.android.tradefed.util.FileUtil;
32 import com.android.tradefed.util.proto.TfMetricProtoUtil;
33 
34 import org.easymock.EasyMock;
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 import org.junit.runners.JUnit4;
41 
42 import java.io.File;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.net.MalformedURLException;
46 import java.net.URL;
47 import java.net.URLClassLoader;
48 import java.util.Arrays;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 
53 /**
54  * Unit tests for {@link JarHostTest}.
55  */
56 @RunWith(JUnit4.class)
57 public class JarHostTestTest {
58 
59     private static final String TEST_JAR1 = "/testtype/testJar1.jar";
60     private JarHostTest mTest;
61     private DeviceBuildInfo mStubBuildInfo;
62     private File mTestDir = null;
63     private ITestInvocationListener mListener;
64 
65     /**
66      * More testable version of {@link JarHostTest}
67      */
68     public static class JarHostTestable extends JarHostTest {
69 
70         public static File mTestDir;
JarHostTestable()71         public JarHostTestable() {}
72 
JarHostTestable(File testDir)73         public JarHostTestable(File testDir) {
74             mTestDir = testDir;
75         }
76     }
77 
78     @Before
setUp()79     public void setUp() throws Exception {
80         mTest = new JarHostTest();
81         mTestDir = FileUtil.createTempDir("jarhostest");
82         mListener = EasyMock.createMock(ITestInvocationListener.class);
83         OptionSetter setter = new OptionSetter(mTest);
84         setter.setOptionValue("enable-pretty-logs", "false");
85         mStubBuildInfo = new DeviceBuildInfo();
86         mStubBuildInfo.setTestsDir(mTestDir, "v1");
87     }
88 
89     @After
tearDown()90     public void tearDown() throws Exception {
91         FileUtil.recursiveDelete(mTestDir);
92     }
93 
94     /**
95      * Helper to read a file from the res/testtype directory and return it.
96      *
97      * @param filename the name of the file in the res/testtype directory
98      * @param parentDir dir where to put the jar. Null if in default tmp directory.
99      * @return the extracted jar file.
100      */
getJarResource(String filename, File parentDir)101     protected File getJarResource(String filename, File parentDir) throws IOException {
102         InputStream jarFileStream = getClass().getResourceAsStream(filename);
103         File jarFile = FileUtil.createTempFile("test", ".jar", parentDir);
104         FileUtil.writeToFile(jarFileStream, jarFile);
105         return jarFile;
106     }
107 
108     /**
109      * Test class, we have to annotate with full org.junit.Test to avoid name collision in import.
110      */
111     @RunWith(JUnit4.class)
112     public static class Junit4TestClass  {
Junit4TestClass()113         public Junit4TestClass() {}
114         @org.junit.Test
testPass1()115         public void testPass1() {}
116     }
117 
118     /**
119      * Test class, we have to annotate with full org.junit.Test to avoid name collision in import.
120      */
121     @RunWith(JUnit4.class)
122     public static class Junit4TestClass2  {
Junit4TestClass2()123         public Junit4TestClass2() {}
124         @Rule public TestMetrics metrics = new TestMetrics();
125 
126         @org.junit.Test
testPass2()127         public void testPass2() {
128             metrics.addTestMetric("key", "value");
129         }
130     }
131 
132     /**
133      * Test that {@link JarHostTest#split()} inherited from {@link HostTest} is still good.
134      */
135     @Test
testSplit_withoutJar()136     public void testSplit_withoutJar() throws Exception {
137         OptionSetter setter = new OptionSetter(mTest);
138         setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
139                 + "JarHostTestTest$Junit4TestClass");
140         setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
141                 + "JarHostTestTest$Junit4TestClass2");
142         // sharCount is ignored; will split by number of classes
143         List<IRemoteTest> res = (List<IRemoteTest>)mTest.split(1);
144         assertEquals(2, res.size());
145         assertTrue(res.get(0) instanceof JarHostTest);
146         assertTrue(res.get(1) instanceof JarHostTest);
147     }
148 
149     /**
150      * Test that {@link JarHostTest#split()} can split classes coming from a jar.
151      */
152     @Test
testSplit_withJar()153     public void testSplit_withJar() throws Exception {
154         File testJar = getJarResource(TEST_JAR1, mTestDir);
155         mTest = new JarHostTestable(mTestDir);
156         mTest.setBuild(mStubBuildInfo);
157         OptionSetter setter = new OptionSetter(mTest);
158         setter.setOptionValue("enable-pretty-logs", "false");
159         setter.setOptionValue("jar", testJar.getName());
160         // sharCount is ignored; will split by number of classes
161         List<IRemoteTest> res = (List<IRemoteTest>)mTest.split(1);
162         assertEquals(2, res.size());
163         assertTrue(res.get(0) instanceof JarHostTest);
164         assertEquals("[android.ui.cts.TaskSwitchingTest]",
165                 ((JarHostTest)res.get(0)).getClassNames().toString());
166         assertTrue(res.get(1) instanceof JarHostTest);
167         assertEquals("[android.ui.cts.InstallTimeTest]",
168                 ((JarHostTest)res.get(1)).getClassNames().toString());
169     }
170 
171     /**
172      * Testable version of {@link JarHostTest} that allows adding jar to classpath for testing
173      * purpose.
174      */
175     public static class JarHostTestLoader extends JarHostTestable {
176 
177         private static File mTestJar;
178 
JarHostTestLoader()179         public JarHostTestLoader() {}
180 
JarHostTestLoader(File testDir, File jar)181         public JarHostTestLoader(File testDir, File jar) {
182             super(testDir);
183             mTestJar = jar;
184         }
185 
186         @Override
getClassLoader()187         protected ClassLoader getClassLoader() {
188             ClassLoader child = super.getClassLoader();
189             try {
190                 child = new URLClassLoader(Arrays.asList(mTestJar.toURI().toURL())
191                         .toArray(new URL[]{}), super.getClassLoader());
192             } catch (MalformedURLException e) {
193                 CLog.e(e);
194             }
195             return child;
196         }
197     }
198 
199     /**
200      * If a jar file is not found, the countTest will fail but we still want to report a
201      * testRunStart and End pair for results.
202      */
203     @Test
testCountTestFails()204     public void testCountTestFails() throws Exception {
205         OptionSetter setter = new OptionSetter(mTest);
206         setter.setOptionValue("jar", "thisjardoesnotexistatall.jar");
207         mListener.testRunStarted(EasyMock.anyObject(), EasyMock.eq(0));
208         mListener.testRunEnded(EasyMock.anyLong(), (HashMap<String, Metric>) EasyMock.anyObject());
209         EasyMock.replay(mListener);
210         try {
211             mTest.run(mListener);
212             fail("Should have thrown an exception.");
213         } catch(RuntimeException expected) {
214             // expected
215         }
216         EasyMock.verify(mListener);
217     }
218 
219     /**
220      * Test that metrics from tests in JarHost are reported and accounted for.
221      */
222     @Test
testJarHostMetrics()223     public void testJarHostMetrics() throws Exception {
224         OptionSetter setter = new OptionSetter(mTest);
225         setter.setOptionValue("class", "com.android.compatibility.common.tradefed.testtype."
226                 + "JarHostTestTest$Junit4TestClass2");
227         mListener.testRunStarted(EasyMock.anyObject(), EasyMock.eq(1));
228         TestDescription tid = new TestDescription("com.android.compatibility.common.tradefed."
229                 + "testtype.JarHostTestTest$Junit4TestClass2", "testPass2");
230         mListener.testStarted(EasyMock.eq(tid), EasyMock.anyLong());
231         Map<String, String> metrics = new HashMap<>();
232         metrics.put("key", "value");
233         mListener.testEnded(EasyMock.eq(tid), EasyMock.anyLong(),
234                 EasyMock.eq(TfMetricProtoUtil.upgradeConvert(metrics)));
235         mListener.testRunEnded(EasyMock.anyLong(), (HashMap<String, Metric>) EasyMock.anyObject());
236         EasyMock.replay(mListener);
237         mTest.run(mListener);
238         EasyMock.verify(mListener);
239     }
240 }
241