Merge "USB MIDI: UsbMidiPacketConverterTests" into udc-dev

This commit is contained in:
TreeHugger Robot
2023-02-28 23:26:47 +00:00
committed by Android (Google) Code Review

View File

@@ -0,0 +1,407 @@
/*
* 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.usb;
import static org.junit.Assert.assertEquals;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Random;
/**
* Unit tests for com.android.server.usb.UsbMidiPacketConverter.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class UsbMidiPacketConverterTest {
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]);
}
}
@Test
public void testDecoderSinglePacket() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createDecoders(2);
byte[] input = new byte[] {0x19 /* Cable 1 Note-On */, (byte) 0x91, 0x33, 0x66};
byte[] expectedOutputCable0 = new byte[] {};
byte[] expectedOutputCable1 = new byte[] {(byte) 0x91, 0x33, 0x66};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
compareByteArrays(expectedOutputCable1, actualOutputCable1);
}
@Test
public void testDecoderMultiplePackets() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createDecoders(4);
byte[] input = new byte[] {
0x1B /* Cable 1 Control Change */, (byte) 0xB4, 0x55, 0x6E,
0x35 /* Cable 3 Single byte SysEx */, (byte) 0xF8, 0x00, 0x00,
0x02 /* Cable 0 Two byte System Common */, (byte) 0xF3, 0x12, 0x00};
byte[] expectedOutputCable0 = new byte[] {(byte) 0xF3, 0x12};
byte[] expectedOutputCable1 = new byte[] {(byte) 0xB4, 0x55, 0x6E};
byte[] expectedOutputCable2 = new byte[] {};
byte[] expectedOutputCable3 = new byte[] {(byte) 0xF8};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
byte[] actualOutputCable2 = usbMidiPacketConverter.pullDecodedMidiPackets(2);
byte[] actualOutputCable3 = usbMidiPacketConverter.pullDecodedMidiPackets(3);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
compareByteArrays(expectedOutputCable1, actualOutputCable1);
compareByteArrays(expectedOutputCable2, actualOutputCable2);
compareByteArrays(expectedOutputCable3, actualOutputCable3);
}
@Test
public void testDecoderSysExEndFirstByte() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createDecoders(2);
byte[] input = new byte[] {
0x14 /* Cable 1 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
0x15 /* Cable 1 Single byte SysEx End */, (byte) 0xF7, 0x00, 0x00};
byte[] expectedOutputCable0 = new byte[] {};
byte[] expectedOutputCable1 = new byte[] {
(byte) 0xF0, 0x00, 0x01,
(byte) 0xF7};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
compareByteArrays(expectedOutputCable1, actualOutputCable1);
}
@Test
public void testDecoderSysExEndSecondByte() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createDecoders(1);
byte[] input = new byte[] {
0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
0x06 /* Cable 0 Two byte SysEx End */, 0x02, (byte) 0xF7, 0x00};
byte[] expectedOutputCable0 = new byte[] {
(byte) 0xF0, 0x00, 0x01,
0x02, (byte) 0xF7};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
}
@Test
public void testDecoderSysExEndThirdByte() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
byte[] input = new byte[] {
0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01,
0x07 /* Cable 0 Three byte SysEx End */, 0x02, 0x03, (byte) 0xF7};
usbMidiPacketConverter.createDecoders(1);
byte[] expectedOutputCable0 = new byte[] {
(byte) 0xF0, 0x00, 0x01,
0x02, 0x03, (byte) 0xF7};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
}
@Test
public void testDecoderSysExStartEnd() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
byte[] input = new byte[] {
0x06 /* Cable 0 Two byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
usbMidiPacketConverter.createDecoders(1);
byte[] expectedOutputCable0 = new byte[] {
(byte) 0xF0, (byte) 0xF7};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
}
@Test
public void testDecoderSysExStartByteEnd() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
byte[] input = new byte[] {
0x07 /* Cable 0 Three byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7};
usbMidiPacketConverter.createDecoders(1);
byte[] expectedOutputCable0 = new byte[] {
(byte) 0xF0, 0x44, (byte) 0xF7};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
}
@Test
public void testDecoderDefaultToFirstCable() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
byte[] input = new byte[] {0x49 /* Cable 4 Note-On */, (byte) 0x91, 0x22, 0x33};
usbMidiPacketConverter.createDecoders(1);
byte[] expectedOutputCable0 = new byte[] {
(byte) 0x91, 0x22, 0x33};
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0);
compareByteArrays(expectedOutputCable0, actualOutputCable0);
}
@Test
public void testDecoderLargePacketDoesNotCrash() {
for (long seed = 1001; seed < 5000; seed += 777) {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createDecoders(3);
Random rnd = new Random(seed);
byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */);
usbMidiPacketConverter.decodeMidiPackets(input, input.length);
usbMidiPacketConverter.pullDecodedMidiPackets(0);
usbMidiPacketConverter.pullDecodedMidiPackets(1);
usbMidiPacketConverter.pullDecodedMidiPackets(2);
}
}
@Test
public void testEncoderBasic() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x33, 0x66};
byte[] expectedOutput = new byte[] {
0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x33, 0x66};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderMultiplePackets() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(3);
byte[] inputCable2 = new byte[] {
(byte) 0xB4 /* Control Change */, 0x55, 0x6E};
byte[] inputCable1 = new byte[] {
(byte) 0xF8 /* Timing Clock (Single Byte) */,
(byte) 0xF3 /* Song Select (Two Bytes) */, 0x12};
byte[] expectedOutput = new byte[] {
0x2B /* Cable 2 Control Change */, (byte) 0xB4, 0x55, 0x6E,
0x15 /* Cable 1 Timing Clock */, (byte) 0xF8, 0x00, 0x00,
0x12 /* Cable 1 Two Byte System Common */, (byte) 0xF3, 0x12, 0x00};
usbMidiPacketConverter.encodeMidiPackets(inputCable2, inputCable2.length, 2);
usbMidiPacketConverter.encodeMidiPackets(inputCable1, inputCable1.length, 1);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderWeavePackets() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(2);
byte[] inputCable1Msg1 = new byte[] {
(byte) 0x93 /* Note-On */, 0x23, 0x43};
byte[] inputCable0Msg = new byte[] {
(byte) 0xB4 /* Control Change */, 0x65, 0x26};
byte[] inputCable1Msg2 = new byte[] {
(byte) 0xA4 /* Poly-KeyPress */, 0x52, 0x76};
byte[] expectedOutput = new byte[] {
0x19 /* Cable 1 Note-On */, (byte) 0x93, 0x23, 0x43,
0x0B /* Cable 0 Control Change */, (byte) 0xB4, 0x65, 0x26,
0x1A /* Cable 1 Poly-KeyPress */, (byte) 0xA4, 0x52, 0x76};
usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg1, inputCable1Msg1.length, 1);
usbMidiPacketConverter.encodeMidiPackets(inputCable0Msg, inputCable0Msg.length, 0);
usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg2, inputCable1Msg2.length, 1);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderSysExEndFirstByte() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {
(byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
(byte) 0xF7 /* SysEx End */};
byte[] expectedOutput = new byte[] {
0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
0x05 /* Cable 0 One Byte SysEx End */, (byte) 0xF7, 0x00, 0x00};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderSysExEndSecondByte() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {
(byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
0x02, (byte) 0xF7 /* SysEx End */};
byte[] expectedOutput = new byte[] {
0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
0x06 /* Cable 0 Two Byte SysEx End */, 0x02, (byte) 0xF7, 0x00};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderSysExEndThirdByte() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {
(byte) 0xF0 /* SysEx Start */, 0x00, 0x01,
0x02, 0x03, (byte) 0xF7 /* SysEx End */};
byte[] expectedOutput = new byte[] {
0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01,
0x07 /* Cable 0 Three Byte SysEx End */, 0x02, 0x03, (byte) 0xF7};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderSysExStartEnd() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {
(byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */};
byte[] expectedOutput = new byte[] {
0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderSysExStartByteEnd() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {
(byte) 0xF0 /* SysEx Start */, 0x44, (byte) 0xF7 /* SysEx End */};
byte[] expectedOutput = new byte[] {
0x07 /* Cable 0 Three Byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderMultiplePulls() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(1);
byte[] input = new byte[] {
(byte) 0xF0 /* SysEx Start */, 0x44, 0x55,
0x66, 0x77}; // 0x66 and 0x77 will not be pulled the first time
byte[] expectedOutput = new byte[] {
0x04 /* SysEx Start */, (byte) 0xF0, 0x44, 0x55};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
input = new byte[] {
0x11, // Combined with 0x66 and 0x77 above
0x22, (byte) 0xF7 /* SysEx End */};
expectedOutput = new byte[] {
0x04 /* Cable 0 SysEx Continue */, 0x66, 0x77, 0x11,
0x06 /* Cable 0 Two Byte SysEx End */, 0x22, (byte) 0xF7, 0x00};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
input = new byte[] {
(byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */};
expectedOutput = new byte[] {
0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0);
output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderDefaultToFirstCable() {
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(2);
byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x22, 0x33};
byte[] expectedOutput = new byte[] {
0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x22, 0x33};
usbMidiPacketConverter.encodeMidiPackets(input, input.length, 4);
byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets();
compareByteArrays(expectedOutput, output);
}
@Test
public void testEncoderLargePacketDoesNotCrash() {
for (long seed = 234; seed < 4000; seed += 666) {
Random rnd = new Random(seed);
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(4);
for (int cableNumber = 0; cableNumber < 4; cableNumber++) {
byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */);
usbMidiPacketConverter.encodeMidiPackets(input, input.length, cableNumber);
}
usbMidiPacketConverter.pullEncodedMidiPackets();
}
}
@Test
public void testEncodeDecode() {
final int bufferSize = 30;
final int numCables = 16;
final int bytesToEncodePerEncoding = 10;
byte[][] rawMidi = new byte[numCables][bufferSize];
for (long seed = 45; seed < 3000; seed += 300) {
Random rnd = new Random(seed);
for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
rawMidi[cableNumber] = generateRandomByteStream(rnd, bufferSize);
// Change the last byte to SysEx End.
// This way the encoder is guaranteed to flush all packets.
rawMidi[cableNumber][bufferSize - 1] = (byte) 0xF7;
}
UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter();
usbMidiPacketConverter.createEncoders(numCables);
// Encode packets and interweave them
for (int startByte = 0; startByte < bufferSize;
startByte += bytesToEncodePerEncoding) {
for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
byte[] bytesToEncode = Arrays.copyOfRange(rawMidi[cableNumber], startByte,
startByte + bytesToEncodePerEncoding);
usbMidiPacketConverter.encodeMidiPackets(bytesToEncode, bytesToEncode.length,
cableNumber);
}
}
byte[] usbMidi = usbMidiPacketConverter.pullEncodedMidiPackets();
usbMidiPacketConverter.createDecoders(numCables);
// Now decode the MIDI packets to check if they are the same as the original
usbMidiPacketConverter.decodeMidiPackets(usbMidi, usbMidi.length);
for (int cableNumber = 0; cableNumber < numCables; cableNumber++) {
byte[] decodedRawMidi = usbMidiPacketConverter.pullDecodedMidiPackets(cableNumber);
compareByteArrays(rawMidi[cableNumber], decodedRawMidi);
}
}
}
}