Bug 1352495 - File-info mach subcommand to dump tests by bug component. r=gps draft
authorArmen Zambrano <armenzg@mozilla.com>
Tue, 04 Apr 2017 15:58:29 -0400
changeset 559910 cc16d2be3458fa6c123d57e11c629a54cf25aed8
parent 559608 731639fccc709a4dd95fed7e9dda88efb2227906
child 560698 863ee15508b31846656e1625acc93f2e92a2ca34
push id53271
push userbmo:armenzg@mozilla.com
push dateMon, 10 Apr 2017 20:06:24 +0000
reviewersgps
bugs1352495
milestone55.0a1
Bug 1352495 - File-info mach subcommand to dump tests by bug component. r=gps This change add a mach subcommand to construct the relationship between bug components and test files. The sample command for the whole tree is: ./mach file-info test-bug-components-associations . You can run this example very fast: ./mach file-info test-bug-components-associations testing/mochitest/ We also generate a tests_in_bug_components.json file with what tests fall under which bug component. MozReview-Commit-ID: 6854R4MJ31G
python/mozbuild/mozbuild/frontend/mach_commands.py
--- a/python/mozbuild/mozbuild/frontend/mach_commands.py
+++ b/python/mozbuild/mozbuild/frontend/mach_commands.py
@@ -1,15 +1,16 @@
 # 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/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from collections import defaultdict
+import json
 import os
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
     SubCommand,
 )
@@ -111,16 +112,86 @@ class MozbuildFileCommands(MachCommandBa
             print(e.message)
             return 1
 
         for component, files in sorted(components.items(), key=lambda x: (x is None, x)):
             print('%s :: %s' % (component.product, component.component) if component else 'UNKNOWN')
             for f in sorted(files):
                 print('  %s' % f)
 
+
+    @SubCommand('file-info', 'test-bug-components-associations',
+                'Show relations between tests and bug components.')
+    @CommandArgument('paths', nargs='+',
+                     help='Paths whose data to query')
+    def associations(self, paths):
+        """Show metadata for each test file.
+
+        Given a requested path print a break down of bug components to test files
+        """
+        from mozbuild.testing import TestResolver
+        resolver = self._spawn(TestResolver)
+
+        # This information comes from the build generated all-tests.pkl file
+        # {u'dir_relpath': u'toolkit/mozapps/update/tests/chrome',
+        #  u'file_relpath': u'toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul',
+        #  u'flavor': u'chrome',
+        #  'here': u'/Users/armenzg/repos/firefox/obj-x86_64-apple-darwin16.5.0/_tests/testing/mochitest/chrome/toolkit/mozapps/update/tests/chrome',
+        #  'manifest': u'/Users/armenzg/repos/firefox/toolkit/mozapps/update/tests/chrome/chrome.ini',
+        #  'name': 'test_0010_background_basic.xul',
+        #  'path': u'/Users/armenzg/repos/firefox/obj-x86_64-apple-darwin16.5.0/_tests/testing/mochitest/chrome/toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul',
+        #  'relpath': u'toolkit/mozapps/update/tests/chrome/test_0010_background_basic.xul',
+        #  'support-files': '\nutils.js\nupdate.sjs',
+        #  'tags': 'appupdate'}
+        tests = list(resolver.resolve_tests(paths=paths))
+
+        # This data structure will help determine if a file is a test file
+        tests_by_path = {}
+        # NOTE: Metadata generated by resolve_tests() can have multiple items for
+        # the same test file. Some test files have different configurations.
+        # Each test configuration is represented as its own test entry in the test resolver.
+        # We're de-duping based on file path
+        for metadata in tests:
+            # Metadata for reftests use 'file_relpath' while the rest use 'relpath'
+            path = metadata.get('relpath', metadata.get('file_relpath'))
+            # Bug 1334525 - We can't handle web-platform proplery; ignore for now
+            if path and 'web-platform' not in path:
+                tests_by_path[path] = metadata
+
+        print('We have %d tests under this/these paths: %s' % (len(tests_by_path), paths))
+
+        # Collect bug components for each file
+        files_info = self._get_files_info(tests_by_path.keys())
+
+        # Both data structures should have the same number of file paths
+        assert sorted(files_info.keys()) == sorted(tests_by_path.keys())
+
+        # Let's create a data structure to contain the bug components recognized
+        components_to_files_metadata = defaultdict(list)
+        for path, module_info in files_info.items():
+            m = module_info.get('BUG_COMPONENT', 'UNKNOWN')
+            if m != 'UNKNOWN':
+                m = '%s::%s' % (m.product, m.component)
+            components_to_files_metadata[m].append(path)
+
+        print('We have found tests in %d components' % len(components_to_files_metadata))
+
+        # Order the list to guarantee deterministic output when dumping to file
+        for k, _ in components_to_files_metadata.items():
+            components_to_files_metadata[k].sort()
+
+        with open('tests_in_bug_components.json', 'w') as fd:
+            fd.write(json.dumps(components_to_files_metadata, indent=4, sort_keys=True))
+
+        for c, files in sorted(components_to_files_metadata.items()):
+            print('%s -> %s test files' % (c, len(files)))
+
+        print('We have generated the file tests_in_bug_components.json with more data')
+
+
     @SubCommand('file-info', 'missing-bugzilla',
                 'Show files missing Bugzilla component info')
     @CommandArgument('-r', '--rev',
                      help='Version control revision to look up info from')
     @CommandArgument('paths', nargs='+',
                      help='Paths whose data to query')
     def file_info_missing_bugzilla(self, paths, rev=None):
         try: