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.support.v4.util;
16
17import static org.junit.Assert.fail;
18
19import android.support.test.filters.LargeTest;
20import android.support.test.runner.AndroidJUnit4;
21import android.util.Log;
22
23import org.junit.Test;
24import org.junit.runner.RunWith;
25
26import java.util.ConcurrentModificationException;
27
28/**
29 * Unit tests for SimpleArrayMap
30 */
31@RunWith(AndroidJUnit4.class)
32@LargeTest
33public class SimpleArrayMapTest {
34    private static final String TAG = "SimpleArrayMapTest";
35    SimpleArrayMap<String, String> map = new SimpleArrayMap<>();
36    private boolean mDone;
37
38    /**
39     * Attempt to generate a ConcurrentModificationException in ArrayMap.
40     */
41    @Test
42    public void testConcurrentModificationException() throws Exception {
43        final int TEST_LEN_MS = 5000;
44        Log.d(TAG, "Starting SimpleArrayMap concurrency test");
45        mDone = false;
46        new Thread(new Runnable() {
47            @Override
48            public void run() {
49                int i = 0;
50                while (!mDone) {
51                    try {
52                        map.put(String.format("key %d", i++), "B_DONT_DO_THAT");
53                    } catch (ArrayIndexOutOfBoundsException e) {
54                        // SimpleArrayMap is not thread safe, so lots of concurrent modifications
55                        // can still cause data corruption
56                        Log.w(TAG, "concurrent modification uncaught, causing indexing failure", e);
57                    } catch (ClassCastException e) {
58                        // cache corruption should not occur as it is hard to trace and one thread
59                        // may corrupt the pool for all threads in the same process.
60                        Log.e(TAG, "concurrent modification uncaught, causing cache corruption", e);
61                        fail();
62                    } catch (ConcurrentModificationException e) {
63                    }
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            } catch (InterruptedException e) {
72            } catch (ArrayIndexOutOfBoundsException e) {
73                Log.w(TAG, "concurrent modification uncaught, causing indexing failure");
74            } catch (ClassCastException e) {
75                Log.e(TAG, "concurrent modification uncaught, causing cache corruption");
76                fail();
77            } catch (ConcurrentModificationException e) {
78            }
79        }
80        mDone = true;
81    }
82
83    /**
84     * Check to make sure the same operations behave as expected in a single thread.
85     */
86    @Test
87    public void testNonConcurrentAccesses() throws Exception {
88        for (int i = 0; i < 100000; i++) {
89            try {
90                map.put(String.format("key %d", i++), "B_DONT_DO_THAT");
91                if (i % 500 == 0) {
92                    map.clear();
93                }
94            } catch (ConcurrentModificationException e) {
95                Log.e(TAG, "concurrent modification caught on single thread", e);
96                fail();
97            }
98        }
99    }
100}
101