From 65424be3ef1accc159c9a6b38f05493ab8ef8c8d Mon Sep 17 00:00:00 2001
From: Rick Waldron <waldron.rick@gmail.com>
Date: Thu, 7 Sep 2017 16:35:07 -0400
Subject: [PATCH] Lint: harness features flag enforcement via linter

---
 harness/features.yml                          |  3 +
 tools/lint/lib/check.py                       |  2 +-
 tools/lint/lib/checks/harnessfeatures.py      | 59 +++++++++++++++++++
 tools/lint/lint.py                            |  6 +-
 .../test/fixtures/harness_features_empty.js   | 12 ++++
 .../harness_features_multiple_includes.js     | 11 ++++
 .../test/fixtures/harness_features_partial.js | 12 ++++
 .../test/fixtures/harness_features_valid.js   | 11 ++++
 8 files changed, 114 insertions(+), 2 deletions(-)
 create mode 100644 harness/features.yml
 create mode 100644 tools/lint/lib/checks/harnessfeatures.py
 create mode 100644 tools/lint/test/fixtures/harness_features_empty.js
 create mode 100644 tools/lint/test/fixtures/harness_features_multiple_includes.js
 create mode 100644 tools/lint/test/fixtures/harness_features_partial.js
 create mode 100644 tools/lint/test/fixtures/harness_features_valid.js

diff --git a/harness/features.yml b/harness/features.yml
new file mode 100644
index 0000000000..34a3a0bfa9
--- /dev/null
+++ b/harness/features.yml
@@ -0,0 +1,3 @@
+propertyHelper.js: [template]
+typeCoercion.js: [Symbol.toPrimitive,BigInt]
+testTypedArray.js: [TypedArray]
diff --git a/tools/lint/lib/check.py b/tools/lint/lib/check.py
index 3130728224..45bbaa3140 100644
--- a/tools/lint/lib/check.py
+++ b/tools/lint/lib/check.py
@@ -1,5 +1,5 @@
 class Check(object):
-    '''Base class for  defining linting checks.'''
+    '''Base class for defining linting checks.'''
     ID = None
 
     def run(self, name, meta, source):
diff --git a/tools/lint/lib/checks/harnessfeatures.py b/tools/lint/lib/checks/harnessfeatures.py
new file mode 100644
index 0000000000..8115b51cd5
--- /dev/null
+++ b/tools/lint/lib/checks/harnessfeatures.py
@@ -0,0 +1,59 @@
+import yaml
+
+from ..check import Check
+
+class CheckHarnessFeatures(Check):
+    '''Ensure tests that use harnesses with explicit features flag requirements
+    specify only `features` from a list of valid values.'''
+    ID = 'HARNESS_FEATURES'
+
+    def __init__(self):
+        with open('./harness/features.yml', 'r') as f:
+            self.include_has_features = yaml.load(f.read())
+
+    def comparison_result_lists(self, meta):
+
+        result = {'features': set(), 'missing': set()}
+        meta_features = meta['features'] if 'features' in meta else []
+        meta_includes = meta['includes']
+        features = []
+
+        if not meta or 'includes' not in meta:
+            return result
+
+        if len(meta_includes) == 0:
+            return result
+
+        for meta_include in meta_includes:
+            if meta_include in self.include_has_features:
+                features = self.include_has_features[meta_include]
+
+            if len(features) == 0:
+                return result
+
+            if 'features' not in meta or len(meta['features']) == 0:
+                result['missing'].update(features)
+            else:
+                meta_features = meta['features']
+
+                for feature in features:
+                    if feature not in meta_features:
+                        result['missing'].add(feature)
+
+        result['features'].update(meta_features);
+
+        return result
+
+
+    def run(self, name, meta, source):
+
+        result = self.comparison_result_lists(meta)
+
+        if len(result['features']) == 0 and len(result['missing']) == 0:
+            return
+
+        if len(result['missing']) > 0:
+            if len(result['features']) == 0:
+                return 'Missing: `features: [%s]`' % ', '.join(list(result['missing']))
+            else:
+                return 'Missing from `features`: %s' % ', '.join(list(result['missing']))
diff --git a/tools/lint/lint.py b/tools/lint/lint.py
index 09e22507a0..9c446b5bb9 100755
--- a/tools/lint/lint.py
+++ b/tools/lint/lint.py
@@ -7,6 +7,7 @@ import sys
 
 from lib.collect_files import collect_files
 from lib.checks.features import CheckFeatures
+from lib.checks.harnessfeatures import CheckHarnessFeatures
 from lib.checks.frontmatter import CheckFrontmatter
 from lib.checks.license import CheckLicense
 from lib.checks.negative import CheckNegative
@@ -23,7 +24,10 @@ parser.add_argument('path',
         help='file name or directory of files to lint')
 
 checks = [
-        CheckFrontmatter(), CheckFeatures('features.txt'), CheckLicense(),
+        CheckFrontmatter(),
+        CheckFeatures('features.txt'),
+        CheckHarnessFeatures(),
+        CheckLicense(),
         CheckNegative()
     ]
 
diff --git a/tools/lint/test/fixtures/harness_features_empty.js b/tools/lint/test/fixtures/harness_features_empty.js
new file mode 100644
index 0000000000..2fc5bae129
--- /dev/null
+++ b/tools/lint/test/fixtures/harness_features_empty.js
@@ -0,0 +1,12 @@
+HARNESS_FEATURES - Missing Frontmatter: `features: [TypedArray]`
+^ expected errors | v input
+// Copyright (C) 2017 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+features: []
+includes: [testTypedArray.js]
+---*/
+
+// empty
diff --git a/tools/lint/test/fixtures/harness_features_multiple_includes.js b/tools/lint/test/fixtures/harness_features_multiple_includes.js
new file mode 100644
index 0000000000..c9cabb8d71
--- /dev/null
+++ b/tools/lint/test/fixtures/harness_features_multiple_includes.js
@@ -0,0 +1,11 @@
+HARNESS_FEATURES - Missing Frontmatter: `features: [TypedArray, template]
+^ expected errors | v input
+// Copyright (C) 2017 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+includes: [testTypedArray.js, compareArray.js]
+---*/
+
+// empty
diff --git a/tools/lint/test/fixtures/harness_features_partial.js b/tools/lint/test/fixtures/harness_features_partial.js
new file mode 100644
index 0000000000..4d6a15c551
--- /dev/null
+++ b/tools/lint/test/fixtures/harness_features_partial.js
@@ -0,0 +1,12 @@
+HARNESS_FEATURES - Missing from `features`: Symbol.toPrimitive
+^ expected errors | v input
+// Copyright (C) 2017 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+features: [BigInt]
+includes: [typeCoercion.js]
+---*/
+
+// empty
diff --git a/tools/lint/test/fixtures/harness_features_valid.js b/tools/lint/test/fixtures/harness_features_valid.js
new file mode 100644
index 0000000000..4f92cf54eb
--- /dev/null
+++ b/tools/lint/test/fixtures/harness_features_valid.js
@@ -0,0 +1,11 @@
+^ expected errors | v input
+// Copyright (C) 2017 Rick Waldron. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+features: [TypedArray]
+includes: [testTypedArray.js]
+---*/
+
+// empty
-- 
GitLab