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