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 17bdbde55592792efe350acd6a46733f439f6a3f3dAurimas Liutikasimport android.support.test.filters.LargeTest; 18d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport android.util.ArrayMap; 19d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler 20d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport junit.framework.TestCase; 21d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport org.junit.Test; 22d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler 23d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerimport java.util.ConcurrentModificationException; 24d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler 25d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler/** 26d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Unit tests for ArrayMap that don't belong in CTS. 27d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler */ 28bdbde55592792efe350acd6a46733f439f6a3f3dAurimas Liutikas@LargeTest 29d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandlerpublic class ArrayMapTest extends TestCase { 30d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler private static final String TAG = "ArrayMapTest"; 31d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler ArrayMap<String, String> map = new ArrayMap<>(); 32d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler 33d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler /** 34d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Attempt to generate a ConcurrentModificationException in ArrayMap. 35d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * <p> 36d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * ArrayMap is explicitly documented to be non-thread-safe, yet it's easy to accidentally screw 37d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * this up; ArrayMap should (in the spirit of the core Java collection types) make an effort to 38d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * catch this and throw ConcurrentModificationException instead of crashing somewhere in its 39d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * internals. 40d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * 41d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * @throws Exception 42d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler */ 43d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler @Test 44d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler public void testConcurrentModificationException() throws Exception { 45d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler final int TEST_LEN_MS = 5000; 46d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.println("Starting ArrayMap concurrency test"); 47d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler new Thread(() -> { 48d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler int i = 0; 49d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler while (map != null) { 50d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler try { 51d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler map.put(String.format("key %d", i++), "B_DONT_DO_THAT"); 52d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ArrayIndexOutOfBoundsException e) { 53d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler Log.e(TAG, "concurrent modification uncaught, causing indexing failure", e); 54d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler fail(); 55d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ClassCastException e) { 56d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e); 57d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler fail(); 58d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ConcurrentModificationException e) { 59d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.println("[successfully caught CME at put #" + i 60d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler + " size=" + (map == null ? "??" : String.valueOf(map.size())) + "]"); 61d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 62d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler if (i % 200 == 0) { 63d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.print("."); 64d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 65d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 66d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler }).start(); 67d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler for (int i = 0; i < (TEST_LEN_MS / 100); i++) { 68d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler try { 69d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler Thread.sleep(100); 70d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler map.clear(); 71d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.print("X"); 72d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (InterruptedException e) { 73d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ArrayIndexOutOfBoundsException e) { 74d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler Log.e(TAG, "concurrent modification uncaught, causing indexing failure"); 75d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler fail(); 76d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ClassCastException e) { 77d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler Log.e(TAG, "concurrent modification uncaught, causing cache corruption"); 78d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler fail(); 79d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ConcurrentModificationException e) { 80d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.println( 81d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler "[successfully caught CME at clear #" 82d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler + i + " size=" + map.size() + "]"); 83d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 84d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 85d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler map = null; // will stop other thread 86d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.println(); 87d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 88d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler 89d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler /** 90d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler * Check to make sure the same operations behave as expected in a single thread. 91d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler */ 92d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler @Test 93d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler public void testNonConcurrentAccesses() throws Exception { 94d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler for (int i = 0; i < 100000; i++) { 95d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler try { 96d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler map.put(String.format("key %d", i++), "B_DONT_DO_THAT"); 97d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler if (i % 200 == 0) { 98d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.print("."); 99d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 100d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler if (i % 500 == 0) { 101d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler map.clear(); 102d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler System.out.print("X"); 103d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 104d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } catch (ConcurrentModificationException e) { 105d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler Log.e(TAG, "concurrent modification caught on single thread", e); 106d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler fail(); 107d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 108d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 109d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler } 110d0ecb1ed10725a6b2c84d64e212984cd4c0d26d2Dan Sandler} 111