Bug 1305877 - Add a directory hash chunker; r?jgraham draft
authorGregory Szorc <gps@mozilla.com>
Thu, 29 Sep 2016 14:42:50 -0700
changeset 420704 c9311e759dd59577a0618539088cecc6f51cfd89
parent 420703 17d8e22ef616cf7f24a360a56cbe9190e4429bdf
child 420705 519bfe64a1ba96a845c633f50e530a27191bac43
push id31265
push usergszorc@mozilla.com
push dateTue, 04 Oct 2016 13:32:17 +0000
reviewersjgraham
bugs1305877
milestone52.0a1
Bug 1305877 - Add a directory hash chunker; r?jgraham Per discussion in the bug, we want automation to behave like local development mode with regards to running tests in a directory. That means we want all tests in a directory to be executed together. We introduce a directory hash chunker. It is like the HashChunker except it hashes the directory of the test (not the full path). This ensures that all tests in the same directory end up in the same chunk. Compared to HashChunker, this will likely make slow directories contribute to higher variance in chunk execution times. MozReview-Commit-ID: CeV0Gi6NRRp
testing/web-platform/harness/wptrunner/testloader.py
testing/web-platform/harness/wptrunner/wptcommandline.py
--- a/testing/web-platform/harness/wptrunner/testloader.py
+++ b/testing/web-platform/harness/wptrunner/testloader.py
@@ -46,16 +46,30 @@ class HashChunker(TestChunker):
     def __call__(self, manifest):
         chunk_index = self.chunk_number - 1
         for test_path, tests in manifest:
             h = int(hashlib.md5(test_path).hexdigest(), 16)
             if h % self.total_chunks == chunk_index:
                 yield test_path, tests
 
 
+class DirectoryHashChunker(TestChunker):
+    """Like HashChunker except the directory is hashed.
+
+    This ensures that all tests in the same directory end up in the same
+    chunk.
+    """
+    def __call__(self, manifest):
+        chunk_index = self.chunk_number - 1
+        for test_path, tests in manifest:
+            h = int(hashlib.md5(os.path.dirname(test_path)).hexdigest(), 16)
+            if h % self.total_chunks == chunk_index:
+                yield test_path, tests
+
+
 class EqualTimeChunker(TestChunker):
     def _group_by_directory(self, manifest_items):
         """Split the list of manifest items into a ordered dict that groups tests in
         so that anything in the same subdirectory beyond a depth of 3 is in the same
         group. So all tests in a/b/c, a/b/c/d and a/b/c/e will be grouped together
         and separate to tests in a/b/f
 
         Returns: tuple (ordered dict of {test_dir: PathData}, total estimated runtime)
@@ -446,16 +460,17 @@ class TestLoader(object):
         self.include_https = include_https
 
         self.chunk_type = chunk_type
         self.total_chunks = total_chunks
         self.chunk_number = chunk_number
 
         self.chunker = {"none": Unchunked,
                         "hash": HashChunker,
+                        "dir_hash": DirectoryHashChunker,
                         "equal_time": EqualTimeChunker}[chunk_type](total_chunks,
                                                                     chunk_number)
 
         self._test_ids = None
 
         self.directory_manifests = {}
 
         self._load_tests()
--- a/testing/web-platform/harness/wptrunner/wptcommandline.py
+++ b/testing/web-platform/harness/wptrunner/wptcommandline.py
@@ -140,17 +140,17 @@ scheme host and port.""")
                             help="Build is a release (overrides any mozinfo file)")
 
 
     chunking_group = parser.add_argument_group("Test Chunking")
     chunking_group.add_argument("--total-chunks", action="store", type=int, default=1,
                                 help="Total number of chunks to use")
     chunking_group.add_argument("--this-chunk", action="store", type=int, default=1,
                                 help="Chunk number to run")
-    chunking_group.add_argument("--chunk-type", action="store", choices=["none", "equal_time", "hash"],
+    chunking_group.add_argument("--chunk-type", action="store", choices=["none", "equal_time", "hash", "dir_hash"],
                                 default=None, help="Chunking type to use")
 
     ssl_group = parser.add_argument_group("SSL/TLS")
     ssl_group.add_argument("--ssl-type", action="store", default=None,
                         choices=["openssl", "pregenerated", "none"],
                         help="Type of ssl support to enable (running without ssl may lead to spurious errors)")
 
     ssl_group.add_argument("--openssl-binary", action="store",