ArrayMapTest.java revision d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2
1d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler/*
2d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Copyright (C) 2017 The Android Open Source Project
3d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler *
4d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * except in compliance with the License. You may obtain a copy of the License at
6d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler *
7d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler *      http://www.apache.org/licenses/LICENSE-2.0
8d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler *
9d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Unless required by applicable law or agreed to in writing, software distributed under the
10d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * KIND, either express or implied. See the License for the specific language governing
12d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * permissions and limitations under the License.
13d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler */
14d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
15d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerpackage android.util;
16d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
17d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport android.util.ArrayMap;
18d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
19d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport junit.framework.TestCase;
20d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport org.junit.Test;
21d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
22d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport java.util.ConcurrentModificationException;
23d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
24d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler/**
25d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Unit tests for ArrayMap that don't belong in CTS.
26d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler */
27d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerpublic class ArrayMapTest extends TestCase {
28d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    private static final String TAG = "ArrayMapTest";
29d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    ArrayMap<String, String> map = new ArrayMap<>();
30d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
31d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    /**
32d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * Attempt to generate a ConcurrentModificationException in ArrayMap.
33d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * <p>
34d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * ArrayMap is explicitly documented to be non-thread-safe, yet it's easy to accidentally screw
35d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * this up; ArrayMap should (in the spirit of the core Java collection types) make an effort to
36d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * catch this and throw ConcurrentModificationException instead of crashing somewhere in its
37d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * internals.
38d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     *
39d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * @throws Exception
40d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     */
41d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    @Test
42d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    public void testConcurrentModificationException() throws Exception {
43d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        final int TEST_LEN_MS = 5000;
44d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        System.out.println("Starting ArrayMap concurrency test");
45d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        new Thread(() -> {
46d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            int i = 0;
47d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            while (map != null) {
48d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                try {
49d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    map.put(String.format("key %d", i++), "B_DONT_DO_THAT");
50d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                } catch (ArrayIndexOutOfBoundsException e) {
51d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    Log.e(TAG, "concurrent modification uncaught, causing indexing failure", e);
52d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    fail();
53d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                } catch (ClassCastException e) {
54d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
55d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    fail();
56d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                } catch (ConcurrentModificationException e) {
57d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    System.out.println("[successfully caught CME at put #" + i
58d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                            + " size=" + (map == null ? "??" : String.valueOf(map.size())) + "]");
59d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                }
60d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                if (i % 200 == 0) {
61d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    System.out.print(".");
62d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                }
63d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            }
64d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        }).start();
65d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        for (int i = 0; i < (TEST_LEN_MS / 100); i++) {
66d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            try {
67d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                Thread.sleep(100);
68d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                map.clear();
69d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                System.out.print("X");
70d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            } catch (InterruptedException e) {
71d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            } catch (ArrayIndexOutOfBoundsException e) {
72d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                Log.e(TAG, "concurrent modification uncaught, causing indexing failure");
73d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                fail();
74d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            } catch (ClassCastException e) {
75d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                Log.e(TAG, "concurrent modification uncaught, causing cache corruption");
76d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                fail();
77d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            } catch (ConcurrentModificationException e) {
78d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                System.out.println(
79d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                        "[successfully caught CME at clear #"
80d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                                + i + " size=" + map.size() + "]");
81d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            }
82d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        }
83d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        map = null; // will stop other thread
84d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        System.out.println();
85d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    }
86d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler
87d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    /**
88d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     * Check to make sure the same operations behave as expected in a single thread.
89d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler     */
90d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    @Test
91d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    public void testNonConcurrentAccesses() throws Exception {
92d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        for (int i = 0; i < 100000; i++) {
93d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            try {
94d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                map.put(String.format("key %d", i++), "B_DONT_DO_THAT");
95d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                if (i % 200 == 0) {
96d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    System.out.print(".");
97d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                }
98d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                if (i % 500 == 0) {
99d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    map.clear();
100d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                    System.out.print("X");
101d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                }
102d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            } catch (ConcurrentModificationException e) {
103d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                Log.e(TAG, "concurrent modification caught on single thread", e);
104d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler                fail();
105d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler            }
106d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler        }
107d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler    }
108d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler}
109