Bug 1296842: check parameters; r=jmaher
authorDustin J. Mitchell <dustin@mozilla.com>
Mon, 07 Nov 2016 19:13:40 +0000
changeset 364325 126467fa0641535597213dd494aec402bd47c5ff
parent 364324 f29234ab0d3c57a374824bec5278fe6c37f561be
child 364326 8988e17605ed135abd91f64a0c3c30b559d453ce
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher
bugs1296842
milestone52.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 1296842: check parameters; r=jmaher MozReview-Commit-ID: 1JCpufowNHD
taskcluster/mach_commands.py
taskcluster/taskgraph/parameters.py
taskcluster/taskgraph/test/test_parameters.py
--- a/taskcluster/mach_commands.py
+++ b/taskcluster/mach_commands.py
@@ -212,16 +212,17 @@ class MachCommands(MachCommandBase):
     def show_taskgraph(self, graph_attr, options):
         import taskgraph.parameters
         import taskgraph.target_tasks
         import taskgraph.generator
 
         try:
             self.setup_logging(quiet=options['quiet'], verbose=options['verbose'])
             parameters = taskgraph.parameters.load_parameters_file(options)
+            parameters.check()
 
             target_tasks_method = parameters.get('target_tasks_method', 'all_tasks')
             target_tasks_method = taskgraph.target_tasks.get_method(target_tasks_method)
             tgg = taskgraph.generator.TaskGraphGenerator(
                 root_dir=options['root'],
                 parameters=parameters,
                 target_tasks_method=target_tasks_method)
 
--- a/taskcluster/taskgraph/parameters.py
+++ b/taskcluster/taskgraph/parameters.py
@@ -5,20 +5,56 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import json
 import yaml
 from mozbuild.util import ReadOnlyDict
 
+# Please keep this list sorted and in sync with taskcluster/docs/parameters.rst
+PARAMETER_NAMES = set([
+    'base_repository',
+    'build_date',
+    'head_ref',
+    'head_repository',
+    'head_rev',
+    'level',
+    'message',
+    'moz_build_date',
+    'optimize_target_tasks',
+    'owner',
+    'project',
+    'pushdate',
+    'pushlog_id',
+    'target_tasks_method',
+    'triggered_by',
+])
+
 
 class Parameters(ReadOnlyDict):
     """An immutable dictionary with nicer KeyError messages on failure"""
+    def check(self):
+        names = set(self)
+        msg = []
+
+        missing = PARAMETER_NAMES - names
+        if missing:
+            msg.append("missing parameters: " + ", ".join(missing))
+
+        extra = names - PARAMETER_NAMES
+        if extra:
+            msg.append("extra parameters: " + ", ".join(extra))
+
+        if msg:
+            raise Exception("; ".join(msg))
+
     def __getitem__(self, k):
+        if k not in PARAMETER_NAMES:
+            raise KeyError("no such parameter {!r}".format(k))
         try:
             return super(Parameters, self).__getitem__(k)
         except KeyError:
             raise KeyError("taskgraph parameter {!r} not found".format(k))
 
 
 def load_parameters_file(options):
     """
--- a/taskcluster/taskgraph/test/test_parameters.py
+++ b/taskcluster/taskgraph/test/test_parameters.py
@@ -1,36 +1,55 @@
 # 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
 
 import unittest
 
-from ..parameters import Parameters, load_parameters_file
+from ..parameters import Parameters, load_parameters_file, PARAMETER_NAMES
 from mozunit import main, MockedOpen
 
 
 class TestParameters(unittest.TestCase):
 
+    vals = {n: n for n in PARAMETER_NAMES}
+
     def test_Parameters_immutable(self):
-        p = Parameters(x=10, y=20)
+        p = Parameters(**self.vals)
 
         def assign():
-            p['x'] = 20
+            p['head_ref'] = 20
         self.assertRaises(Exception, assign)
 
-    def test_Parameters_KeyError(self):
-        p = Parameters(x=10, y=20)
+    def test_Parameters_missing_KeyError(self):
+        p = Parameters(**self.vals)
         self.assertRaises(KeyError, lambda: p['z'])
 
+    def test_Parameters_invalid_KeyError(self):
+        """even if the value is present, if it's not a valid property, raise KeyError"""
+        p = Parameters(xyz=10, **self.vals)
+        self.assertRaises(KeyError, lambda: p['xyz'])
+
     def test_Parameters_get(self):
-        p = Parameters(x=10, y=20)
-        self.assertEqual(p['x'], 10)
+        p = Parameters(head_ref=10, level=20)
+        self.assertEqual(p['head_ref'], 10)
+
+    def test_Parameters_check(self):
+        p = Parameters(**self.vals)
+        p.check()  # should not raise
+
+    def test_Parameters_check_missing(self):
+        p = Parameters()
+        self.assertRaises(Exception, lambda: p.check())
+
+    def test_Parameters_check_extra(self):
+        p = Parameters(xyz=10, **self.vals)
+        self.assertRaises(Exception, lambda: p.check())
 
     def test_load_parameters_file_yaml(self):
         with MockedOpen({"params.yml": "some: data\n"}):
             self.assertEqual(
                     load_parameters_file({'parameters': 'params.yml'}),
                     {'some': 'data'})
 
     def test_load_parameters_file_json(self):