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