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