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