• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 android.compilation.cts.statuscheckerapp;
18 
19 import static dalvik.system.DexFile.OptimizationInfo;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.os.Bundle;
26 
27 import androidx.test.core.app.ApplicationProvider;
28 import androidx.test.filters.SmallTest;
29 import androidx.test.platform.app.InstrumentationRegistry;
30 import androidx.test.runner.AndroidJUnit4;
31 
32 import dalvik.system.ApplicationRuntime;
33 import dalvik.system.BaseDexClassLoader;
34 import dalvik.system.DexFile;
35 import dalvik.system.PathClassLoader;
36 import dalvik.system.VMDebug;
37 import dalvik.system.VMRuntime;
38 
39 import com.google.common.io.ByteStreams;
40 import com.google.common.truth.Correspondence;
41 
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 
45 import java.io.File;
46 import java.io.FileOutputStream;
47 import java.io.InputStream;
48 import java.io.OutputStream;
49 import java.lang.reflect.Method;
50 import java.nio.file.Paths;
51 import java.util.Map;
52 import java.util.function.BiFunction;
53 import java.util.regex.Pattern;
54 
55 /**
56  * An instrumentation test that checks optimization status.
57  */
58 @SmallTest
59 @RunWith(AndroidJUnit4.class)
60 public class StatusCheckerAppTest {
61     private static final String TAG = "StatusCheckerAppTest";
62     private static final String SECONDARY_DEX_RES = "/StatusCheckerApp_Secondary.jar";
63 
64     @Test
checkStatus()65     public void checkStatus() throws Exception {
66         Bundle bundle = InstrumentationRegistry.getArguments();
67         OptimizationInfo info = ApplicationRuntime.getBaseApkOptimizationInfo();
68         assertThat(info.getStatus()).isEqualTo(bundle.getString("compiler-filter"));
69         assertThat(info.getReason()).isEqualTo(bundle.getString("compilation-reason"));
70         if (bundle.containsKey("is-verified")) {
71             assertThat(info.isVerified()).isEqualTo(bundle.getString("is-verified").equals("true"));
72         }
73         if (bundle.containsKey("is-optimized")) {
74             assertThat(info.isOptimized())
75                     .isEqualTo(bundle.getString("is-optimized").equals("true"));
76         }
77         if (bundle.containsKey("is-fully-compiled")) {
78             assertThat(info.isFullyCompiled())
79                     .isEqualTo(bundle.getString("is-fully-compiled").equals("true"));
80         }
81     }
82 
83     @Test
createAndLoadSecondaryDex()84     public void createAndLoadSecondaryDex() throws Exception {
85         Bundle bundle = InstrumentationRegistry.getArguments();
86         String secondaryDexFilename = bundle.getString("secondary-dex-filename");
87         createAndLoadSecondaryDex(secondaryDexFilename, PathClassLoader::new);
88     }
89 
90     @Test
createAndLoadSecondaryDexUnsupportedClassLoader()91     public void createAndLoadSecondaryDexUnsupportedClassLoader() throws Exception {
92         Bundle bundle = InstrumentationRegistry.getArguments();
93         String secondaryDexFilename = bundle.getString("secondary-dex-filename");
94         createAndLoadSecondaryDex(secondaryDexFilename, CustomClassLoader::new);
95     }
96 
createAndLoadSecondaryDex(String secondaryDexFilename, BiFunction<String, ClassLoader, ClassLoader> classLoaderCtor)97     private String createAndLoadSecondaryDex(String secondaryDexFilename,
98             BiFunction<String, ClassLoader, ClassLoader> classLoaderCtor) throws Exception {
99         File secondaryDexFile =
100                 Paths.get(getApplicationInfo().dataDir, secondaryDexFilename).toFile();
101         if (secondaryDexFile.exists()) {
102             secondaryDexFile.delete();
103         }
104         copyResourceToFile(SECONDARY_DEX_RES, secondaryDexFile);
105         assertThat(secondaryDexFile.setReadOnly()).isTrue();
106         ClassLoader unused = classLoaderCtor.apply(
107                 secondaryDexFile.getAbsolutePath(),
108                 this.getClass().getClassLoader());
109         return secondaryDexFile.getAbsolutePath();
110     }
111 
getApplicationInfo()112     private ApplicationInfo getApplicationInfo() {
113         Context context = ApplicationProvider.getApplicationContext();
114         return context.getApplicationInfo();
115     }
116 
117     @Test
testSecondaryDexReporting()118     public void testSecondaryDexReporting() throws Exception {
119         String dataDir = getApplicationInfo().dataDir;
120         var reporter =
121                 (BaseDexClassLoader.Reporter) BaseDexClassLoader.class.getMethod("getReporter")
122                         .invoke(null);
123 
124         // Invalid dex paths. The binder calls should be rejected, though we won't see any failure
125         // on the client side because the calls are oneway.
126         reporter.report(Map.of("relative/reported_bad_1.apk", "PCL[]"));
127         reporter.report(
128                 Map.of(Paths.get(dataDir, "non-normal/./reported_bad_2.apk").toString(), "PCL[]"));
129 
130         // Invalid class loader contexts. The binder calls should be rejected too.
131         reporter.report(Map.of(Paths.get(dataDir, "reported_bad_3.apk").toString(), "ABC"));
132         reporter.report(
133                 Map.of(Paths.get(dataDir, "reported_bad_4.apk").toString(), "PCL[./bar.jar]"));
134 
135         // Valid paths and class loader contexts.
136         reporter.report(Map.of(Paths.get(dataDir, "reported_good_1.apk").toString(), "PCL[]"));
137         reporter.report(
138                 Map.of(Paths.get(dataDir, "reported_good_2.apk").toString(), "PCL[bar.jar]"));
139         reporter.report(Map.of(Paths.get(dataDir, "reported_good_3.apk").toString(),
140                 "=UnsupportedClassLoaderContext="));
141     }
142 
143     @Test
testGetDexFileOutputPaths()144     public void testGetDexFileOutputPaths() throws Exception {
145         String[] paths = DexFile.getDexFileOutputPaths(
146                 getApplicationInfo().sourceDir, VMRuntime.getRuntime().vmInstructionSet());
147 
148         // We can't be too specific because the paths are ART-internal and are subject to change.
149         assertThat(paths)
150                 .asList()
151                 .comparingElementsUsing(Correspondence.from(String::endsWith, "ends with"))
152                 .containsAtLeast(".odex", ".vdex");
153     }
154 
155     @Test
checkExecutableMethodFileOffsets()156     public void checkExecutableMethodFileOffsets() throws Exception {
157         Bundle bundle = InstrumentationRegistry.getArguments();
158         // Check a method in `cts/hostsidetests/compilation/assets/status_checker_app.prof.txt`.
159         Method method = getClass().getMethod("checkStatus");
160         VMDebug.ExecutableMethodFileOffsets fileOffsets =
161                 VMDebug.getExecutableMethodFileOffsets(method);
162         assertThat(fileOffsets.getContainerPath())
163                 .containsMatch(Pattern.compile(bundle.getString("container-path-pattern")));
164         assertThat(fileOffsets.getMethodOffset()).isGreaterThan(0);
165     }
166 
copyResourceToFile(String resourceName, File file)167     public File copyResourceToFile(String resourceName, File file) throws Exception {
168         try (OutputStream outputStream = new FileOutputStream(file);
169                 InputStream inputStream = getClass().getResourceAsStream(resourceName)) {
170             assertThat(ByteStreams.copy(inputStream, outputStream)).isGreaterThan(0);
171         }
172         return file;
173     }
174 
175     // A custom class loader that is unsupported by CLC encoding.
176     public class CustomClassLoader extends PathClassLoader {
CustomClassLoader(String dexPath, ClassLoader parent)177         public CustomClassLoader(String dexPath, ClassLoader parent) {
178             super(dexPath, parent);
179         }
180     }
181 }
182