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