1 /* 2 * Copyright (C) 2012 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 com.android.dx; 18 19 import android.os.Build; 20 import android.os.Process; 21 import org.junit.Test; 22 23 import java.io.File; 24 import java.util.Arrays; 25 import java.util.HashSet; 26 import java.util.Set; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertNotNull; 30 import static org.junit.Assert.assertTrue; 31 32 public final class AppDataDirGuesserTest { 33 @Test testGuessCacheDir_SimpleExample()34 public void testGuessCacheDir_SimpleExample() { 35 guessCacheDirFor("/data/app/a.b.c-xxx/base.apk") 36 .shouldGive("/data/data/a.b.c/cache"); 37 guessCacheDirFor("/data/app/a.b.c.tests-xxx/base.apk") 38 .shouldGive("/data/data/a.b.c.tests/cache"); 39 } 40 41 @Test testGuessCacheDir_MultipleResultsSeparatedByColon()42 public void testGuessCacheDir_MultipleResultsSeparatedByColon() { 43 guessCacheDirFor("/data/app/a.b.c-xxx/base.apk:/data/app/d.e.f-xxx/base.apk") 44 .shouldGive("/data/data/a.b.c/cache", "/data/data/d.e.f/cache"); 45 } 46 47 @Test testGuessCacheDir_NotWriteableSkipped()48 public void testGuessCacheDir_NotWriteableSkipped() { 49 guessCacheDirFor("/data/app/a.b.c-xxx/base.apk:/data/app/d.e.f-xxx/base.apk") 50 .withNonWriteable("/data/data/a.b.c/cache") 51 .shouldGive("/data/data/d.e.f/cache"); 52 } 53 54 @Test testGuessCacheDir_ForSecondaryUser()55 public void testGuessCacheDir_ForSecondaryUser() { 56 guessCacheDirFor("/data/app/a.b.c-xxx/base.apk:/data/app/d.e.f-xxx/base.apk") 57 .withNonWriteable("/data/data/a.b.c", "/data/data/d.e.f") 58 .withProcessUid(1110009) 59 .shouldGive("/data/user/11/a.b.c/cache", "/data/user/11/d.e.f/cache"); 60 } 61 62 @Test testGuessCacheDir_StripHyphenatedSuffixes()63 public void testGuessCacheDir_StripHyphenatedSuffixes() { 64 guessCacheDirFor("/data/app/a.b.c-2/base.apk") 65 .shouldGive("/data/data/a.b.c/cache"); 66 } 67 68 @Test testGuessCacheDir_LeadingAndTrailingColonsIgnored()69 public void testGuessCacheDir_LeadingAndTrailingColonsIgnored() { 70 guessCacheDirFor("/data/app/a.b.c-xxx/base.apk:asdf:") 71 .shouldGive("/data/data/a.b.c/cache"); 72 guessCacheDirFor(":asdf:/data/app/a.b.c-xxx/base.apk") 73 .shouldGive("/data/data/a.b.c/cache"); 74 } 75 76 @Test testGuessCacheDir_InvalidInputsGiveEmptyArray()77 public void testGuessCacheDir_InvalidInputsGiveEmptyArray() { 78 guessCacheDirFor("").shouldGive(); 79 } 80 81 @Test testGuessCacheDir_JarsIgnored()82 public void testGuessCacheDir_JarsIgnored() { 83 guessCacheDirFor("/data/app/a.b.c.jar").shouldGive(); 84 guessCacheDirFor("/system/framework/android.test.runner.jar").shouldGive(); 85 } 86 87 @Test testGuessCacheDir_RealWorldExample()88 public void testGuessCacheDir_RealWorldExample() { 89 String realPath = "/system/framework/android.test.runner.jar:" + 90 "/data/app/com.google.android.voicesearch.tests-2/base.apk:" + 91 "/data/app/com.google.android.voicesearch-1/base.apk"; 92 guessCacheDirFor(realPath) 93 .withNonWriteable("/data/data/com.google.android.voicesearch.tests/cache") 94 .shouldGive("/data/data/com.google.android.voicesearch/cache"); 95 } 96 97 @Test testGuessCacheDir_RealWorldExampleWithOneLevelSubDirectories()98 public void testGuessCacheDir_RealWorldExampleWithOneLevelSubDirectories() { 99 String realPath = "/system/framework/android.test.runner.jar:" + 100 "/data/app/com.google.android.voicesearch.tests-abcde/base.apk:" + 101 "/data/app/com.google.android.voicesearch-fghij/base.apk"; 102 guessCacheDirFor(realPath) 103 .withNonWriteable("/data/data/com.google.android.voicesearch.tests/cache") 104 .shouldGive("/data/data/com.google.android.voicesearch/cache"); 105 } 106 107 @Test testGuessCacheDir_RealWorldExampleWithTwoLevelSubDirectories()108 public void testGuessCacheDir_RealWorldExampleWithTwoLevelSubDirectories() { 109 String realPath = "/system/framework/android.test.runner.jar:" + 110 "/data/app/abcde/com.google.android.voicesearch.tests-fghij/base.apk:" + 111 "/data/app/klmno/com.google.android.voicesearch-pqrst/base.apk"; 112 guessCacheDirFor(realPath) 113 .withNonWriteable("/data/data/com.google.android.voicesearch.tests/cache") 114 .shouldGive("/data/data/com.google.android.voicesearch/cache"); 115 } 116 117 @Test testGuessCacheDir_RealWorldExampleWithHyphensInPath()118 public void testGuessCacheDir_RealWorldExampleWithHyphensInPath() { 119 String realPath = "/system/framework/android.test.runner.jar:" + 120 "/data/app/a-b-c/com.google.android.voicesearch.tests-e-f-g/base.apk:" + 121 "/data/app/com.google.android.voicesearch-k-l-n/base.apk"; 122 guessCacheDirFor(realPath) 123 .withNonWriteable("/data/data/com.google.android.voicesearch.tests/cache") 124 .shouldGive("/data/data/com.google.android.voicesearch/cache"); 125 } 126 127 @Test testSplitPathList()128 public void testSplitPathList() { 129 final String[] expected = { "foo", "bar" }; 130 assertTrue(Arrays.equals(expected, AppDataDirGuesser.splitPathList("foo:bar"))); 131 assertTrue(Arrays.equals(expected, 132 AppDataDirGuesser.splitPathList("dexPath=foo:bar"))); 133 assertTrue(Arrays.equals(expected, 134 AppDataDirGuesser.splitPathList("dexPath=foo:bar,bazPath=bar:bar2"))); 135 } 136 137 @Test testPre43PathProcessing()138 public void testPre43PathProcessing() { 139 String input = "dalvik.system.PathClassLoader[dexPath=/data/app/abc-1.apk," + 140 "libraryPath=/data/app-lib/abc-1]"; 141 String processed = AppDataDirGuesser.processClassLoaderString(input); 142 assertTrue("dexPath=/data/app/abc-1.apk,libraryPath=/data/app-lib/abc-1".equals(processed)); 143 } 144 145 @Test test43PathProcessing()146 public void test43PathProcessing() { 147 String input = "dalvik.system.PathClassLoader[DexPathList[[zip file " + 148 "\"/data/app/abc-1/base.apk\", zip file \"/data/app/def-1/base.apk\"], " + 149 "nativeLibraryDirectories=[/data/app-lib/abc-1]]]"; 150 String processed = AppDataDirGuesser.processClassLoaderString(input); 151 assertTrue("/data/app/abc-1/base.apk:/data/app/def-1/base.apk".equals(processed)); 152 } 153 154 @Test testApiLevel17PlusPathProcessing()155 public void testApiLevel17PlusPathProcessing() { 156 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 157 // Our processing should work for anything >= Android 4.2. 158 String input = getClass().getClassLoader().toString(); 159 String processed = AppDataDirGuesser.processClassLoaderString(input); 160 // TODO: this comment is no longer true now that we run tests on Android instead of vogar 161 // A tighter check would be interesting. But vogar doesn't run the tests in a directory 162 // recognized by the guesser (usually under /data/local/tmp), so we cannot use the 163 // processed result as input to guessPath. 164 assertTrue(!input.equals(processed)); 165 } 166 } 167 168 @Test testGetProcessUid()169 public void testGetProcessUid() { 170 AppDataDirGuesser guesser = new AppDataDirGuesser(); 171 assertTrue(guesser.getProcessUid() == Process.myUid()); 172 } 173 174 private interface TestCondition { withNonWriteable(String... files)175 TestCondition withNonWriteable(String... files); withProcessUid(Integer uid)176 TestCondition withProcessUid(Integer uid); shouldGive(String... files)177 void shouldGive(String... files); 178 } 179 guessCacheDirFor(final String path)180 private TestCondition guessCacheDirFor(final String path) { 181 final Set<String> notWriteable = new HashSet<>(); 182 return new TestCondition() { 183 private Integer processUid; 184 @Override 185 public void shouldGive(String... files) { 186 AppDataDirGuesser guesser = new AppDataDirGuesser() { 187 @Override 188 public boolean isWriteableDirectory(File file) { 189 return !notWriteable.contains(file.getAbsolutePath()); 190 } 191 @Override 192 boolean fileOrDirExists(File file) { 193 return true; 194 } 195 @Override 196 Integer getProcessUid() { 197 return processUid; 198 } 199 }; 200 File[] results = guesser.guessPath(path); 201 assertNotNull("Null results for " + path, results); 202 assertEquals("Bad lengths for " + path, files.length, results.length); 203 for (int i = 0; i < files.length; ++i) { 204 assertEquals("Element " + i, new File(files[i]), results[i]); 205 } 206 } 207 208 @Override 209 public TestCondition withNonWriteable(String... files) { 210 notWriteable.addAll(Arrays.asList(files)); 211 return this; 212 } 213 214 @Override 215 public TestCondition withProcessUid(Integer uid) { 216 processUid = uid; 217 return this; 218 } 219 }; 220 } 221 } 222