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 #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