1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.util;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 
22 import androidx.test.filters.LargeTest;
23 import androidx.test.runner.AndroidJUnit4;
24 
25 import org.junit.After;
26 import org.junit.Before;
27 import org.junit.Ignore;
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 
31 @LargeTest
32 @RunWith(AndroidJUnit4.class)
33 public class RaceConditionReproducerTest {
34     private final static String SOME_VALID_SEQUENCE_3_3 = "B1|A1|A2|B2|A3|B3";
35 
factorial(int n)36     private static int factorial(int n) {
37         int res = 1;
38         for (int i = 2; i <= n; ++i) res *= i;
39         return res;
40     }
41 
42     RaceConditionReproducer eventProcessor;
43 
44     @Before
setup()45     public void setup() {
46         eventProcessor = new RaceConditionReproducer();
47     }
48 
49     @After
tearDown()50     public void tearDown() {
51         TraceHelperForTest.cleanup();
52     }
53 
run3_3_TestAction()54     private void run3_3_TestAction() throws InterruptedException {
55         Thread tb = new Thread(() -> {
56             eventProcessor.onEvent("B1");
57             eventProcessor.onEvent("B2");
58             eventProcessor.onEvent("B3");
59         });
60         tb.start();
61 
62         eventProcessor.onEvent("A1");
63         eventProcessor.onEvent("A2");
64         eventProcessor.onEvent("A3");
65 
66         tb.join();
67     }
68 
69     @Test
70     @Ignore // The test is too long for continuous testing.
71     // 2 threads, 3 events each.
test3_3()72     public void test3_3() throws Exception {
73         boolean sawTheValidSequence = false;
74 
75         for (; ; ) {
76             eventProcessor.startIteration();
77             run3_3_TestAction();
78             final boolean needMoreIterations = eventProcessor.finishIteration();
79 
80             sawTheValidSequence = sawTheValidSequence ||
81                     SOME_VALID_SEQUENCE_3_3.equals(eventProcessor.getCurrentSequenceString());
82 
83             if (!needMoreIterations) break;
84         }
85 
86         assertEquals("Wrong number of leaf nodes",
87                 factorial(3 + 3) / (factorial(3) * factorial(3)),
88                 eventProcessor.numberOfLeafNodes());
89         assertTrue(sawTheValidSequence);
90     }
91 
92     @Test
93     @Ignore // The test is too long for continuous testing.
94     // 2 threads, 3 events, including enter-exit pairs each.
test3_3_enter_exit()95     public void test3_3_enter_exit() throws Exception {
96         boolean sawTheValidSequence = false;
97 
98         for (; ; ) {
99             eventProcessor.startIteration();
100             Thread tb = new Thread(() -> {
101                 eventProcessor.onEvent("B1:enter");
102                 eventProcessor.onEvent("B1:exit");
103                 eventProcessor.onEvent("B2");
104                 eventProcessor.onEvent("B3:enter");
105                 eventProcessor.onEvent("B3:exit");
106             });
107             tb.start();
108 
109             eventProcessor.onEvent("A1");
110             eventProcessor.onEvent("A2:enter");
111             eventProcessor.onEvent("A2:exit");
112             eventProcessor.onEvent("A3:enter");
113             eventProcessor.onEvent("A3:exit");
114 
115             tb.join();
116             final boolean needMoreIterations = eventProcessor.finishIteration();
117 
118             sawTheValidSequence = sawTheValidSequence ||
119                     "B1:enter|B1:exit|A1|A2:enter|A2:exit|B2|A3:enter|A3:exit|B3:enter|B3:exit".
120                             equals(eventProcessor.getCurrentSequenceString());
121 
122             if (!needMoreIterations) break;
123         }
124 
125         assertEquals("Wrong number of leaf nodes",
126                 factorial(3 + 3) / (factorial(3) * factorial(3)),
127                 eventProcessor.numberOfLeafNodes());
128         assertTrue(sawTheValidSequence);
129     }
130 
131     @Test
132     // 2 threads, 3 events each; reproducing a particular event sequence.
test3_3_ReproMode()133     public void test3_3_ReproMode() throws Exception {
134         eventProcessor = new RaceConditionReproducer(SOME_VALID_SEQUENCE_3_3);
135         eventProcessor.startIteration();
136         run3_3_TestAction();
137         assertTrue(!eventProcessor.finishIteration());
138         assertEquals(SOME_VALID_SEQUENCE_3_3, eventProcessor.getCurrentSequenceString());
139 
140         assertEquals("Wrong number of leaf nodes", 1, eventProcessor.numberOfLeafNodes());
141     }
142 
143     @Test
144     @Ignore // The test is too long for continuous testing.
145     // 2 threads with 2 events; 1 thread with 1 event.
test2_1_2()146     public void test2_1_2() throws Exception {
147         for (; ; ) {
148             eventProcessor.startIteration();
149             Thread tb = new Thread(() -> {
150                 eventProcessor.onEvent("B1");
151                 eventProcessor.onEvent("B2");
152             });
153             tb.start();
154 
155             Thread tc = new Thread(() -> {
156                 eventProcessor.onEvent("C1");
157             });
158             tc.start();
159 
160             eventProcessor.onEvent("A1");
161             eventProcessor.onEvent("A2");
162 
163             tb.join();
164             tc.join();
165 
166             if (!eventProcessor.finishIteration()) break;
167         }
168 
169         assertEquals("Wrong number of leaf nodes",
170                 factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
171                 eventProcessor.numberOfLeafNodes());
172     }
173 
174     @Test
175     @Ignore // The test is too long for continuous testing.
176     // 2 threads with 2 events; 1 thread with 1 event. Includes enter-exit pairs.
test2_1_2_enter_exit()177     public void test2_1_2_enter_exit() throws Exception {
178         for (; ; ) {
179             eventProcessor.startIteration();
180             Thread tb = new Thread(() -> {
181                 eventProcessor.onEvent("B1:enter");
182                 eventProcessor.onEvent("B1:exit");
183                 eventProcessor.onEvent("B2:enter");
184                 eventProcessor.onEvent("B2:exit");
185             });
186             tb.start();
187 
188             Thread tc = new Thread(() -> {
189                 eventProcessor.onEvent("C1:enter");
190                 eventProcessor.onEvent("C1:exit");
191             });
192             tc.start();
193 
194             eventProcessor.onEvent("A1:enter");
195             eventProcessor.onEvent("A1:exit");
196             eventProcessor.onEvent("A2:enter");
197             eventProcessor.onEvent("A2:exit");
198 
199             tb.join();
200             tc.join();
201 
202             if (!eventProcessor.finishIteration()) break;
203         }
204 
205         assertEquals("Wrong number of leaf nodes",
206                 factorial(2 + 2 + 1) / (factorial(2) * factorial(2) * factorial(1)),
207                 eventProcessor.numberOfLeafNodes());
208     }
209 }
210