• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.signature.cts.api;
17 
18 import android.os.Bundle;
19 import android.signature.cts.ApiDocumentParser;
20 import android.signature.cts.ClassProvider;
21 import android.signature.cts.ExcludingClassProvider;
22 import android.signature.cts.FailureType;
23 import android.signature.cts.JDiffClassDescription;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.PrintWriter;
29 import java.io.StringWriter;
30 import java.nio.ByteBuffer;
31 import java.nio.channels.FileChannel;
32 import java.nio.file.Files;
33 import java.nio.file.StandardOpenOption;
34 import java.util.EnumSet;
35 import java.util.HashSet;
36 import java.util.Set;
37 import java.util.stream.Stream;
38 import java.util.zip.ZipFile;
39 import org.xmlpull.v1.XmlPullParserException;
40 import repackaged.android.test.InstrumentationTestCase;
41 import repackaged.android.test.InstrumentationTestRunner;
42 
43 import static android.signature.cts.CurrentApi.API_FILE_DIRECTORY;
44 
45 /**
46  */
47 public class AbstractApiTest extends InstrumentationTestCase {
48 
49     private TestResultObserver mResultObserver;
50 
51     ClassProvider classProvider;
52 
53     @Override
setUp()54     protected void setUp() throws Exception {
55         super.setUp();
56         mResultObserver = new TestResultObserver();
57 
58         // Get the arguments passed to the instrumentation.
59         Bundle instrumentationArgs =
60                 ((InstrumentationTestRunner) getInstrumentation()).getArguments();
61 
62         // Prepare for a class provider that loads classes from bootclasspath but filters
63         // out known inaccessible classes.
64         // Note that com.android.internal.R.* inner classes are also excluded as they are
65         // not part of API though exist in the runtime.
66         classProvider = new ExcludingClassProvider(
67                 new BootClassPathClassesProvider(),
68                 name -> name != null && name.startsWith("com.android.internal.R."));
69 
70         initializeFromArgs(instrumentationArgs);
71     }
72 
initializeFromArgs(Bundle instrumentationArgs)73     protected void initializeFromArgs(Bundle instrumentationArgs) throws Exception {
74 
75     }
76 
77     protected interface RunnableWithTestResultObserver {
run(TestResultObserver observer)78         void run(TestResultObserver observer) throws Exception;
79     }
80 
runWithTestResultObserver(RunnableWithTestResultObserver runnable)81     void runWithTestResultObserver(RunnableWithTestResultObserver runnable) {
82         try {
83             runnable.run(mResultObserver);
84         } catch (Exception e) {
85             StringWriter writer = new StringWriter();
86             writer.write(e.toString());
87             writer.write("\n");
88             e.printStackTrace(new PrintWriter(writer));
89             mResultObserver.notifyFailure(FailureType.CAUGHT_EXCEPTION, e.getClass().getName(),
90                     writer.toString());
91         }
92         if (mResultObserver.mDidFail) {
93             StringBuilder errorString = mResultObserver.mErrorString;
94             ClassLoader classLoader = getClass().getClassLoader();
95             errorString.append("\nClassLoader hierarchy\n");
96             while (classLoader != null) {
97                 errorString.append("    ").append(classLoader).append("\n");
98                 classLoader = classLoader.getParent();
99             }
100             fail(errorString.toString());
101         }
102     }
103 
getCommaSeparatedList(Bundle instrumentationArgs, String key)104     static String[] getCommaSeparatedList(Bundle instrumentationArgs, String key) {
105         String argument = instrumentationArgs.getString(key);
106         if (argument == null) {
107             return new String[0];
108         }
109         return argument.split(",");
110     }
111 
readFileOptimized(File file)112     Stream<Object> readFileOptimized(File file) {
113         try {
114             if (file.getName().endsWith(".zip")) {
115                 @SuppressWarnings("resource")
116                 ZipFile zip = new ZipFile(file);
117                 return zip.stream().map(entry -> {
118                     try {
119                         return zip.getInputStream(entry);
120                     } catch (IOException e) {
121                         throw new RuntimeException(e);
122                     }
123                 });
124             } else {
125                 try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(file.toPath(),
126                         EnumSet.of(StandardOpenOption.READ))) {
127                     ByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0,
128                             fileChannel.size());
129                     if (mappedByteBuffer == null) {
130                         throw new IllegalStateException("Could not map " + file);
131                     }
132                     return Stream.of(mappedByteBuffer);
133                 }
134             }
135         } catch (IOException e) {
136             throw new RuntimeException(e);
137         }
138     }
139     Stream<InputStream> readFile(File file) {
140         try {
141             if (file.getName().endsWith(".zip")) {
142                 @SuppressWarnings("resource")
143                 ZipFile zip = new ZipFile(file);
144                 return zip.stream().map(entry -> {
145                     try {
146                         return zip.getInputStream(entry);
147                     } catch (IOException e) {
148                         throw new RuntimeException(e);
149                     }});
150             } else {
151                 return Stream.of(new FileInputStream(file));
152             }
153         } catch (IOException e) {
154             throw new RuntimeException(e);
155         }
156     }
157 
158     Stream<JDiffClassDescription> parseApiFilesAsStream(
159             ApiDocumentParser apiDocumentParser, String[] apiFiles) {
160         return Stream.of(apiFiles)
161                 .map(name -> new File(API_FILE_DIRECTORY + "/" + name))
162                 .flatMap(this::readFile)
163                 .flatMap(stream -> {
164                     try {
165                         return apiDocumentParser.parseAsStream(stream);
166                     } catch (IOException | XmlPullParserException e) {
167                         throw new RuntimeException(e);
168                     }
169                 });
170     }
171 }
172