/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.core; import junit.framework.Assert; import junit.framework.TestCase; import java.io.File; import java.io.InputStream; import java.io.ObjectInputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.security.KeyStore; import java.security.cert.Certificate; import java.util.Arrays; import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Random; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Logger; import java.util.zip.Deflater; import java.util.zip.Inflater; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.LargeTest; public class MiscRegressionTest extends TestCase { // Regression test for #857840: want JKS key store @SmallTest public void testDefaultKeystore() { String type = KeyStore.getDefaultType(); Assert.assertEquals("Default keystore type must be Bouncy Castle", "BKS", type); try { KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType()); Assert.assertNotNull("Keystore must not be null", store); } catch (Exception ex) { throw new RuntimeException(ex); } try { KeyStore store = KeyStore.getInstance("BKS"); Assert.assertNotNull("Keystore must not be null", store); } catch (Exception ex) { throw new RuntimeException(ex); } } // Regression test for #951285: Suitable LogHandler should be chosen // depending on the environment. @MediumTest public void testAndroidLogHandler() throws Exception { Logger.global.severe("This has logging Level.SEVERE, should become ERROR"); Logger.global.warning("This has logging Level.WARNING, should become WARN"); Logger.global.info("This has logging Level.INFO, should become INFO"); Logger.global.config("This has logging Level.CONFIG, should become DEBUG"); Logger.global.fine("This has logging Level.FINE, should become VERBOSE"); Logger.global.finer("This has logging Level.FINER, should become VERBOSE"); Logger.global.finest("This has logging Level.FINEST, should become VERBOSE"); } // Regression test for Issue 5697: // getContextClassLoader returns a non-application classloader // http://code.google.com/p/android/issues/detail?id=5697 // @MediumTest public void testJavaContextClassLoader() throws Exception { Assert.assertNotNull("Must hava a Java context ClassLoader", Thread.currentThread().getContextClassLoader()); } // Regression test for #1045939: Different output for Method.toString() @SmallTest public void testMethodToString() { try { Method m1 = Object.class.getMethod("notify", new Class[] { }); Method m2 = Object.class.getMethod("toString", new Class[] { }); Method m3 = Object.class.getMethod("wait", new Class[] { long.class, int.class }); Method m4 = Object.class.getMethod("equals", new Class[] { Object.class }); Method m5 = String.class.getMethod("valueOf", new Class[] { char[].class }); Method m6 = Runtime.class.getMethod("exec", new Class[] { String[].class }); assertEquals("Method.toString() must match expectations", "public final native void java.lang.Object.notify()", m1.toString()); assertEquals("Method.toString() must match expectations", "public java.lang.String java.lang.Object.toString()", m2.toString()); assertEquals("Method.toString() must match expectations", "public final native void java.lang.Object.wait(long,int) throws java.lang.InterruptedException", m3.toString()); assertEquals("Method.toString() must match expectations", "public boolean java.lang.Object.equals(java.lang.Object)", m4.toString()); assertEquals("Method.toString() must match expectations", "public static java.lang.String java.lang.String.valueOf(char[])", m5.toString()); assertEquals("Method.toString() must match expectations", "public java.lang.Process java.lang.Runtime.exec(java.lang.String[]) throws java.io.IOException", m6.toString()); } catch (Exception ex) { throw new RuntimeException(ex); } } // Regression test for #1062200: Enum fails to deserialize. Actual problem // was that Class.isEnum() erroneously returned true for indirect // descendants of Enum. enum TrafficLights { RED, YELLOW {}, GREEN { @SuppressWarnings("unused") int i; @SuppressWarnings("unused") void foobar() {} }; } @SmallTest public void testClassIsEnum() { Class<?> trafficClass = TrafficLights.class; Class<?> redClass = TrafficLights.RED.getClass(); Class<?> yellowClass = TrafficLights.YELLOW.getClass(); Class<?> greenClass = TrafficLights.GREEN.getClass(); Assert.assertSame("Classes must be equal", trafficClass, redClass); Assert.assertNotSame("Classes must be different", trafficClass, yellowClass); Assert.assertNotSame("Classes must be different", trafficClass, greenClass); Assert.assertNotSame("Classes must be different", yellowClass, greenClass); Assert.assertTrue("Must be an enum", trafficClass.isEnum()); Assert.assertTrue("Must be an enum", redClass.isEnum()); Assert.assertFalse("Must not be an enum", yellowClass.isEnum()); Assert.assertFalse("Must not be an enum", greenClass.isEnum()); Assert.assertNotNull("Must have enum constants", trafficClass.getEnumConstants()); Assert.assertNull("Must not have enum constants", yellowClass.getEnumConstants()); Assert.assertNull("Must not have enum constants", greenClass.getEnumConstants()); } // Regression test for #1046174: JarEntry.getCertificates() is really slow. public void checkJarCertificates(File file) { try { JarFile jarFile = new JarFile(file); JarEntry je = jarFile.getJarEntry("AndroidManifest.xml"); byte[] readBuffer = new byte[1024]; long t0 = System.currentTimeMillis(); // We must read the stream for the JarEntry to retrieve // its certificates. InputStream is = jarFile.getInputStream(je); while (is.read(readBuffer, 0, readBuffer.length) != -1) { // not using } is.close(); Certificate[] certs = je != null ? je.getCertificates() : null; long t1 = System.currentTimeMillis(); android.util.Log.d("TestHarness", "loadCertificates() took " + (t1 - t0) + " ms"); if (certs == null) { android.util.Log.d("TestHarness", "We have no certificates"); } else { android.util.Log.d("TestHarness", "We have " + certs.length + " certificates"); } } catch (Exception ex) { throw new RuntimeException(ex); } } @LargeTest public void testJarCertificates() { File[] files = new File("/system/app").listFiles(); for (int i = 0; i < files.length; i++) { checkJarCertificates(files[i]); } } // Regression test for #1120750: Reflection for static long fields is broken private static final long MY_LONG = 5073258162644648461L; @SmallTest public void testLongFieldReflection() { try { Field field = getClass().getDeclaredField("MY_LONG"); assertEquals(5073258162644648461L, field.getLong(null)); } catch (Exception ex) { throw new RuntimeException(ex); } } // Regression test for Harmony LinkedHashMap bug. Copied from core, just // to make sure it doesn't get lost. @SmallTest public void testLinkedHashMap() { // we want to test the LinkedHashMap in access ordering mode. LinkedHashMap map = new LinkedHashMap<String, String>(10, 0.75f, true); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); Iterator iterator = map.keySet().iterator(); String id = (String) iterator.next(); map.get(id); try { iterator.next(); // A LinkedHashMap is supposed to throw this Exception when a // iterator.next() Operation takes place after a get // Operation. This is because the get Operation is considered // a structural modification if the LinkedHashMap is in // access order mode. fail("expected ConcurrentModificationException was not thrown."); } catch(ConcurrentModificationException e) { // expected } LinkedHashMap mapClone = (LinkedHashMap) map.clone(); iterator = map.keySet().iterator(); id = (String) iterator.next(); mapClone.get(id); try { iterator.next(); } catch(ConcurrentModificationException e) { fail("expected ConcurrentModificationException was not thrown."); } } // Regression test for #1212257: Boot-time package scan is slow. Not // expected to fail. Please see log if you are interested in the results. @LargeTest public void testZipStressManifest() { android.util.Log.d("MiscRegressionTest", "ZIP stress test started"); long time0 = System.currentTimeMillis(); try { File[] files = new File("/system/app").listFiles(); byte[] buffer = new byte[512]; if (files != null) { for (int i = 0; i < files.length; i++) { android.util.Log.d("MiscRegressionTest", "ZIP stress test processing " + files[i] + "..."); ZipFile zip = new ZipFile(files[i]); ZipEntry entry = zip.getEntry("AndroidManifest.xml"); InputStream stream = zip.getInputStream(entry); int j = stream.read(buffer); while (j != -1) { j = stream.read(buffer); } stream.close(); } } } catch (Exception ex) { throw new RuntimeException(ex); } long time1 = System.currentTimeMillis(); android.util.Log.d("MiscRegressionTest", "ZIP stress test finished, " + "time was " + (time1- time0) + "ms"); } @LargeTest public void testZipStressAllFiles() { android.util.Log.d("MiscRegressionTest", "ZIP stress test started"); long time0 = System.currentTimeMillis(); try { File[] files = new File("/system/app").listFiles(); byte[] buffer = new byte[512]; if (files != null) { for (int i = 0; i < files.length; i++) { android.util.Log.d("MiscRegressionTest", "ZIP stress test processing " + files[i] + "..."); ZipFile zip = new ZipFile(files[i]); Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { InputStream stream = zip.getInputStream(entries.nextElement()); int j = stream.read(buffer); while (j != -1) { j = stream.read(buffer); } stream.close(); } } } } catch (Exception ex) { throw new RuntimeException(ex); } long time1 = System.currentTimeMillis(); android.util.Log.d("MiscRegressionTest", "ZIP stress test finished, " + "time was " + (time1- time0) + "ms"); } @SmallTest public void testOsEncodingProperty() { long time0 = System.currentTimeMillis(); String[] files = new File("/system/app").list(); long time1 = System.currentTimeMillis(); android.util.Log.d("MiscRegressionTest", "File.list() test finished, " + "time was " + (time1- time0) + "ms"); } // ------------------------------------------------------------------------- // Regression test for #1185084: Native memory allocated by // java.util.zip.Deflater in system_server. The fix reduced some internal // ZLIB buffers in size, so this test is trying to execute a lot of // deflating to ensure that things are still working properly. private void assertEquals(byte[] a, byte[] b) { assertEquals("Arrays must have same length", a.length, b.length); for (int i = 0; i < a.length; i++) { assertEquals("Array elements #" + i + " must be equal", a[i], b[i]); } } @LargeTest public void testZipDeflateInflateStress() { final int DATA_SIZE = 16384; Random random = new Random(42); // Seed makes test reproducible try { // Outer loop selects "mode" of test. for (int j = 1; j <=2 ; j++) { byte[] input = new byte[DATA_SIZE]; if (j == 1) { // Totally random content random.nextBytes(input); } else { // Random contents with longer repetitions int pos = 0; while (pos < input.length) { byte what = (byte)random.nextInt(256); int howMany = random.nextInt(32); if (pos + howMany >= input.length) { howMany = input.length - pos; } Arrays.fill(input, pos, pos + howMany, what); pos += howMany; } } // Inner loop tries all 9 compression levels. for (int i = 1; i <= 9; i++) { android.util.Log.d("MiscRegressionTest", "ZipDeflateInflateStress test (" + j + "," + i + ")..."); byte[] zipped = new byte[2 * DATA_SIZE]; // Just to make sure... Deflater deflater = new Deflater(i); deflater.setInput(input); deflater.finish(); deflater.deflate(zipped); byte[] output = new byte[DATA_SIZE]; Inflater inflater = new Inflater(); inflater.setInput(zipped); inflater.finished(); inflater.inflate(output); assertEquals(input, output); } } } catch (Exception ex) { throw new RuntimeException(ex); } } // ------------------------------------------------------------------------- // Regression test for #1252043: Thread.getStackTrace() is broken class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { doSomething(); } public void doSomething() { for (int i = 0; i < 20;) { try { Thread.sleep(100); } catch (InterruptedException ex) { } } } } class MyOtherThread extends Thread { public int visibleTraces; public MyOtherThread(ThreadGroup group, String name) { super(group, name); } @Override public void run() { visibleTraces = Thread.getAllStackTraces().size(); } } @LargeTest public void testThreadGetStackTrace() { MyThread t1 = new MyThread("t1"); t1.start(); try { Thread.sleep(1000); } catch (InterruptedException ex) { } StackTraceElement[] traces = t1.getStackTrace(); StackTraceElement trace = traces[traces.length - 2]; // Expect to find MyThread.doSomething in the trace assertTrue("Must find MyThread.doSomething in trace", trace.getClassName().endsWith("$MyThread") && trace.getMethodName().equals("doSomething")); ThreadGroup g1 = new ThreadGroup("1"); MyOtherThread t2 = new MyOtherThread(g1, "t2"); t2.start(); try { t2.join(); } catch (InterruptedException ex) { } // Expect to see the traces of all threads (not just t2) assertTrue("Must have traces for all threads", t2.visibleTraces > 1); } }