Bug 1201590 - WebMIDI Mochitests; r=baku r=padenot draft
authorKyle Machulis <kyle@nonpolynomial.com>
Tue, 21 Jul 2015 14:28:30 -0700
changeset 759267 651fc8ef02e8355d327b46182a38fba3a3cd6509
parent 759266 de8d02eb9168e1be07a175db1f701e6f08c46e45
child 759268 b182a811a2abce1ebe6e8737ea55c37a9ea61364
push id100327
push userbmo:kyle@nonpolynomial.com
push dateSat, 24 Feb 2018 00:18:08 +0000
reviewersbaku, padenot
bugs1201590
milestone60.0a1
Bug 1201590 - WebMIDI Mochitests; r=baku r=padenot MozReview-Commit-ID: F6bSQ4oCRDq
.eslintignore
dom/midi/TestMIDIPlatformService.cpp
dom/midi/moz.build
dom/midi/tests/.eslintrc.js
dom/midi/tests/MIDITestUtils.js
dom/midi/tests/mochitest.ini
dom/midi/tests/test_midi_device_connect_disconnect.html
dom/midi/tests/test_midi_device_enumeration.html
dom/midi/tests/test_midi_device_explicit_open_close.html
dom/midi/tests/test_midi_device_implicit_open_close.html
dom/midi/tests/test_midi_device_pending.html
dom/midi/tests/test_midi_device_sysex.html
dom/midi/tests/test_midi_device_system_rt.html
dom/midi/tests/test_midi_packet_timing_sorting.html
dom/midi/tests/test_midi_permission_allow.html
dom/midi/tests/test_midi_permission_deny.html
dom/midi/tests/test_midi_permission_prompt.html
dom/tests/mochitest/general/test_interfaces.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -235,16 +235,17 @@ dom/ipc/**
 dom/json/**
 dom/jsurl/**
 dom/locales/**
 dom/manifest/**
 dom/mathml/**
 dom/media/**
 !dom/media/*.js*
 dom/messagechannel/**
+dom/midi/**
 dom/network/**
 dom/notification/Notification*.*
 dom/notification/test/browser/**
 dom/notification/test/unit/**
 dom/notification/test/mochitest/**
 dom/offline/**
 dom/payments/**
 dom/performance/**
--- a/dom/midi/TestMIDIPlatformService.cpp
+++ b/dom/midi/TestMIDIPlatformService.cpp
@@ -187,28 +187,24 @@ void TestMIDIPlatformService::ProcessMes
           nsCOMPtr<nsIRunnable> r(new ReceiveRunnable(mControlInputPort.id(), msg));
           mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
           break;
         }
         // Cause control test ports to connect
         case 0x01:
         {
           nsCOMPtr<nsIRunnable> r1(new AddPortRunnable(mStateTestInputPort));
-          nsCOMPtr<nsIRunnable> r2(new AddPortRunnable(mStateTestOutputPort));
           mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL);
-          mBackgroundThread->Dispatch(r2, NS_DISPATCH_NORMAL);
           break;
         }
         // Cause control test ports to disconnect
         case 0x02:
         {
           nsCOMPtr<nsIRunnable> r1(new RemovePortRunnable(mStateTestInputPort));
-          nsCOMPtr<nsIRunnable> r2(new RemovePortRunnable(mStateTestOutputPort));
           mBackgroundThread->Dispatch(r1, NS_DISPATCH_NORMAL);
-          mBackgroundThread->Dispatch(r2, NS_DISPATCH_NORMAL);
           break;
         }
         // Test for packet timing
         case 0x03:
         {
           // Append a few echo command packets in reverse timing order, should
           // come out in correct order on other end.
           nsTArray<MIDIMessage> newMsgs;
@@ -218,18 +214,18 @@ void TestMIDIPlatformService::ProcessMes
           msg.AppendElement(0x00);
           // PR_Now() returns nanosecods, and we need a double with fractional
           // milliseconds.
           TimeStamp currentTime = TimeStamp::Now();
           for (int i = 0; i <= 5; ++i) {
             // Insert messages with timestamps in reverse order, to make sure
             // we're sorting correctly.
             newMsgs.AppendElement(
-              MIDIMessage(msg, currentTime -
-                          TimeDuration::FromMilliseconds(i)));
+              MIDIMessage(msg, currentTime +
+                          TimeDuration::FromMilliseconds(100 - (i * 10))));
           }
           nsCOMPtr<nsIRunnable> r(new QueueMessagesRunnable(aPortId, newMsgs));
           mBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
           break;
         }
         default:
           NS_WARNING("Unknown Test MIDI message received!");
         }
--- a/dom/midi/moz.build
+++ b/dom/midi/moz.build
@@ -55,11 +55,9 @@ UNIFIED_SOURCES = [
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/dom/base',
 ]
 
-# TEST_DIRS += [
-#     'test',
-# ]
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+  "extends": [
+    "plugin:mozilla/mochitest-test",
+  ]
+};
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/MIDITestUtils.js
@@ -0,0 +1,56 @@
+/* eslint-env mozilla/frame-script */
+var MIDITestUtils = {
+  permissionSetup: (allow) => {
+    let permPromiseRes;
+    let permPromise = new Promise((res, rej) => { permPromiseRes = res; });
+    SpecialPowers.pushPrefEnv({"set": [["dom.webmidi.enabled", true],
+                                       ["midi.testing", true],
+                                       ["midi.prompt.testing", true],
+                                       ["media.navigator.permission.disabled", allow]]},
+                              () => {
+                                permPromiseRes();
+                              });
+    return permPromise;
+  },
+  // This list needs to stay synced with the ports in
+  // dom/midi/TestMIDIPlatformService.
+  inputInfo: {
+    id: "b744eebe-f7d8-499b-872b-958f63c8f522",
+    name: "Test Control MIDI Device Input Port",
+    manufacturer: "Test Manufacturer",
+    version: "1.0.0"
+  },
+  outputInfo: {
+    id: "ab8e7fe8-c4de-436a-a960-30898a7c9a3d",
+    name: "Test Control MIDI Device Output Port",
+    manufacturer: "Test Manufacturer",
+    version: "1.0.0"
+  },
+  stateTestInputInfo: {
+    id: "a9329677-8588-4460-a091-9d4a7f629a48",
+    name: "Test State MIDI Device Input Port",
+    manufacturer: "Test Manufacturer",
+    version: "1.0.0"
+  },
+  stateTestOutputInfo: {
+    id: "478fa225-b5fc-4fa6-a543-d32d9cb651e7",
+    name: "Test State MIDI Device Output Port",
+    manufacturer: "Test Manufacturer",
+    version: "1.0.0"
+  },
+  alwaysClosedTestOutputInfo: {
+    id: "f87d0c76-3c68-49a9-a44f-700f1125c07a",
+    name: "Always Closed MIDI Device Output Port",
+    manufacturer: "Test Manufacturer",
+    version: "1.0.0"
+  },
+  checkPacket: (expected, actual) => {
+    if (expected.length != actual.length) {
+      ok(false, "Packet " + expected + " length not same as packet " + actual);
+    }
+    for (var i = 0; i < expected.length; ++i) {
+      is(expected[i], actual[i], "Packet value " + expected[i] + " matches.");
+    }
+  }
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/mochitest.ini
@@ -0,0 +1,18 @@
+[DEFAULT]
+support-files =
+  MIDITestUtils.js
+scheme = https
+
+[test_midi_permission_prompt.html]
+[test_midi_permission_allow.html]
+[test_midi_permission_deny.html]
+[test_midi_device_enumeration.html]
+[test_midi_device_implicit_open_close.html]
+[test_midi_device_explicit_open_close.html]
+[test_midi_device_sysex.html]
+[test_midi_device_system_rt.html]
+[test_midi_packet_timing_sorting.html]
+[test_midi_device_connect_disconnect.html]
+disabled = Bug 1437204
+[test_midi_device_pending.html]
+disabled = Bug 1437204
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_connect_disconnect.html
@@ -0,0 +1,54 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       let output;
+
+       let midi_access;
+       try {
+         midi_access = await navigator.requestMIDIAccess({ "sysex": false });
+         ok(true, "MIDI Access Request successful");
+       } catch (e) {
+         ok(false, "MIDI Access Request failed!");
+         SimpleTest.finish();
+         return;
+       }
+       is(midi_access.sysexEnabled, false, "Sysex should be false");
+       output = midi_access.outputs.get(MIDITestUtils.outputInfo.id);
+       let statePromiseRes;
+       let statePromise = new Promise((res) => { statePromiseRes = res; });
+       await output.open();
+       let stateChangeHandler = (event) => {
+         if (event.port == output) {
+           return;
+         }
+         statePromiseRes(event.port);
+       };
+       midi_access.addEventListener("statechange", stateChangeHandler);
+       // Send command to connect new port.
+       output.send([0x90, 0x01, 0x00]);
+       let p = await statePromise;
+       is(p.state, "connected", "Device " + p.name + " connected");
+
+       // Rebuild our promise, we'll need to await another one.
+       statePromise = new Promise((res) => { statePromiseRes = res; });
+       output.send([0x90, 0x02, 0x00]);
+       p = await statePromise;
+       is(p.state, "disconnected", "Device " + p.name + " disconnected");
+       midi_access.removeEventListener("statechange", stateChangeHandler);
+       SimpleTest.finish();
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_enumeration.html
@@ -0,0 +1,46 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     let objectCompare = (type, props, obj) => {
+       for (var prop in props) {
+         is(props[prop], obj[prop], type + " property value " + prop + " is " + props[prop]);
+       }
+     };
+     let failOnCall = (event) => {
+       ok(false, "No connect/state events should be received on startup!");
+     };
+     async function runTests () {
+       await MIDITestUtils.permissionSetup(true);
+       // Request access without sysex.
+       let access = await navigator.requestMIDIAccess({ "sysex": false });
+       ok(true, "MIDI Access Request successful");
+       is(access.sysexEnabled, false, "Sysex should be false");
+       access.addEventListener("statechange", failOnCall);
+       var input_id = MIDITestUtils.inputInfo.id;
+       var output_id = MIDITestUtils.outputInfo.id;
+       var inputs = access.inputs;
+       var outputs = access.outputs;
+       is(inputs.size, 1, "Should have one input");
+       is(outputs.size, 2, "Should have two outputs");
+       ok(inputs.has(input_id), "input list should contain input id");
+       ok(outputs.has(output_id), "output list should contain output id");
+       var input = access.inputs.get(input_id);
+       var output = access.outputs.get(output_id);
+       objectCompare("input", MIDITestUtils.inputInfo, input);
+       objectCompare("output", MIDITestUtils.outputInfo, output);
+       access.removeEventListener("statechange", failOnCall);
+       SimpleTest.finish();
+     };
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_explicit_open_close.html
@@ -0,0 +1,95 @@
+<html>
+  <head>
+    <title>WebMIDI Device Open/Close Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+
+       let access;
+       try {
+         access = await navigator.requestMIDIAccess({ "sysex": false })
+       } catch (e) {
+         ok(false, "MIDI Access Request Failed!");
+         SimpleTest.finish();
+       }
+
+       ok(true, "MIDI Access Request successful");
+       let input = access.inputs.get(MIDITestUtils.inputInfo.id);
+       let portEventRes;
+       let accessEventRes;
+       let portEventPromise = new Promise((resolve, reject) => { portEventRes = resolve; });
+       let accessEventPromise = new Promise((resolve, reject) => { accessEventRes = resolve; });
+       let shouldClose = false;
+       let checkPort = (event) => {
+         ok(input === event.port, "input port object and event port object are same object");
+         ok(true, "port connection event fired");
+         ok(event.port.connection === (!shouldClose ? "open" : "closed"), "connection registered correctly");
+       };
+       let inputEventHandler = (event) => {
+         checkPort(event);
+         portEventRes();
+       };
+       let accessEventHandler = (event) => {
+         checkPort(event);
+         accessEventRes();
+       };
+       input.addEventListener("statechange", inputEventHandler);
+       access.addEventListener("statechange", accessEventHandler);
+       await input.open();
+       ok(true, "connection successful");
+       ok(input.connection === "open", "connection registered as open");
+       await Promise.all([portEventPromise, accessEventPromise]);
+       input.removeEventListener("statechange", inputEventHandler);
+       access.removeEventListener("statechange", accessEventHandler);
+       ok(true, "MIDI Port Open Test finished.");
+       ok(true, "Testing open failure");
+       let out_access;
+       try {
+         out_access = await navigator.requestMIDIAccess({ "sysex": false });
+       } catch (e) {
+         ok(false, "MIDI Access Request Failed!");
+         SimpleTest.finish();
+       }
+       let outputEventRes;
+       let outputEventHandler = (event) => {
+         ok(output_opened === event.port, "output port object and event port object are same object");
+         ok(true, "access connection event fired");
+         ok(event.port.connection === "closed", "connection registered as closed");
+       };
+       out_access.addEventListener("statechange", outputEventHandler);
+       let output_opened = out_access.outputs.get(MIDITestUtils.alwaysClosedTestOutputInfo.id);
+       try {
+         await output_opened.open();
+         ok(false, "Should've failed to open port!");
+       } catch(err) {
+         is(err.name, "InvalidAccessError", "error name " + err.name + " should be InvalidAccessError");
+         ok(output_opened.connection == "closed", "connection registered as closed");
+         ok(true, "Port not opened, test succeeded");
+       } finally {
+         out_access.removeEventListener("statechange", outputEventHandler);
+       }
+       ok(true, "Starting MIDI port closing test");
+       portEventPromise = new Promise((resolve, reject) => { portEventRes = resolve; });
+       accessEventPromise = new Promise((resolve, reject) => { accessEventRes = resolve; });
+       input.addEventListener("statechange", inputEventHandler);
+       access.addEventListener("statechange", accessEventHandler);
+       shouldClose = true;
+       await input.close();
+       ok(input.connection === "closed", "connection registered as closed");
+       await Promise.all([portEventPromise, accessEventPromise]);
+       input.removeEventListener("statechange", inputEventHandler);
+       access.removeEventListener("statechange", accessEventHandler);
+       SimpleTest.finish();
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_implicit_open_close.html
@@ -0,0 +1,67 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       let access = await navigator.requestMIDIAccess({ "sysex": false });
+       ok(true, "MIDI Access Request successful");
+       is(access.sysexEnabled, false, "Sysex should be false");
+
+       var checkCount = 0;
+       var reopened = false;
+       var input;
+       var output;
+       function checkCallbacks(port) {
+         ok(true, "Got port " + port.connection + " for " + port.name);
+         if (port.connection == "open") {
+           checkCount++;
+         } else {
+           if (!reopened) {
+             reopened = true;
+             // Ports are closed. Fire rest of tests.
+             input.onmidimessage = checkReturn;
+             output.send([0x90, 0x00, 0x7F]);
+           }
+         }
+         if (checkCount == 3) {
+           input.onstatechange = undefined;
+           output.onstatechange = undefined;
+           input.close();
+           output.close();
+           SimpleTest.finish();
+         }
+       }
+       function checkReturn(event) {
+         checkCount++;
+         ok(true, "Got echo message back");
+         MIDITestUtils.checkPacket(event.data, [0x90, 0x00, 0x7f]);
+         if (checkCount == 3) {
+           input.onstatechange = undefined;
+           output.onstatechange = undefined;
+           input.close();
+           output.close();
+           SimpleTest.finish();
+         }
+       }
+
+       input = access.inputs.get(MIDITestUtils.inputInfo.id);
+       output = access.outputs.get(MIDITestUtils.outputInfo.id);
+       // We automatically open ports, so close them first.
+       input.onstatechange = (event) => { checkCallbacks(event.port); };
+       output.onstatechange = (event) => { checkCallbacks(event.port); };
+       input.close();
+       output.close();
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_pending.html
@@ -0,0 +1,122 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+
+
+       var checkCount = 0;
+       var state = "connecting";
+       var output;
+       var test_ports = [];
+       let access;
+
+       let accessRes;
+       let accessRej;
+       let accessPromise;
+       let portRes;
+       let portRej;
+       let portPromise;
+
+       function resetPromises() {
+         accessPromise = new Promise((res, rej) => { accessRes = res; accessRej = rej; });
+         portPromise = new Promise((res, rej) => { portRes = res; portRej = rej; });
+       }
+
+       function accessStateChangeHandler(event) {
+         var p = event.port;
+         // We'll get an open event for the output control port. Ignore it.
+         if (p.name == MIDITestUtils.outputInfo.name) {
+           return;
+         }
+         accessRes(event);
+       }
+
+       function portStateChangeHandler(event) {
+         var p = event.port;
+         // We'll get an open event for the output control port. Ignore it.
+         if (p.name == MIDITestUtils.outputInfo.name) {
+           return;
+         }
+         portRes(event);
+       }
+
+       // Part 1: Create MIDIAccess object, attach state change listener to list for new connections
+       access = await navigator.requestMIDIAccess({ "sysex": false });
+       ok(true, "MIDI Access Request successful");
+       is(access.sysexEnabled, false, "Sysex should be false");
+       access.addEventListener("statechange", accessStateChangeHandler);
+
+       // Part 2: open test device, make sure it connects, attach event handler to device object
+       output = access.outputs.get(MIDITestUtils.outputInfo.id);
+       resetPromises();
+       output.send([0x90, 0x01, 0x00]);
+       let accessEvent = await accessPromise;
+       let testPort = accessEvent.port;
+       test_ports.push(testPort);
+       testPort.addEventListener("statechange", portStateChangeHandler);
+       is(testPort.state, "connected", "Device " + testPort.name + " connected");
+
+       // Part 3: Listen for port status change on open as both an access event
+       // and a port event.
+       resetPromises();
+       testPort.open();
+       accessEvent = await accessPromise;
+       is(testPort.connection, "open", "Connection " + testPort.name + " opened");
+       let portEvent = await portPromise;
+       is(testPort.connection, "open", "Connection " + testPort.name + " opened");
+
+       // Part 4: Disconnect port but don't close, check status to make sure we're pending.
+       resetPromises();
+       output.send([0x90, 0x02, 0x00]);
+       accessEvent = await accessPromise;
+       is(testPort.connection, "pending", "Connection " + testPort.name + " pending");
+       is(access.inputs.has(testPort.id), false, "port removed from input map while pending");
+       portEvent = await portPromise;
+       is(testPort.connection, "pending", "Connection " + testPort.name + " pending");
+
+       // Part 5: Connect ports again, make sure we return to the right status. The events will
+       // fire because the device has been readded to the device maps in the access object.
+       resetPromises();
+       output.send([0x90, 0x01, 0x00]);
+       accessEvent = await accessPromise;
+       var port = access.inputs.get(testPort.id);
+       is(port, accessEvent.port, "port in map and port in event should be the same");
+       is(testPort.connection, "pending", "Connection " + testPort.name + " pending");
+       portEvent = await portPromise;
+       is(testPort.connection, "pending", "Connection " + testPort.name + " pending");
+
+       // Part 6: Close out everything and clean up.
+       resetPromises();
+       accessEvent = await accessPromise;
+       is(accessEvent.port.connection, "open", "Connection " + testPort.name + " opened");
+       portEvent = await portPromise;
+       is(portEvent.port.connection, "open", "Connection " + testPort.name + " opened");
+
+       /* for (let port of test_ports) {
+        *   port.removeEventListener("statechange", checkDevices);
+        * }
+        * access.removeEventListener("statechange", checkDevices);*/
+       output.send([0x90, 0x02, 0x00]);
+       testPort.removeEventListener("statechange", portStateChangeHandler);
+       access.removeEventListener("statechange", accessStateChangeHandler);
+       access = undefined;
+       output = undefined;
+       testPort = undefined;
+       accessEvent = undefined;
+       portEvent = undefined;
+       SimpleTest.finish();
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_sysex.html
@@ -0,0 +1,57 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       var sysexCheckCount = 0;
+       var checkCount = 0;
+       var input;
+       var output;
+       function checkSysexReceive(event) {
+         checkCount++;
+         sysexCheckCount++;
+         if (sysexCheckCount == 1) {
+           is(event.data[0], 0xF0, "Echoed sysex message via sysex port");
+         } else {
+           is(event.data[0], 0x90, "Echoed regular message via sysex port");
+         }
+         if (checkCount == 5) {
+           SimpleTest.finish();
+         }
+       }
+
+       function checkNoSysexReceive(event) {
+         checkCount++;
+         is(event.data[0], 0x90, "Echoed regular message via non-sysex port");
+         if (checkCount == 5) {
+           SimpleTest.finish()
+         }
+       }
+
+       // Request access without sysex.
+       let access_regular = await navigator.requestMIDIAccess({ "sysex": false });
+       let access_sysex = await navigator.requestMIDIAccess({ "sysex": true });
+       ok(true, "MIDI Access Request successful");
+       ok(true, "Check for sysex message drop");
+       input = access_regular.inputs.get(MIDITestUtils.inputInfo.id);
+       output = access_sysex.outputs.get(MIDITestUtils.outputInfo.id);
+       input_sysex = access_sysex.inputs.get(MIDITestUtils.inputInfo.id);
+       input_sysex.onmidimessage = checkSysexReceive;
+       input.onmidimessage = checkNoSysexReceive;
+       output.send([0xF0, 0x00, 0xF7]);
+       output.send([0x90, 0x00, 0x01]);
+       output.send([0x90, 0x00, 0x01]);
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_device_system_rt.html
@@ -0,0 +1,39 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       var checkCount = 0;
+
+       function checkReturn(msg) {
+         checkCount++;
+         if (checkCount == 1) {
+           MIDITestUtils.checkPacket(msg.data, [0xF8]);
+         } else if (checkCount == 2) {
+           MIDITestUtils.checkPacket(msg.data, [0xF9]);
+         } else if (checkCount == 3) {
+           MIDITestUtils.checkPacket(msg.data, [0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0xF7]);
+           SimpleTest.finish();
+         }
+       }
+
+       // Request access without sysex.
+       let access_sysex = await navigator.requestMIDIAccess({ "sysex": true });
+       let input_sysex = access_sysex.inputs.get(MIDITestUtils.inputInfo.id);
+       input_sysex.onmidimessage = checkReturn;
+       let output_sysex = access_sysex.outputs.get(MIDITestUtils.outputInfo.id);
+       output_sysex.send([0xF0, 0x01, 0xF7]);
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_packet_timing_sorting.html
@@ -0,0 +1,47 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       var checkCount = 0;
+       var lastTime = 0;
+       var reopened = false;
+       var input;
+       var output;
+       function checkReturn(event) {
+         ok(event.timeStamp > lastTime, "Received timestamp " + event.timeStamp + " should be greater than " + lastTime);
+         lastTime = event.timeStamp;
+         checkCount++;
+
+         if (checkCount == 6) {
+           input.close();
+           output.close();
+           SimpleTest.finish();
+         }
+       }
+       ok("Testing MIDI packet reordering based on timestamps");
+       // Request access without sysex.
+       let access = await navigator.requestMIDIAccess({ "sysex": false });
+       ok(true, "MIDI Access Request successful");
+       is(access.sysexEnabled, false, "Sysex should be false");
+
+       input = access.inputs.get(MIDITestUtils.inputInfo.id);
+       output = access.outputs.get(MIDITestUtils.outputInfo.id);
+       input.onmidimessage = checkReturn;
+       // trigger the packet timing sorting tests
+       output.send([0x90, 0x03, 0x00], 0);
+       ok(true, "Waiting on packets");
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_permission_allow.html
@@ -0,0 +1,26 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       // Request access without sysex.
+       try {
+         await navigator.requestMIDIAccess({ "sysex": false })
+         ok(true, "MIDI Access Request successful");
+         SimpleTest.finish();
+       } catch {
+         ok(false, "MIDI Access Request Failed!");
+         SimpleTest.finish();
+       }
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_permission_deny.html
@@ -0,0 +1,26 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(false);
+       // Request access without sysex.
+       try {
+         await navigator.requestMIDIAccess({ "sysex": false });
+         ok(false, "MIDI Access Request Deny failed");
+         SimpleTest.finish();
+       } catch {
+         ok(true, "MIDI Access Request Deny successful!");
+         SimpleTest.finish();
+       }
+     }
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/midi/tests/test_midi_permission_prompt.html
@@ -0,0 +1,24 @@
+<html>
+  <head>
+    <title>WebMIDI Listener Test</title>
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="application/javascript" src="MIDITestUtils.js"></script>
+  </head>
+
+  <body onload="runTests()">
+    <script class="testbody" type="application/javascript">
+     SimpleTest.waitForExplicitFinish();
+
+     async function runTests() {
+       await MIDITestUtils.permissionSetup(true);
+       try {
+         await navigator.requestMIDIAccess({ "sysex": false });
+         ok(true, "Prompting for permissions succeeded!");
+       } catch (e) {
+         ok(false, "Prompting for permissions failed!");
+       }
+       SimpleTest.finish();
+     }
+    </script>
+  </body>
+</html>
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -660,16 +660,32 @@ var interfaceNamesInGlobalScope =
     {name: "MenuBoxObject", insecureContext: true, xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MessageChannel", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MessageEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MessagePort", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIAccess", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIConnectionEvent", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIInputMap", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIInput", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIMessageEvent", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIOutputMap", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIOutput", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MIDIPort", disabled: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MimeType", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MimeTypeArray", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MouseEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MouseScrollEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!