MIDI: Add test for MidiEventMultiScheduler

This CL adds a new folder in frameworks/base for MIDI tests.
This folder can act as a folder for unit tests.

A new test is added for MidiEventMultiScheduler.
com.android.internal.midi is used so this test has to be here.

Bug: 266473128
Test: atest MidiTests --rebuild-module-info
Change-Id: If0d33bd46f8c8adf3a3425888f469f942594aca9
This commit is contained in:
Robert Wu
2023-02-07 23:13:32 +00:00
parent ca9e2463e9
commit 7f0a1a40e2
6 changed files with 450 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
//
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
name: "MidiTests",
srcs: ["**/*.java"],
static_libs: [
"androidx.test.rules",
"mockito-target-inline-minus-junit4",
"platform-test-annotations",
"services.midi",
"truth-prebuilt",
],
jni_libs: ["libdexmakerjvmtiagent"],
certificate: "platform",
platform_apis: true,
test_suites: ["device-tests"],
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.server.midi" >
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.server.midi"
android:label="MidiTests"/>
</manifest>

View File

@@ -0,0 +1,30 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<configuration description="Runs sample instrumentation test.">
<target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="MidiTests.apk"/>
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
<option name="test-suite-tag" value="apct"/>
<option name="test-tag" value="MidiTests"/>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.midi"/>
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
<option name="hidden-api-checks" value="false"/>
</test>
</configuration>

1
tests/MidiTests/OWNERS Normal file
View File

@@ -0,0 +1 @@
include /services/midi/OWNERS

View File

@@ -0,0 +1,7 @@
{
"presubmit": [
{
"name": "MidiTests"
}
]
}

View File

