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> </b> " + 114 "<td> </td></tr> " + 115 "<tr><td bgcolor=\"#3366cc\"><font face=arial,sans-serif color=\"#ffffff\"><b>Error</b></td></tr> " + 116 "<tr><td> </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