1 /* 2 * Copyright (C) 2017 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.presubmit; 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.testtype.suite.TestSuiteInfo; 23 import com.android.tradefed.util.AaptParser; 24 import com.android.tradefed.util.AbiUtils; 25 26 import org.junit.Test; 27 import org.junit.runner.RunWith; 28 import org.junit.runners.JUnit4; 29 30 import java.io.File; 31 import java.io.FilenameFilter; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 import java.util.Set; 40 41 /** 42 * Tests to validate that the build is containing usable test artifact. 43 */ 44 @RunWith(JUnit4.class) 45 public class ValidateTestsAbi { 46 47 private static final Set<String> MODULE_EXCEPTIONS = new HashSet<>(); 48 static { 49 /** 50 * This particular module is shipping all it's dependencies in all abis with prebuilt stuff. 51 * Excluding it for now to have the test setup. 52 */ 53 MODULE_EXCEPTIONS.add("CtsSplitApp"); 54 55 /** 56 * This module tests for security vulnerabilities when installing attacker-devised APKs. 57 */ 58 MODULE_EXCEPTIONS.add("CtsCorruptApkTests"); 59 } 60 61 private static final Set<String> BINARY_EXCEPTIONS = new HashSet<>(); 62 static { 63 /** 64 * This binary is a host side helper, so we do not need to check it. 65 */ 66 BINARY_EXCEPTIONS.add("sepolicy-analyze"); 67 } 68 69 /** 70 * Test that all apks have the same supported abis. 71 * Sometimes, if a module is missing LOCAL_MULTILIB := both, we will end up with only one of 72 * the two abis required and the second one will fail. 73 */ 74 @Test 75 public void testApksAbis() { 76 String ctsRoot = System.getProperty("CTS_ROOT"); 77 File testcases = new File(ctsRoot, "/android-cts/testcases/"); 78 if (!testcases.exists()) { 79 fail(String.format("%s does not exists", testcases)); 80 return; 81 } 82 File[] listApks = testcases.listFiles(new FilenameFilter() { 83 @Override 84 public boolean accept(File dir, String name) { 85 for (String module : MODULE_EXCEPTIONS) { 86 if (name.startsWith(module)) { 87 return false; 88 } 89 } 90 91 return name.endsWith(".apk"); 92 } 93 }); 94 assertTrue(listApks.length > 0); 95 int maxAbi = 0; 96 Map<String, Integer> apkToAbi = new HashMap<>(); 97 98 for (File testApk : listApks) { 99 AaptParser result = AaptParser.parse(testApk); 100 // Retry as we have seen flake with aapt sometimes. 101 if (result == null) { 102 for (int i = 0; i < 2; i++) { 103 result = AaptParser.parse(testApk); 104 if (result != null) { 105 break; 106 } 107 } 108 // If still couldn't parse the apk 109 if (result == null) { 110 fail(String.format("Fail to run 'aapt dump badging %s'", 111 testApk.getAbsolutePath())); 112 } 113 } 114 // We only check the apk that have native code 115 if (!result.getNativeCode().isEmpty()) { 116 List<String> supportedAbiApk = result.getNativeCode(); 117 Set<String> buildTarget = AbiUtils.getAbisForArch( 118 TestSuiteInfo.getInstance().getTargetArch()); 119 // first check, all the abis are supported 120 for (String abi : supportedAbiApk) { 121 if (!buildTarget.contains(abi)) { 122 fail(String.format("apk %s %s does not support our abis [%s]", 123 testApk.getName(), supportedAbiApk, buildTarget)); 124 } 125 } 126 apkToAbi.put(testApk.getName(), supportedAbiApk.size()); 127 maxAbi = Math.max(maxAbi, supportedAbiApk.size()); 128 } 129 } 130 131 // We do a second pass to make sure nobody is short on abi 132 for (Entry<String, Integer> apk : apkToAbi.entrySet()) { 133 if (apk.getValue() < maxAbi) { 134 fail(String.format("apk %s only has %s abi when it should have %s", apk.getKey(), 135 apk.getValue(), maxAbi)); 136 } 137 } 138 } 139 140 /** 141 * Test that when CTS has multiple abis, we have binary for each ABI. In this case the abi will 142 * be the same with different bitness (only case supported by build system). 143 * <p/> 144 * If there is only one bitness, then we check that it's the right one. 145 */ 146 @Test 147 public void testBinariesAbis() { 148 String ctsRoot = System.getProperty("CTS_ROOT"); 149 File testcases = new File(ctsRoot, "/android-cts/testcases/"); 150 if (!testcases.exists()) { 151 fail(String.format("%s does not exist", testcases)); 152 return; 153 } 154 String[] listBinaries = testcases.list(new FilenameFilter() { 155 @Override 156 public boolean accept(File dir, String name) { 157 if (name.contains(".")) { 158 return false; 159 } 160 if (BINARY_EXCEPTIONS.contains(name)) { 161 return false; 162 } 163 File file = new File(dir, name); 164 if (file.isDirectory()) { 165 return false; 166 } 167 if (!file.canExecute()) { 168 return false; 169 } 170 return true; 171 } 172 }); 173 assertTrue(listBinaries.length > 0); 174 List<String> orderedList = Arrays.asList(listBinaries); 175 // we sort to have binary starting with same name, next to each other. The last two 176 // characters of their name with be the bitness (32 or 64). 177 Collections.sort(orderedList); 178 Set<String> buildTarget = AbiUtils.getAbisForArch( 179 TestSuiteInfo.getInstance().getTargetArch()); 180 // We expect one binary per abi of CTS, they should be appended with 32 or 64 181 for (int i = 0; i < orderedList.size(); i=i + buildTarget.size()) { 182 List<String> subSet = orderedList.subList(i, i + buildTarget.size()); 183 if (subSet.size() > 1) { 184 String base = subSet.get(0).substring(0, subSet.get(0).length() - 2); 185 for (int j = 0; j < subSet.size(); j++) { 186 assertEquals(base, subSet.get(j).substring(0, subSet.get(j).length() - 2)); 187 } 188 } else { 189 String bitness = AbiUtils.getBitness(buildTarget.iterator().next()); 190 assertTrue(subSet.get(i).endsWith(bitness)); 191 } 192 } 193 } 194 } 195