1 /* 2 * Copyright (C) 2008 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 android.core; 18 19 import junit.framework.Assert; 20 import junit.framework.TestCase; 21 22 import java.io.File; 23 import java.io.InputStream; 24 import java.io.ObjectInputStream; 25 import java.lang.reflect.Field; 26 import java.lang.reflect.Method; 27 import java.security.KeyStore; 28 import java.security.cert.Certificate; 29 import java.util.Arrays; 30 import java.util.ConcurrentModificationException; 31 import java.util.Enumeration; 32 import java.util.Iterator; 33 import java.util.LinkedHashMap; 34 import java.util.Random; 35 import java.util.jar.JarEntry; 36 import java.util.jar.JarFile; 37 import java.util.logging.Logger; 38 import java.util.zip.Deflater; 39 import java.util.zip.Inflater; 40 import java.util.zip.ZipEntry; 41 import java.util.zip.ZipFile; 42 import android.test.suitebuilder.annotation.MediumTest; 43 import android.test.suitebuilder.annotation.SmallTest; 44 import android.test.suitebuilder.annotation.LargeTest; 45 46 public class MiscRegressionTest extends TestCase { 47 48 // Regression test for #857840: want JKS key store 49 @SmallTest 50 public void testDefaultKeystore() { 51 String type = KeyStore.getDefaultType(); 52 Assert.assertEquals("Default keystore type must be Bouncy Castle", "BKS", type); 53 54 try { 55 KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType()); 56 Assert.assertNotNull("Keystore must not be null", store); 57 } catch (Exception ex) { 58 throw new RuntimeException(ex); 59 } 60 61 try { 62 KeyStore store = KeyStore.getInstance("BKS"); 63 Assert.assertNotNull("Keystore must not be null", store); 64 } catch (Exception ex) { 65 throw new RuntimeException(ex); 66 } 67 } 68 69 // Regression test for #951285: Suitable LogHandler should be chosen 70 // depending on the environment. 71 @MediumTest 72 public void testAndroidLogHandler() throws Exception { 73 Logger.global.severe("This has logging Level.SEVERE, should become ERROR"); 74 Logger.global.warning("This has logging Level.WARNING, should become WARN"); 75 Logger.global.info("This has logging Level.INFO, should become INFO"); 76 Logger.global.config("This has logging Level.CONFIG, should become DEBUG"); 77 Logger.global.fine("This has logging Level.FINE, should become VERBOSE"); 78 Logger.global.finer("This has logging Level.FINER, should become VERBOSE"); 79 Logger.global.finest("This has logging Level.FINEST, should become VERBOSE"); 80 } 81 82 // Regression test for Issue 5697: 83 // getContextClassLoader returns a non-application classloader 84 // http://code.google.com/p/android/issues/detail?id=5697 85 // 86 @MediumTest 87 public void testJavaContextClassLoader() throws Exception { 88 Assert.assertNotNull("Must hava a Java context ClassLoader", 89 Thread.currentThread().getContextClassLoader()); 90 } 91 92 // Regression test for #1045939: Different output for Method.toString() 93 @SmallTest 94 public void testMethodToString() { 95 try { 96 Method m1 = Object.class.getMethod("notify", new Class[] { }); 97 Method m2 = Object.class.getMethod("toString", new Class[] { }); 98 Method m3 = Object.class.getMethod("wait", new Class[] { long.class, int.class }); 99 Method m4 = Object.class.getMethod("equals", new Class[] { Object.class }); 100 Method m5 = String.class.getMethod("valueOf", new Class[] { char[].class }); 101 Method m6 = Runtime.class.getMethod("exec", new Class[] { String[].class }); 102 103 assertEquals("Method.toString() must match expectations", 104 "public final native void java.lang.Object.notify()", 105 m1.toString()); 106 107 assertEquals("Method.toString() must match expectations", 108 "public java.lang.String java.lang.Object.toString()", 109 m2.toString()); 110 111 assertEquals("Method.toString() must match expectations", 112 "public final native void java.lang.Object.wait(long,int) throws java.lang.InterruptedException", 113 m3.toString()); 114 115 assertEquals("Method.toString() must match expectations", 116 "public boolean java.lang.Object.equals(java.lang.Object)", 117 m4.toString()); 118 119 assertEquals("Method.toString() must match expectations", 120 "public static java.lang.String java.lang.String.valueOf(char[])", 121 m5.toString()); 122 123 assertEquals("Method.toString() must match expectations", 124 "public java.lang.Process java.lang.Runtime.exec(java.lang.String[]) throws java.io.IOException", 125 m6.toString()); 126 127 } catch (Exception ex) { 128 throw new RuntimeException(ex); 129 } 130 131 } 132 133 // Regression test for #1062200: Enum fails to deserialize. Actual problem 134 // was that Class.isEnum() erroneously returned true for indirect 135 // descendants of Enum. 136 enum TrafficLights { 137 RED, 138 YELLOW {}, 139 GREEN { 140 @SuppressWarnings("unused") 141 int i; 142 @SuppressWarnings("unused") 143 void foobar() {} 144 }; 145 } 146 147 @SmallTest 148 public void testClassIsEnum() { 149 Class<?> trafficClass = TrafficLights.class; 150 151 Class<?> redClass = TrafficLights.RED.getClass(); 152 Class<?> yellowClass = TrafficLights.YELLOW.getClass(); 153 Class<?> greenClass = TrafficLights.GREEN.getClass(); 154 155 Assert.assertSame("Classes must be equal", trafficClass, redClass); 156 Assert.assertNotSame("Classes must be different", trafficClass, yellowClass); 157 Assert.assertNotSame("Classes must be different", trafficClass, greenClass); 158 Assert.assertNotSame("Classes must be different", yellowClass, greenClass); 159 160 Assert.assertTrue("Must be an enum", trafficClass.isEnum()); 161 Assert.assertTrue("Must be an enum", redClass.isEnum()); 162 Assert.assertFalse("Must not be an enum", yellowClass.isEnum()); 163 Assert.assertFalse("Must not be an enum", greenClass.isEnum()); 164 165 Assert.assertNotNull("Must have enum constants", trafficClass.getEnumConstants()); 166 Assert.assertNull("Must not have enum constants", yellowClass.getEnumConstants()); 167 Assert.assertNull("Must not have enum constants", greenClass.getEnumConstants()); 168 } 169 170 // Regression test for #1046174: JarEntry.getCertificates() is really slow. 171 public void checkJarCertificates(File file) { 172 try { 173 JarFile jarFile = new JarFile(file); 174 JarEntry je = jarFile.getJarEntry("AndroidManifest.xml"); 175 byte[] readBuffer = new byte[1024]; 176 177 long t0 = System.currentTimeMillis(); 178 179 // We must read the stream for the JarEntry to retrieve 180 // its certificates. 181 InputStream is = jarFile.getInputStream(je); 182 while (is.read(readBuffer, 0, readBuffer.length) != -1) { 183 // not using 184 } 185 is.close(); 186 Certificate[] certs = je != null ? je.getCertificates() : null; 187 188 long t1 = System.currentTimeMillis(); 189 android.util.Log.d("TestHarness", "loadCertificates() took " + (t1 - t0) + " ms"); 190 if (certs == null) { 191 android.util.Log.d("TestHarness", "We have no certificates"); 192 } else { 193 android.util.Log.d("TestHarness", "We have " + certs.length + " certificates"); 194 } 195 } catch (Exception ex) { 196 throw new RuntimeException(ex); 197 } 198 } 199 200 @LargeTest 201 public void testJarCertificates() { 202 File[] files = new File("/system/app").listFiles(); 203 for (int i = 0; i < files.length; i++) { 204 checkJarCertificates(files[i]); 205 } 206 } 207 208 // Regression test for #1120750: Reflection for static long fields is broken 209 private static final long MY_LONG = 5073258162644648461L; 210 211 @SmallTest 212 public void testLongFieldReflection() { 213 try { 214 Field field = getClass().getDeclaredField("MY_LONG"); 215 assertEquals(5073258162644648461L, field.getLong(null)); 216 } catch (Exception ex) { 217 throw new RuntimeException(ex); 218 } 219 } 220 221 // Regression test for Harmony LinkedHashMap bug. Copied from core, just 222 // to make sure it doesn't get lost. 223 @SmallTest 224 public void testLinkedHashMap() { 225 // we want to test the LinkedHashMap in access ordering mode. 226 LinkedHashMap map = new LinkedHashMap<String, String>(10, 0.75f, true); 227 228 map.put("key1", "value1"); 229 map.put("key2", "value2"); 230 map.put("key3", "value3"); 231 232 Iterator iterator = map.keySet().iterator(); 233 String id = (String) iterator.next(); 234 map.get(id); 235 try { 236 iterator.next(); 237 // A LinkedHashMap is supposed to throw this Exception when a 238 // iterator.next() Operation takes place after a get 239 // Operation. This is because the get Operation is considered 240 // a structural modification if the LinkedHashMap is in 241 // access order mode. 242 fail("expected ConcurrentModificationException was not thrown."); 243 } catch(ConcurrentModificationException e) { 244 // expected 245 } 246 247 LinkedHashMap mapClone = (LinkedHashMap) map.clone(); 248 249 iterator = map.keySet().iterator(); 250 id = (String) iterator.next(); 251 mapClone.get(id); 252 try { 253 iterator.next(); 254 } catch(ConcurrentModificationException e) { 255 fail("expected ConcurrentModificationException was not thrown."); 256 } 257 } 258 259 // Regression test for #1212257: Boot-time package scan is slow. Not 260 // expected to fail. Please see log if you are interested in the results. 261 @LargeTest 262 public void testZipStressManifest() { 263 android.util.Log.d("MiscRegressionTest", "ZIP stress test started"); 264 265 long time0 = System.currentTimeMillis(); 266 267 try { 268 File[] files = new File("/system/app").listFiles(); 269 270 byte[] buffer = new byte[512]; 271 272 if (files != null) { 273 for (int i = 0; i < files.length; i++) { 274 android.util.Log.d("MiscRegressionTest", 275 "ZIP stress test processing " + files[i] + "..."); 276 277 ZipFile zip = new ZipFile(files[i]); 278 279 ZipEntry entry = zip.getEntry("AndroidManifest.xml"); 280 InputStream stream = zip.getInputStream(entry); 281 282 int j = stream.read(buffer); 283 while (j != -1) { 284 j = stream.read(buffer); 285 } 286 287 stream.close(); 288 } 289 } 290 } catch (Exception ex) { 291 throw new RuntimeException(ex); 292 } 293 294 long time1 = System.currentTimeMillis(); 295 296 android.util.Log.d("MiscRegressionTest", "ZIP stress test finished, " + 297 "time was " + (time1- time0) + "ms"); 298 } 299 300 @LargeTest 301 public void testZipStressAllFiles() { 302 android.util.Log.d("MiscRegressionTest", "ZIP stress test started"); 303 304 long time0 = System.currentTimeMillis(); 305 306 try { 307 File[] files = new File("/system/app").listFiles(); 308 309 byte[] buffer = new byte[512]; 310 311 if (files != null) { 312 for (int i = 0; i < files.length; i++) { 313 android.util.Log.d("MiscRegressionTest", 314 "ZIP stress test processing " + files[i] + "..."); 315 316 ZipFile zip = new ZipFile(files[i]); 317 318 Enumeration<? extends ZipEntry> entries = zip.entries(); 319 while (entries.hasMoreElements()) { 320 InputStream stream = zip.getInputStream(entries.nextElement()); 321 322 int j = stream.read(buffer); 323 while (j != -1) { 324 j = stream.read(buffer); 325 } 326 327 stream.close(); 328 } 329 } 330 } 331 } catch (Exception ex) { 332 throw new RuntimeException(ex); 333 } 334 335 long time1 = System.currentTimeMillis(); 336 337 android.util.Log.d("MiscRegressionTest", "ZIP stress test finished, " + 338 "time was " + (time1- time0) + "ms"); 339 } 340 341 @SmallTest 342 public void testOsEncodingProperty() { 343 long time0 = System.currentTimeMillis(); 344 String[] files = new File("/system/app").list(); 345 long time1 = System.currentTimeMillis(); 346 android.util.Log.d("MiscRegressionTest", "File.list() test finished, " + 347 "time was " + (time1- time0) + "ms"); 348 } 349 350 // ------------------------------------------------------------------------- 351 // Regression test for #1185084: Native memory allocated by 352 // java.util.zip.Deflater in system_server. The fix reduced some internal 353 // ZLIB buffers in size, so this test is trying to execute a lot of 354 // deflating to ensure that things are still working properly. 355 private void assertEquals(byte[] a, byte[] b) { 356 assertEquals("Arrays must have same length", a.length, b.length); 357 358 for (int i = 0; i < a.length; i++) { 359 assertEquals("Array elements #" + i + " must be equal", a[i], b[i]); 360 } 361 } 362 363 @LargeTest 364 public void testZipDeflateInflateStress() { 365 366 final int DATA_SIZE = 16384; 367 368 Random random = new Random(42); // Seed makes test reproducible 369 370 try { 371 // Outer loop selects "mode" of test. 372 for (int j = 1; j <=2 ; j++) { 373 374 byte[] input = new byte[DATA_SIZE]; 375 376 if (j == 1) { 377 // Totally random content 378 random.nextBytes(input); 379 } else { 380 // Random contents with longer repetitions 381 int pos = 0; 382 while (pos < input.length) { 383 byte what = (byte)random.nextInt(256); 384 int howMany = random.nextInt(32); 385 if (pos + howMany >= input.length) { 386 howMany = input.length - pos; 387 } 388 Arrays.fill(input, pos, pos + howMany, what); 389 pos += howMany; 390 } 391 } 392 393 // Inner loop tries all 9 compression levels. 394 for (int i = 1; i <= 9; i++) { 395 android.util.Log.d("MiscRegressionTest", "ZipDeflateInflateStress test (" + j + "," + i + ")..."); 396 397 byte[] zipped = new byte[2 * DATA_SIZE]; // Just to make sure... 398 399 Deflater deflater = new Deflater(i); 400 deflater.setInput(input); 401 deflater.finish(); 402 403 deflater.deflate(zipped); 404 405 byte[] output = new byte[DATA_SIZE]; 406 407 Inflater inflater = new Inflater(); 408 inflater.setInput(zipped); 409 inflater.finished(); 410 411 inflater.inflate(output); 412 413 assertEquals(input, output); 414 } 415 } 416 } catch (Exception ex) { 417 throw new RuntimeException(ex); 418 } 419 } 420 421 // ------------------------------------------------------------------------- 422 // Regression test for #1252043: Thread.getStackTrace() is broken 423 class MyThread extends Thread { 424 public MyThread(String name) { 425 super(name); 426 } 427 428 @Override 429 public void run() { 430 doSomething(); 431 } 432 433 public void doSomething() { 434 for (int i = 0; i < 20;) { 435 try { 436 Thread.sleep(100); 437 } catch (InterruptedException ex) { 438 } 439 } 440 } 441 } 442 443 class MyOtherThread extends Thread { 444 public int visibleTraces; 445 446 public MyOtherThread(ThreadGroup group, String name) { 447 super(group, name); 448 } 449 450 @Override 451 public void run() { 452 visibleTraces = Thread.getAllStackTraces().size(); 453 } 454 } 455 456 @LargeTest 457 public void testThreadGetStackTrace() { 458 MyThread t1 = new MyThread("t1"); 459 t1.start(); 460 461 try { 462 Thread.sleep(1000); 463 } catch (InterruptedException ex) { 464 } 465 466 StackTraceElement[] traces = t1.getStackTrace(); 467 StackTraceElement trace = traces[traces.length - 2]; 468 469 // Expect to find MyThread.doSomething in the trace 470 assertTrue("Must find MyThread.doSomething in trace", 471 trace.getClassName().endsWith("$MyThread") && 472 trace.getMethodName().equals("doSomething")); 473 474 ThreadGroup g1 = new ThreadGroup("1"); 475 MyOtherThread t2 = new MyOtherThread(g1, "t2"); 476 t2.start(); 477 try { 478 t2.join(); 479 } catch (InterruptedException ex) { 480 } 481 482 // Expect to see the traces of all threads (not just t2) 483 assertTrue("Must have traces for all threads", t2.visibleTraces > 1); 484 } 485 } 486