Bug 1201590 - WebMIDI Mochitests; r=baku,padenot
authorKyle Machulis <kyle@nonpolynomial.com>
Tue, 21 Jul 2015 14:28:30 -0700
changeset 460152 21fc5c9ae6288bb9fe16b3cf44e38afb3fab6f00
parent 460151 a84ef21b6b36193d56d5fe470cd27074b810391a
child 460153 8b156da986eeaeb427afd88b18f5136df29249c5
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, padenot
bugs1201590
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1201590 - WebMIDI Mochitests; r=baku,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;
@@ -219,17 +215,17 @@ void TestMIDIPlatformService::ProcessMes
           // 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)));
+                          TimeDuration::FromMilliseconds(i * 2)));
           }
           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!