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