diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py
index 4f39c702edd..76e5bf9cb79 100644
--- a/python/servo/testing_commands.py
+++ b/python/servo/testing_commands.py
@@ -271,6 +271,18 @@ class MachCommands(CommandBase):
def test_jquery(self, release, dev):
return self.jquery_test_runner("test", release, dev)
+ @Command('test-dromaeo',
+ description='Run the Dromaeo test suite',
+ category='testing')
+ @CommandArgument('tests', default=["recommended"], nargs="...",
+ help="Specific tests to run")
+ @CommandArgument('--release', '-r', action='store_true',
+ help='Run the release build')
+ @CommandArgument('--dev', '-d', action='store_true',
+ help='Run the dev build')
+ def test_dromaeo(self, tests, release, dev):
+ return self.dromaeo_test_runner(tests, release, dev)
+
@Command('update-jquery',
description='Update the jQuery test suite expected results',
category='testing')
@@ -367,3 +379,28 @@ class MachCommands(CommandBase):
return subprocess.check_call(
[run_file, cmd, bin_path, base_dir])
+
+ def dromaeo_test_runner(self, tests, release, dev):
+ self.ensure_bootstrapped()
+ base_dir = path.abspath(path.join("tests", "dromaeo"))
+ dromaeo_dir = path.join(base_dir, "dromaeo")
+ run_file = path.join(base_dir, "run_dromaeo.py")
+
+ # Clone the Dromaeo repository if it doesn't exist
+ if not os.path.isdir(dromaeo_dir):
+ subprocess.check_call(
+ ["git", "clone", "-b", "servo", "--depth", "1", "https://github.com/notriddle/dromaeo", dromaeo_dir])
+
+ # Run pull in case the Dromaeo repo was updated since last test run
+ subprocess.check_call(
+ ["git", "-C", dromaeo_dir, "pull"])
+
+ # Compile test suite
+ subprocess.check_call(
+ ["make", "-C", dromaeo_dir, "web"])
+
+ # Check that a release servo build exists
+ bin_path = path.abspath(self.get_binary_path(release, dev))
+
+ return subprocess.check_call(
+ [run_file, "|".join(tests), bin_path, base_dir])
diff --git a/tests/dromaeo/run_dromaeo.py b/tests/dromaeo/run_dromaeo.py
new file mode 100755
index 00000000000..6a2cea41dee
--- /dev/null
+++ b/tests/dromaeo/run_dromaeo.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import re
+import subprocess
+import sys
+import BaseHTTPServer
+import SimpleHTTPServer
+import SocketServer
+import threading
+import urlparse
+import json
+
+
+# Port to run the HTTP server on for Dromaeo.
+TEST_SERVER_PORT = 8192
+
+
+# Run servo and print / parse the results for a specific Dromaeo module.
+def run_servo(servo_exe, tests):
+ url = "http://localhost:{0}/dromaeo/web/?{1}&automated&post_json".format(TEST_SERVER_PORT, tests)
+ args = [servo_exe, url, "-z", "-f"]
+ return subprocess.Popen(args)
+
+
+# Print usage if command line args are incorrect
+def print_usage():
+ print("USAGE: {0} tests servo_binary dromaeo_base_dir".format(sys.argv[0]))
+
+
+# Handle the POST at the end
+class RequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+ def do_POST(self):
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write("POST OK.
")
+ length = int(self.headers.getheader('content-length'))
+ parameters = urlparse.parse_qs(self.rfile.read(length))
+ self.server.got_post = True
+ self.server.post_data = parameters['data']
+
+ def log_message(self, format, *args):
+ return
+
+
+if __name__ == '__main__':
+ if len(sys.argv) == 4:
+ tests = sys.argv[1]
+ servo_exe = sys.argv[2]
+ base_dir = sys.argv[3]
+ os.chdir(base_dir)
+
+ # Ensure servo binary can be found
+ if not os.path.isfile(servo_exe):
+ print("Unable to find {0}. This script expects an existing build of Servo.".format(servo_exe))
+ sys.exit(1)
+
+ # Start the test server
+ server = BaseHTTPServer.HTTPServer(('', TEST_SERVER_PORT), RequestHandler)
+
+ print("Testing Dromaeo on Servo!")
+ proc = run_servo(servo_exe, tests)
+ server.got_post = False
+ while not server.got_post:
+ server.handle_request()
+ data = json.loads(server.post_data[0])
+ n = 0
+ l = 0
+ for test in data:
+ n = max(n, len(data[test]))
+ l = max(l, len(test))
+ print("\n Test{0} | Time".format(" " * (l - len("Test"))))
+ print("-{0}-|-{1}-".format("-" * l, "-" * n))
+ for test in data:
+ print(" {0}{1} | {2}".format(test, " " * (l - len(test)), data[test]))
+ proc.kill()
+ else:
+ print_usage()