1 /*
2  * Copyright (C) 2023 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.server.midi;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 
25 import androidx.test.filters.SmallTest;
26 import androidx.test.runner.AndroidJUnit4;
27 
28 import com.android.internal.midi.MidiEventMultiScheduler;
29 import com.android.internal.midi.MidiEventScheduler;
30 
31 import org.junit.Test;
32 import org.junit.runner.RunWith;
33 
34 import java.util.Random;
35 
36 /**
37  * Unit tests for com.android.internal.midi.MidiEventMultiScheduler.
38  */
39 @SmallTest
40 @RunWith(AndroidJUnit4.class)
41 public class MidiEventMultiSchedulerTest {
generateRandomByteStream(Random rnd, int size)42     private byte[] generateRandomByteStream(Random rnd, int size) {
43         byte[] output = new byte[size];
44         rnd.nextBytes(output);
45         return output;
46     }
47 
compareByteArrays(byte[] expectedArray, byte[] outputArray)48     private void compareByteArrays(byte[] expectedArray, byte[] outputArray) {
49         assertEquals(expectedArray.length, outputArray.length);
50         for (int i = 0; i < outputArray.length; i++) {
51             assertEquals(expectedArray[i], outputArray[i]);
52         }
53     }
54 
timeFromNow(long milliseconds)55     private long timeFromNow(long milliseconds) {
56         return System.nanoTime() + 1000000L * milliseconds;
57     }
58 
59     @Test
testMultiScheduler()60     public void testMultiScheduler() {
61         try {
62             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
63             assertEquals(3, multiScheduler.getNumEventSchedulers());
64             MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
65             MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
66             MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
67 
68             scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0, (byte) 0xf7},
69                     0, 2, timeFromNow(100)));
70             scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1, (byte) 0xf2},
71                     0, 2, timeFromNow(200)));
72             scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf3, (byte) 0xf4},
73                     0, 2, timeFromNow(300)));
74             scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
75                     0, 2, timeFromNow(400)));
76             assertTrue(multiScheduler.waitNextEvent());
77             assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
78             assertNull(scheduler1.getNextEvent(System.nanoTime()));
79             assertNull(scheduler2.getNextEvent(System.nanoTime()));
80             assertTrue(multiScheduler.waitNextEvent());
81             assertNull(scheduler0.getNextEvent(System.nanoTime()));
82             assertNotNull(scheduler1.getNextEvent(System.nanoTime()));
83             assertNull(scheduler2.getNextEvent(System.nanoTime()));
84             assertTrue(multiScheduler.waitNextEvent());
85             assertNull(scheduler0.getNextEvent(System.nanoTime()));
86             assertNull(scheduler1.getNextEvent(System.nanoTime()));
87             assertNotNull(scheduler2.getNextEvent(System.nanoTime()));
88             assertTrue(multiScheduler.waitNextEvent());
89             assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
90             assertNull(scheduler1.getNextEvent(System.nanoTime()));
91             assertNull(scheduler2.getNextEvent(System.nanoTime()));
92         } catch (InterruptedException ex) {
93 
94         }
95     }
96 
97     @Test
testSchedulerLargeData()98     public void testSchedulerLargeData() {
99         try {
100             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
101             assertEquals(1, multiScheduler.getNumEventSchedulers());
102             MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
103 
104             Random rnd = new Random(42);
105 
106             final int arraySize = 1000;
107             byte[] expectedArray = generateRandomByteStream(rnd, arraySize);
108 
109             scheduler0.add(scheduler0.createScheduledEvent(expectedArray, 0, arraySize,
110                     timeFromNow(100)));
111             assertTrue(multiScheduler.waitNextEvent());
112             MidiEventScheduler.MidiEvent event =
113                     (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
114             assertNotNull(event);
115             compareByteArrays(expectedArray, event.data);
116         } catch (InterruptedException ex) {
117 
118         }
119     }
120 
121     @Test
testSchedulerClose()122     public void testSchedulerClose() {
123         try {
124             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
125             assertEquals(1, multiScheduler.getNumEventSchedulers());
126             MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
127             scheduler0.close();
128             // After all schedulers are closed, waitNextEvent() should return false.
129             assertFalse(multiScheduler.waitNextEvent());
130         } catch (InterruptedException ex) {
131 
132         }
133     }
134 
135     @Test
testSchedulerMultiClose()136     public void testSchedulerMultiClose() {
137         try {
138             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
139             assertEquals(3, multiScheduler.getNumEventSchedulers());
140             multiScheduler.close();
141             // After all schedulers are closed, waitNextEvent() should return false.
142             assertFalse(multiScheduler.waitNextEvent());
143         } catch (InterruptedException ex) {
144 
145         }
146     }
147 
148     @Test
testSchedulerNoPreemptiveClose()149     public void testSchedulerNoPreemptiveClose() {
150         try {
151             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
152             assertEquals(3, multiScheduler.getNumEventSchedulers());
153             MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
154             MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
155             MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
156             scheduler0.close();
157             scheduler1.close();
158             scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
159                     0, 2, timeFromNow(100)));
160             assertTrue(multiScheduler.waitNextEvent());
161             scheduler2.close();
162             // After all schedulers are closed, waitNextEvent() should return false.
163             assertFalse(multiScheduler.waitNextEvent());
164         } catch (InterruptedException ex) {
165 
166         }
167     }
168 
169     @Test
testSchedulerSpamEvents()170     public void testSchedulerSpamEvents() {
171         MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
172         assertEquals(1, multiScheduler.getNumEventSchedulers());
173         MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
174         // Create a msg with size 1
175         byte[] msg = new byte[1];
176         for (int i = 0; i < 1000; i++) {
177             msg[0] = (byte) i;
178             scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
179             MidiEventScheduler.MidiEvent event =
180                     (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
181             assertNotNull(event);
182             assertEquals(1, event.count);
183             assertEquals(msg[0], event.data[0]);
184         }
185         assertNull(scheduler0.getNextEvent(System.nanoTime()));
186     }
187 
188     @Test
testSchedulerSpamEventsPullLater()189     public void testSchedulerSpamEventsPullLater() {
190         MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
191         assertEquals(1, multiScheduler.getNumEventSchedulers());
192         MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
193         // Create a msg with size 1
194         byte[] msg = new byte[1];
195         for (int i = 0; i < 1000; i++) {
196             msg[0] = (byte) i;
197             scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
198         }
199 
200         for (int i = 0; i < 1000; i++) {
201             MidiEventScheduler.MidiEvent event =
202                     (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
203             assertNotNull(event);
204             assertEquals(1, event.count);
205             assertEquals((byte) i, event.data[0]);
206         }
207         assertNull(scheduler0.getNextEvent(System.nanoTime()));
208     }
209 
210     @Test
testSchedulerSpamEventsCallbackLater()211     public void testSchedulerSpamEventsCallbackLater() {
212         MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
213         assertEquals(1, multiScheduler.getNumEventSchedulers());
214         MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
215         // Create a msg with size 1
216         byte[] msg = new byte[1];
217         for (int i = 0; i < 1000; i++) {
218             msg[0] = (byte) i;
219             scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
220         }
221 
222         for (int i = 0; i < 1000; i++) {
223             try {
224                 assertTrue(multiScheduler.waitNextEvent());
225             } catch (InterruptedException ex) {
226             }
227             MidiEventScheduler.MidiEvent event =
228                     (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
229             assertNotNull(event);
230             assertEquals(1, event.count);
231             assertEquals((byte) i, event.data[0]);
232         }
233         assertNull(scheduler0.getNextEvent(System.nanoTime()));
234     }
235 
236     @Test
testMultiSchedulerOutOfOrder()237     public void testMultiSchedulerOutOfOrder() {
238         try {
239             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
240             assertEquals(3, multiScheduler.getNumEventSchedulers());
241             MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
242             MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
243             MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
244 
245             scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
246                     0, 1,
247                     timeFromNow(400)));
248             scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
249                     0, 1,
250                     timeFromNow(300)));
251             scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
252                     0, 1,
253                     timeFromNow(200)));
254             scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
255                     0, 1,
256                     timeFromNow(100)));
257 
258             assertTrue(multiScheduler.waitNextEvent());
259             MidiEventScheduler.MidiEvent event =
260                     (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
261             assertNotNull(event);
262             assertEquals(1, event.count);
263             assertEquals((byte) 0xf0, event.data[0]);
264             assertNull(scheduler1.getNextEvent(System.nanoTime()));
265             assertNull(scheduler2.getNextEvent(System.nanoTime()));
266             assertTrue(multiScheduler.waitNextEvent());
267             assertNull(scheduler0.getNextEvent(System.nanoTime()));
268             event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
269             assertNotNull(event);
270             assertEquals(1, event.count);
271             assertEquals((byte) 0xf1, event.data[0]);
272             assertNull(scheduler2.getNextEvent(System.nanoTime()));
273             assertTrue(multiScheduler.waitNextEvent());
274             assertNull(scheduler0.getNextEvent(System.nanoTime()));
275             assertNull(scheduler1.getNextEvent(System.nanoTime()));
276             event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
277             assertNotNull(event);
278             assertEquals(1, event.count);
279             assertEquals((byte) 0xf2, event.data[0]);
280             assertTrue(multiScheduler.waitNextEvent());
281             event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
282             assertNotNull(event);
283             assertEquals(1, event.count);
284             assertEquals((byte) 0xf3, event.data[0]);
285             assertNull(scheduler1.getNextEvent(System.nanoTime()));
286             assertNull(scheduler2.getNextEvent(System.nanoTime()));
287         } catch (InterruptedException ex) {
288 
289         }
290     }
291 
292     @Test
testMultiSchedulerOutOfOrderNegativeTime()293     public void testMultiSchedulerOutOfOrderNegativeTime() {
294         try {
295             MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
296             assertEquals(3, multiScheduler.getNumEventSchedulers());
297             MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
298             MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
299             MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
300 
301             scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
302                     0, 1,
303                     timeFromNow(-100)));
304             scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
305                     0, 1,
306                     timeFromNow(-200)));
307             scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
308                     0, 1,
309                     timeFromNow(-300)));
310             scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
311                     0, 1,
312                     timeFromNow(-400)));
313 
314             assertTrue(multiScheduler.waitNextEvent());
315             MidiEventScheduler.MidiEvent event =
316                     (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
317             assertNotNull(event);
318             assertEquals(1, event.count);
319             assertEquals((byte) 0xf0, event.data[0]);
320             assertTrue(multiScheduler.waitNextEvent());
321             event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
322             assertNotNull(event);
323             assertEquals(1, event.count);
324             assertEquals((byte) 0xf1, event.data[0]);
325             assertTrue(multiScheduler.waitNextEvent());
326             event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
327             assertNotNull(event);
328             assertEquals(1, event.count);
329             assertEquals((byte) 0xf2, event.data[0]);
330             assertNull(scheduler1.getNextEvent(System.nanoTime()));
331             assertTrue(multiScheduler.waitNextEvent());
332             event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
333             assertNotNull(event);
334             assertEquals(1, event.count);
335             assertEquals((byte) 0xf3, event.data[0]);
336             assertNull(scheduler1.getNextEvent(System.nanoTime()));
337             assertNull(scheduler2.getNextEvent(System.nanoTime()));
338         } catch (InterruptedException ex) {
339 
340         }
341     }
342 }
343