Home | History | Annotate | Download | only in repository
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
      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.sdklib.internal.repository;
     18 
     19 import com.android.sdklib.SdkManager;
     20 import com.android.sdklib.repository.SdkAddonConstants;
     21 import com.android.util.Pair;
     22 
     23 import org.w3c.dom.Document;
     24 
     25 import java.io.ByteArrayInputStream;
     26 import java.io.File;
     27 import java.io.FileInputStream;
     28 import java.io.IOException;
     29 import java.io.InputStream;
     30 import java.util.ArrayList;
     31 import java.util.Arrays;
     32 
     33 import junit.framework.TestCase;
     34 
     35 /**
     36  * Tests for {@link SdkAddonSource}
     37  */
     38 public class SdkAddonSourceTest extends TestCase {
     39 
     40     /**
     41      * An internal helper class to give us visibility to the protected members we want
     42      * to test.
     43      */
     44     private static class MockSdkAddonSource extends SdkAddonSource {
     45         public MockSdkAddonSource() {
     46             super("fake-url", null /*uiName*/);
     47         }
     48 
     49         public Document _findAlternateToolsXml(InputStream xml) {
     50             return super.findAlternateToolsXml(xml);
     51         }
     52 
     53         public boolean _parsePackages(Document doc, String nsUri, ITaskMonitor monitor) {
     54             return super.parsePackages(doc, nsUri, monitor);
     55         }
     56 
     57         public int _getXmlSchemaVersion(InputStream xml) {
     58             return super.getXmlSchemaVersion(xml);
     59         }
     60 
     61         public String _validateXml(InputStream xml, String url, int version,
     62                                    String[] outError, Boolean[] validatorFound) {
     63             return super.validateXml(xml, url, version, outError, validatorFound);
     64         }
     65 
     66         public Document _getDocument(InputStream xml, ITaskMonitor monitor) {
     67             return super.getDocument(xml, monitor);
     68         }
     69 
     70     }
     71 
     72     private MockSdkAddonSource mSource;
     73 
     74     @Override
     75     protected void setUp() throws Exception {
     76         super.setUp();
     77 
     78         mSource = new MockSdkAddonSource();
     79     }
     80 
     81     @Override
     82     protected void tearDown() throws Exception {
     83         super.tearDown();
     84 
     85         mSource = null;
     86     }
     87 
     88     public void testFindAlternateToolsXml_Errors() throws Exception {
     89         // Support null as input
     90         Document result = mSource._findAlternateToolsXml(null);
     91         assertNull(result);
     92 
     93         // Support an empty input
     94         String str = "";
     95         ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes());
     96         result = mSource._findAlternateToolsXml(input);
     97         assertNull(result);
     98 
     99         // Support a random string as input
    100         str = "Some random string, not even HTML nor XML";
    101         input = new ByteArrayInputStream(str.getBytes());
    102         result = mSource._findAlternateToolsXml(input);
    103         assertNull(result);
    104 
    105         // Support an HTML input, e.g. a typical 404 document as returned by DL
    106         str = "<html><head> " +
    107         "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\"> " +
    108         "<title>404 Not Found</title> " + "<style><!--" + "body {font-family: arial,sans-serif}" +
    109         "div.nav { ... blah blah more css here ... color: green}" +
    110         "//--></style> " + "<script><!--" + "var rc=404;" + "//-->" + "</script> " + "</head> " +
    111         "<body text=#000000 bgcolor=#ffffff> " +
    112         "<table border=0 cellpadding=2 cellspacing=0 width=100%><tr><td rowspan=3 width=1% nowrap> " +
    113         "<b><font face=times color=#0039b6 size=10>G</font><font face=times color=#c41200 size=10>o</font><font face=times color=#f3c518 size=10>o</font><font face=times color=#0039b6 size=10>g</font><font face=times color=#30a72f size=10>l</font><font face=times color=#c41200 size=10>e</font>&nbsp;&nbsp;</b> " +
    114         "<td>&nbsp;</td></tr> " +
    115         "<tr><td bgcolor=\"#3366cc\"><font face=arial,sans-serif color=\"#ffffff\"><b>Error</b></td></tr> " +
    116         "<tr><td>&nbsp;</td></tr></table> " + "<blockquote> " + "<H1>Not Found</H1> " +
    117         "The requested URL <code>/404</code> was not found on this server." + " " + "<p> " +
    118         "</blockquote> " +
    119         "<table width=100% cellpadding=0 cellspacing=0><tr><td bgcolor=\"#3366cc\"><img alt=\"\" width=1 height=4></td></tr></table> " +
    120         "</body></html> ";
    121         input = new ByteArrayInputStream(str.getBytes());
    122         result = mSource._findAlternateToolsXml(input);
    123         assertNull(result);
    124 
    125         // Support some random XML document, totally unrelated to our sdk-repository schema
    126         str = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
    127         "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"" +
    128         "    package=\"some.cool.app\" android:versionName=\"1.6.04\" android:versionCode=\"1604\">" +
    129         "    <application android:label=\"@string/app_name\" android:icon=\"@drawable/icon\"/>" +
    130         "</manifest>";
    131         input = new ByteArrayInputStream(str.getBytes());
    132         result = mSource._findAlternateToolsXml(input);
    133         assertNull(result);
    134     }
    135 
    136     /**
    137      * Validate that findAlternateToolsXml doesn't work for addon even
    138      * when trying to load a valid addon xml.
    139      */
    140     public void testFindAlternateToolsXml_1() throws Exception {
    141         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_1.xml");
    142 
    143         Document result = mSource._findAlternateToolsXml(xmlStream);
    144         assertNull(result);
    145     }
    146 
    147     /**
    148      * Validate we can still load a valid add-on schema version 1
    149      */
    150     public void testLoadOldXml_1() throws Exception {
    151         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_1.xml");
    152 
    153         // guess the version from the XML document
    154         int version = mSource._getXmlSchemaVersion(xmlStream);
    155         assertEquals(1, version);
    156 
    157         Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };
    158         String[] validationError = new String[] { null };
    159         String url = "not-a-valid-url://" + SdkAddonConstants.URL_DEFAULT_FILENAME;
    160 
    161         String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);
    162         assertEquals(Boolean.TRUE, validatorFound[0]);
    163         assertEquals(null, validationError[0]);
    164         assertEquals(SdkAddonConstants.getSchemaUri(1), uri);
    165 
    166         // Validation was successful, load the document
    167         MockMonitor monitor = new MockMonitor();
    168         Document doc = mSource._getDocument(xmlStream, monitor);
    169         assertNotNull(doc);
    170 
    171         // Get the packages
    172         assertTrue(mSource._parsePackages(doc, uri, monitor));
    173 
    174         assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +
    175                      "Found My Second add-on by John Deer, Android API 2, revision 42\n" +
    176                      "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +
    177                      "Found G USB Driver package, revision 43 (Obsolete)\n" +
    178                      "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
    179                      "Found Unkown Extra package, revision 2 (Obsolete)\n",
    180                 monitor.getCapturedVerboseLog());
    181         assertEquals("", monitor.getCapturedLog());
    182         assertEquals("", monitor.getCapturedErrorLog());
    183 
    184         // check the packages we found... we expected to find 11 packages with each at least
    185         // one archive.
    186         Package[] pkgs = mSource.getPackages();
    187         assertEquals(6, pkgs.length);
    188         for (Package p : pkgs) {
    189             assertTrue(p.getArchives().length >= 1);
    190         }
    191 
    192         // Check the extra packages path, vendor, install folder
    193 
    194         final String osSdkPath = "SDK";
    195         final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);
    196 
    197         ArrayList<String> extraPaths   = new ArrayList<String>();
    198         ArrayList<String> extraVendors = new ArrayList<String>();
    199         ArrayList<File>   extraInstall = new ArrayList<File>();
    200         for (Package p : pkgs) {
    201             if (p instanceof ExtraPackage) {
    202                 extraPaths.add(((ExtraPackage) p).getPath());
    203                 extraVendors.add(((ExtraPackage) p).getVendor());
    204                 extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));
    205             }
    206         }
    207         assertEquals(
    208                 "[extra_api_dep, usb_driver, extra0000005f]",
    209                 Arrays.toString(extraPaths.toArray()));
    210         assertEquals(
    211                 "[android_vendor, g, vendor0000005f]",
    212                 Arrays.toString(extraVendors.toArray()));
    213         assertEquals(
    214                 ("[SDK/extras/android_vendor/extra_api_dep, " +
    215                   "SDK/extras/g/usb_driver, " +
    216                   "SDK/extras/vendor0000005f/extra0000005f]").replace('/', File.separatorChar),
    217                 Arrays.toString(extraInstall.toArray()));
    218     }
    219 
    220     /**
    221      * Validate we can still load a valid add-on schema version 2
    222      */
    223     public void testLoadOldXml_2() throws Exception {
    224         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_2.xml");
    225 
    226         // guess the version from the XML document
    227         int version = mSource._getXmlSchemaVersion(xmlStream);
    228         assertEquals(2, version);
    229 
    230         Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };
    231         String[] validationError = new String[] { null };
    232         String url = "not-a-valid-url://" + SdkAddonConstants.URL_DEFAULT_FILENAME;
    233 
    234         String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);
    235         assertEquals(Boolean.TRUE, validatorFound[0]);
    236         assertEquals(null, validationError[0]);
    237         assertEquals(SdkAddonConstants.getSchemaUri(2), uri);
    238 
    239         // Validation was successful, load the document
    240         MockMonitor monitor = new MockMonitor();
    241         Document doc = mSource._getDocument(xmlStream, monitor);
    242         assertNotNull(doc);
    243 
    244         // Get the packages
    245         assertTrue(mSource._parsePackages(doc, uri, monitor));
    246 
    247         assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +
    248                      "Found My Second add-on by John Deer, Android API 2, revision 42\n" +
    249                      "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +
    250                      "Found G USB Driver package, revision 43 (Obsolete)\n" +
    251                      "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
    252                      "Found Unkown Extra package, revision 2 (Obsolete)\n",
    253                 monitor.getCapturedVerboseLog());
    254         assertEquals("", monitor.getCapturedLog());
    255         assertEquals("", monitor.getCapturedErrorLog());
    256 
    257         // check the packages we found... we expected to find 11 packages with each at least
    258         // one archive.
    259         // Note the order doesn't necessary match the one from the
    260         // assertEquald(getCapturedVerboseLog) because packages are sorted using the
    261         // Packages' sorting order, e.g. all platforms are sorted by descending API level, etc.
    262         Package[] pkgs = mSource.getPackages();
    263 
    264         assertEquals(6, pkgs.length);
    265         for (Package p : pkgs) {
    266             assertTrue(p.getArchives().length >= 1);
    267         }
    268 
    269         // Check the layoutlib of the platform packages.
    270         ArrayList<Pair<Integer, Integer>> layoutlibVers = new ArrayList<Pair<Integer,Integer>>();
    271         for (Package p : pkgs) {
    272             if (p instanceof AddonPackage) {
    273                 layoutlibVers.add(((AddonPackage) p).getLayoutlibVersion());
    274             }
    275         }
    276         assertEquals(
    277                  "[Pair [first=3, second=42], " +         // for #3 "This add-on has no libraries"
    278                  "Pair [first=0, second=0], " +           // for #2 "My Second add-on"
    279                  "Pair [first=5, second=0]]",            // for #1 "My First add-on"
    280                 Arrays.toString(layoutlibVers.toArray()));
    281 
    282 
    283         // Check the extra packages path, vendor, install folder
    284 
    285         final String osSdkPath = "SDK";
    286         final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);
    287 
    288         ArrayList<String> extraPaths   = new ArrayList<String>();
    289         ArrayList<String> extraVendors = new ArrayList<String>();
    290         ArrayList<File>   extraInstall = new ArrayList<File>();
    291         for (Package p : pkgs) {
    292             if (p instanceof ExtraPackage) {
    293                 extraPaths.add(((ExtraPackage) p).getPath());
    294                 extraVendors.add(((ExtraPackage) p).getVendor());
    295                 extraInstall.add(((ExtraPackage) p).getInstallFolder(osSdkPath, sdkManager));
    296             }
    297         }
    298         assertEquals(
    299                 "[extra_api_dep, usb_driver, extra0000005f]",
    300                 Arrays.toString(extraPaths.toArray()));
    301         assertEquals(
    302                 "[android_vendor, g, vendor0000005f]",
    303                 Arrays.toString(extraVendors.toArray()));
    304         assertEquals(
    305                 ("[SDK/extras/android_vendor/extra_api_dep, " +
    306                   "SDK/extras/g/usb_driver, " +
    307                   "SDK/extras/vendor0000005f/extra0000005f]").replace('/', File.separatorChar),
    308                 Arrays.toString(extraInstall.toArray()));
    309     }
    310 
    311     /**
    312      * Validate we can still load a valid add-on schema version 3
    313      */
    314     public void testLoadOldXml_3() throws Exception {
    315         InputStream xmlStream = getTestResource("/com/android/sdklib/testdata/addon_sample_3.xml");
    316 
    317         // guess the version from the XML document
    318         int version = mSource._getXmlSchemaVersion(xmlStream);
    319         assertEquals(3, version);
    320 
    321         Boolean[] validatorFound = new Boolean[] { Boolean.FALSE };
    322         String[] validationError = new String[] { null };
    323         String url = "not-a-valid-url://" + SdkAddonConstants.URL_DEFAULT_FILENAME;
    324 
    325         String uri = mSource._validateXml(xmlStream, url, version, validationError, validatorFound);
    326         assertEquals(Boolean.TRUE, validatorFound[0]);
    327         assertEquals(null, validationError[0]);
    328         assertEquals(SdkAddonConstants.getSchemaUri(3), uri);
    329 
    330         // Validation was successful, load the document
    331         MockMonitor monitor = new MockMonitor();
    332         Document doc = mSource._getDocument(xmlStream, monitor);
    333         assertNotNull(doc);
    334 
    335         // Get the packages
    336         assertTrue(mSource._parsePackages(doc, uri, monitor));
    337 
    338         assertEquals("Found My First add-on by John Doe, Android API 1, revision 1\n" +
    339                      "Found My Second add-on by John Deer, Android API 2, revision 42\n" +
    340                      "Found This add-on has no libraries by Joe Bar, Android API 4, revision 3\n" +
    341                      "Found G USB Driver package, revision 43 (Obsolete)\n" +
    342                      "Found Android Vendor Extra API Dep package, revision 2 (Obsolete)\n" +
    343                      "Found Unkown Extra package, revision 2 (Obsolete)\n",
    344                 monitor.getCapturedVerboseLog());
    345         assertEquals("", monitor.getCapturedLog());
    346         assertEquals("", monitor.getCapturedErrorLog());
    347 
    348         // check the packages we found... we expected to find 6 packages with each at least
    349         // one archive.
    350         // Note the order doesn't necessary match the one from the
    351         // assertEquald(getCapturedVerboseLog) because packages are sorted using the
    352         // Packages' sorting order, e.g. all platforms are sorted by descending API level, etc.
    353         Package[] pkgs = mSource.getPackages();
    354 
    355         assertEquals(6, pkgs.length);
    356         for (Package p : pkgs) {
    357             assertTrue(p.getArchives().length >= 1);
    358         }
    359 
    360         // Check the layoutlib of the platform packages.
    361         ArrayList<Pair<Integer, Integer>> layoutlibVers = new ArrayList<Pair<Integer,Integer>>();
    362         for (Package p : pkgs) {
    363             if (p instanceof AddonPackage) {
    364                 layoutlibVers.add(((AddonPackage) p).getLayoutlibVersion());
    365             }
    366         }
    367         assertEquals(
    368                  "[Pair [first=3, second=42], " +         // for #3 "This add-on has no libraries"
    369                  "Pair [first=0, second=0], " +           // for #2 "My Second add-on"
    370                  "Pair [first=5, second=0]]",            // for #1 "My First add-on"
    371                 Arrays.toString(layoutlibVers.toArray()));
    372 
    373 
    374         // Check the extra packages: path, vendor, install folder, old-paths
    375 
    376         final String osSdkPath = "SDK";
    377         final SdkManager sdkManager = new MockEmptySdkManager(osSdkPath);
    378 
    379         ArrayList<String> extraPaths   = new ArrayList<String>();
    380         ArrayList<String> extraVendors = new ArrayList<String>();
    381         ArrayList<File>   extraInstall = new ArrayList<File>();
    382         ArrayList<ArrayList<String>> extraFilePaths = new ArrayList<ArrayList<String>>();
    383         for (Package p : pkgs) {
    384             if (p instanceof ExtraPackage) {
    385                 ExtraPackage ep = (ExtraPackage) p;
    386                 // combine path and old-paths in the form "path [old_path1, old_path2]"
    387                 extraPaths.add(ep.getPath() + " " + Arrays.toString(ep.getOldPaths()));
    388                 extraVendors.add(ep.getVendor());
    389                 extraInstall.add(ep.getInstallFolder(osSdkPath, sdkManager));
    390 
    391                 ArrayList<String> filePaths = new ArrayList<String>();
    392                 for (String filePath : ep.getProjectFiles()) {
    393                     filePaths.add(filePath);
    394                 }
    395                 extraFilePaths.add(filePaths);
    396             }
    397         }
    398         assertEquals(
    399                 "[extra_api_dep [path1, old_path2, oldPath3], " +
    400                  "usb_driver [], " +
    401                  "extra0000005f []]",
    402                 Arrays.toString(extraPaths.toArray()));
    403         assertEquals(
    404                 "[android_vendor, " +
    405                  "g, " +
    406                  "vendor0000005f]",
    407                 Arrays.toString(extraVendors.toArray()));
    408         assertEquals(
    409                 ("[SDK/extras/android_vendor/extra_api_dep, " +
    410                   "SDK/extras/g/usb_driver, " +
    411                   "SDK/extras/vendor0000005f/extra0000005f]").replace('/', File.separatorChar),
    412                 Arrays.toString(extraInstall.toArray()));
    413         assertEquals(
    414                 "[[v8/veggies_8.jar, root.jar, dir1/dir 2 with space/mylib.jar], " +
    415                  "[], " +
    416                  "[]]",
    417                 Arrays.toString(extraFilePaths.toArray()));
    418     }
    419 
    420     /**
    421      * Returns an SdkLib file resource as a {@link ByteArrayInputStream},
    422      * which has the advantage that we can use {@link InputStream#reset()} on it
    423      * at any time to read it multiple times.
    424      * <p/>
    425      * The default for getResourceAsStream() is to return a {@link FileInputStream} that
    426      * does not support reset(), yet we need it in the tested code.
    427      *
    428      * @throws IOException if some I/O read fails
    429      */
    430     private ByteArrayInputStream getTestResource(String filename) throws IOException {
    431         InputStream xmlStream = this.getClass().getResourceAsStream(filename);
    432 
    433         try {
    434             byte[] data = new byte[8192];
    435             int offset = 0;
    436             int n;
    437 
    438             while ((n = xmlStream.read(data, offset, data.length - offset)) != -1) {
    439                 offset += n;
    440 
    441                 if (offset == data.length) {
    442                     byte[] newData = new byte[offset + 8192];
    443                     System.arraycopy(data, 0, newData, 0, offset);
    444                     data = newData;
    445                 }
    446             }
    447 
    448             return new ByteArrayInputStream(data, 0, offset);
    449         } finally {
    450             if (xmlStream != null) {
    451                 xmlStream.close();
    452             }
    453         }
    454     }
    455 }
    456