@@ -0,0 +1,342 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.midi;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.midi.MidiEventMultiScheduler;
import com.android.internal.midi.MidiEventScheduler;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Random;
/**
* Unit tests for com.android.internal.midi.MidiEventMultiScheduler.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class MidiEventMultiSchedulerTest {
private byte[] generateRandomByteStream(Random rnd, int size) {
byte[] output = new byte[size];
rnd.nextBytes(output);
return output;
}
private void compareByteArrays(byte[] expectedArray, byte[] outputArray) {
assertEquals(expectedArray.length, outputArray.length);
for (int i = 0; i < outputArray.length; i++) {
assertEquals(expectedArray[i], outputArray[i]);
}
}
private long timeFromNow(long milliseconds) {
return System.nanoTime() + 1000000L * milliseconds;
}
@Test
public void testMultiScheduler() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
assertEquals(3, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0, (byte) 0xf7},
0, 2, timeFromNow(100)));
scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1, (byte) 0xf2},
0, 2, timeFromNow(200)));
scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf3, (byte) 0xf4},
0, 2, timeFromNow(300)));
scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
0, 2, timeFromNow(400)));
assertTrue(multiScheduler.waitNextEvent());
assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertNull(scheduler2.getNextEvent(System.nanoTime()));
assertTrue(multiScheduler.waitNextEvent());
assertNull(scheduler0.getNextEvent(System.nanoTime()));
assertNotNull(scheduler1.getNextEvent(System.nanoTime()));
assertNull(scheduler2.getNextEvent(System.nanoTime()));
assertTrue(multiScheduler.waitNextEvent());
assertNull(scheduler0.getNextEvent(System.nanoTime()));
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertNotNull(scheduler2.getNextEvent(System.nanoTime()));
assertTrue(multiScheduler.waitNextEvent());
assertNotNull(scheduler0.getNextEvent(System.nanoTime()));
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertNull(scheduler2.getNextEvent(System.nanoTime()));
} catch (InterruptedException ex) {
}
}
@Test
public void testSchedulerLargeData() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
assertEquals(1, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
Random rnd = new Random(42);
final int arraySize = 1000;
byte[] expectedArray = generateRandomByteStream(rnd, arraySize);
scheduler0.add(scheduler0.createScheduledEvent(expectedArray, 0, arraySize,
timeFromNow(100)));
assertTrue(multiScheduler.waitNextEvent());
MidiEventScheduler.MidiEvent event =
(MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
compareByteArrays(expectedArray, event.data);
} catch (InterruptedException ex) {
}
}
@Test
public void testSchedulerClose() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
assertEquals(1, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
scheduler0.close();
// After all schedulers are closed, waitNextEvent() should return false.
assertFalse(multiScheduler.waitNextEvent());
} catch (InterruptedException ex) {
}
}
@Test
public void testSchedulerMultiClose() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
assertEquals(3, multiScheduler.getNumEventSchedulers());
multiScheduler.close();
// After all schedulers are closed, waitNextEvent() should return false.
assertFalse(multiScheduler.waitNextEvent());
} catch (InterruptedException ex) {
}
}
@Test
public void testSchedulerNoPreemptiveClose() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
assertEquals(3, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
scheduler0.close();
scheduler1.close();
scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6},
0, 2, timeFromNow(100)));
assertTrue(multiScheduler.waitNextEvent());
scheduler2.close();
// After all schedulers are closed, waitNextEvent() should return false.
assertFalse(multiScheduler.waitNextEvent());
} catch (InterruptedException ex) {
}
}
@Test
public void testSchedulerSpamEvents() {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
assertEquals(1, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
// Create a msg with size 1
byte[] msg = new byte[1];
for (int i = 0; i < 1000; i++) {
msg[0] = (byte) i;
scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
MidiEventScheduler.MidiEvent event =
(MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals(msg[0], event.data[0]);
}
assertNull(scheduler0.getNextEvent(System.nanoTime()));
}
@Test
public void testSchedulerSpamEventsPullLater() {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
assertEquals(1, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
// Create a msg with size 1
byte[] msg = new byte[1];
for (int i = 0; i < 1000; i++) {
msg[0] = (byte) i;
scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
}
for (int i = 0; i < 1000; i++) {
MidiEventScheduler.MidiEvent event =
(MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) i, event.data[0]);
}
assertNull(scheduler0.getNextEvent(System.nanoTime()));
}
@Test
public void testSchedulerSpamEventsCallbackLater() {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1);
assertEquals(1, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
// Create a msg with size 1
byte[] msg = new byte[1];
for (int i = 0; i < 1000; i++) {
msg[0] = (byte) i;
scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0)));
}
for (int i = 0; i < 1000; i++) {
try {
assertTrue(multiScheduler.waitNextEvent());
} catch (InterruptedException ex) {
}
MidiEventScheduler.MidiEvent event =
(MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) i, event.data[0]);
}
assertNull(scheduler0.getNextEvent(System.nanoTime()));
}
@Test
public void testMultiSchedulerOutOfOrder() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
assertEquals(3, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
0, 1,
timeFromNow(400)));
scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
0, 1,
timeFromNow(300)));
scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
0, 1,
timeFromNow(200)));
scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
0, 1,
timeFromNow(100)));
assertTrue(multiScheduler.waitNextEvent());
MidiEventScheduler.MidiEvent event =
(MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf0, event.data[0]);
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertNull(scheduler2.getNextEvent(System.nanoTime()));
assertTrue(multiScheduler.waitNextEvent());
assertNull(scheduler0.getNextEvent(System.nanoTime()));
event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf1, event.data[0]);
assertNull(scheduler2.getNextEvent(System.nanoTime()));
assertTrue(multiScheduler.waitNextEvent());
assertNull(scheduler0.getNextEvent(System.nanoTime()));
assertNull(scheduler1.getNextEvent(System.nanoTime()));
event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf2, event.data[0]);
assertTrue(multiScheduler.waitNextEvent());
event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf3, event.data[0]);
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertNull(scheduler2.getNextEvent(System.nanoTime()));
} catch (InterruptedException ex) {
}
}
@Test
public void testMultiSchedulerOutOfOrderNegativeTime() {
try {
MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3);
assertEquals(3, multiScheduler.getNumEventSchedulers());
MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0);
MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1);
MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2);
scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3},
0, 1,
timeFromNow(-100)));
scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2},
0, 1,
timeFromNow(-200)));
scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1},
0, 1,
timeFromNow(-300)));
scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0},
0, 1,
timeFromNow(-400)));
assertTrue(multiScheduler.waitNextEvent());
MidiEventScheduler.MidiEvent event =
(MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf0, event.data[0]);
assertTrue(multiScheduler.waitNextEvent());
event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf1, event.data[0]);
assertTrue(multiScheduler.waitNextEvent());
event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf2, event.data[0]);
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertTrue(multiScheduler.waitNextEvent());
event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime());
assertNotNull(event);
assertEquals(1, event.count);
assertEquals((byte) 0xf3, event.data[0]);
assertNull(scheduler1.getNextEvent(System.nanoTime()));
assertNull(scheduler2.getNextEvent(System.nanoTime()));
} catch (InterruptedException ex) {
}
}
}