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