summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjugglinmike <mike@mikepennisi.com>2017-05-01 12:04:05 -0400
committerLeo Balter <leonardo.balter@gmail.com>2017-05-01 12:04:05 -0400
commit74954bfa915136c7998f8a0d9ca40b885075d409 (patch)
tree61c91d90c8f612dad18bffee1e59720f734998c9
parent7bb4cd8f417f9568521d62d282a7aec9b46699c1 (diff)
downloadqtdeclarative-testsuites-74954bfa915136c7998f8a0d9ca40b885075d409.tar.gz
Introduce automated validation for test format (#994)
This script is intended to identify common test file formatting errors prior to their acceptance into the project. It is designed to support future extensions for additional validation rules.
-rw-r--r--.travis.yml7
-rw-r--r--CONTRIBUTING.md13
-rw-r--r--lint.whitelist2
-rw-r--r--test/built-ins/Object/assign/Override.js4
-rw-r--r--tools/lint/__init__.py0
-rw-r--r--tools/lint/lib/__init__.py0
-rw-r--r--tools/lint/lib/check.py6
-rw-r--r--tools/lint/lib/checks/__init__.py0
-rw-r--r--tools/lint/lib/checks/frontmatter.py42
-rw-r--r--tools/lint/lib/checks/license.py43
-rw-r--r--tools/lint/lib/collect_files.py20
-rw-r--r--tools/lint/lib/eprint.py5
-rw-r--r--tools/lint/lib/frontmatter.py16
-rw-r--r--tools/lint/lib/whitelist.py24
-rwxr-xr-xtools/lint/lint.py74
-rw-r--r--tools/lint/requirements.txt1
-rw-r--r--tools/lint/test/fixtures/frontmatter_invalid_yaml.js15
-rw-r--r--tools/lint/test/fixtures/frontmatter_missing_desc.js13
-rw-r--r--tools/lint/test/fixtures/frontmatter_module_FIXTURE.js7
-rw-r--r--tools/lint/test/fixtures/frontmatter_module_with_meta_FIXTURE.js15
-rw-r--r--tools/lint/test/fixtures/frontmatter_negative_missing_phase.js12
-rw-r--r--tools/lint/test/fixtures/frontmatter_negative_missing_type.js12
-rw-r--r--tools/lint/test/fixtures/frontmatter_negative_string.js11
-rw-r--r--tools/lint/test/fixtures/frontmatter_negative_valid.js12
-rw-r--r--tools/lint/test/fixtures/frontmatter_omitted.js8
-rw-r--r--tools/lint/test/fixtures/frontmatter_unrecognized.js15
-rw-r--r--tools/lint/test/fixtures/license_alternate_1.js9
-rw-r--r--tools/lint/test/fixtures/license_alternate_2.js9
-rw-r--r--tools/lint/test/fixtures/license_alternate_3.js9
-rw-r--r--tools/lint/test/fixtures/license_alternate_4,js10
-rw-r--r--tools/lint/test/fixtures/license_alternate_5.js9
-rw-r--r--tools/lint/test/fixtures/license_generated.js11
-rw-r--r--tools/lint/test/fixtures/license_invalid_year.js10
-rw-r--r--tools/lint/test/fixtures/license_missing.js8
-rw-r--r--tools/lint/test/fixtures/valid_es5id.js9
-rw-r--r--tools/lint/test/fixtures/valid_es6id.js9
-rw-r--r--tools/lint/test/fixtures/valid_esid.js9
-rwxr-xr-xtools/lint/test/run.py100
-rwxr-xr-x[-rw-r--r--]tools/scripts/ci_build.sh (renamed from tools/scripts/ci.sh)0
-rwxr-xr-xtools/scripts/ci_lint.sh18
-rwxr-xr-x[-rw-r--r--]tools/scripts/deploy.sh0
41 files changed, 592 insertions, 5 deletions
diff --git a/.travis.yml b/.travis.yml
index f5788e9fd..0d8b7440a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,9 @@
language: python
install: pip install --requirement tools/generation/requirements.txt
script:
- - echo The test generation tool should be working.
+ - ./tools/scripts/ci_build.sh
- ./tools/generation/test/run.py
- - sh ./tools/scripts/ci.sh
+ - ./tools/lint/test/run.py
+ - ./tools/scripts/ci_lint.sh
after_success:
- - sh ./tools/scripts/deploy.sh
+ - ./tools/scripts/deploy.sh
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bee74e42a..8e007afc1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -249,6 +249,19 @@ p.then(function () {
As above, exceptions that are thrown from a `then` clause are passed to a later `$DONE` function and reported asynchronously.
+## Linting
+
+Some of the expectations documented here are enforced via a "linting" script. This script is used to validate patches automatically at submission time, but it may also be invoked locally via the following command:
+
+ python tools/lint/lint.py --whitelist lint.whitelist [paths to tests]
+
+...where `[paths to tests]` is a list of one or more paths to test files or directories containing test files.
+
+In some cases, it may be necessary for a test to intentionally violate the rules enforced by the linting tool. Such violations can be allowed by including the path of the test(s) in the `lint.whitelist` file. Each path must appear on a dedicated line in that file, and a space-separated list of rules to ignore must follow each path. Lines beginning with the pound sign (`#`) will be ignored. For example:
+
+ # This file documents authorship information and is not itself a test
+ test/built-ins/Simd/AUTHORS FRONTMATTER LICENSE
+
## Procedurally-generated tests
Some language features are expressed through a number of distinct syntactic forms. Test262 maintains these tests as a set of "test cases" and "test templates" in order to ensure equivalent coverage across all forms. The sub-directories within the `src/` directory describe the various language features that benefit from this approach.
diff --git a/lint.whitelist b/lint.whitelist
new file mode 100644
index 000000000..7954856d9
--- /dev/null
+++ b/lint.whitelist
@@ -0,0 +1,2 @@
+# This file documents authorship information and is not itself a test
+test/built-ins/Simd/AUTHORS FRONTMATTER LICENSE
diff --git a/test/built-ins/Object/assign/Override.js b/test/built-ins/Object/assign/Override.js
index 0a7d69438..426713ef3 100644
--- a/test/built-ins/Object/assign/Override.js
+++ b/test/built-ins/Object/assign/Override.js
@@ -8,13 +8,13 @@ es6id: 19.1.2.1.5.c
//"a" will be an property of the final object and the value should be 1
var target = {a:1};
-/*---
+/*
"1a2c3" have own enumerable properties, so it Should be wrapped to objects;
{b:6} is an object,should be assigned to final object.
undefined and null should be ignored;
125 is a number,it cannot has own enumerable properties;
{a:"c"},{a:5} will override property a, the value should be 5.
----*/
+*/
var result = Object.assign(target,"1a2c3",{a:"c"},undefined,{b:6},null,125,{a:5});
assert.sameValue(Object.keys(result).length, 7 , "The length should be 7 in the final object.");
diff --git a/tools/lint/__init__.py b/tools/lint/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/lint/__init__.py
diff --git a/tools/lint/lib/__init__.py b/tools/lint/lib/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/lint/lib/__init__.py
diff --git a/tools/lint/lib/check.py b/tools/lint/lib/check.py
new file mode 100644
index 000000000..313072822
--- /dev/null
+++ b/tools/lint/lib/check.py
@@ -0,0 +1,6 @@
+class Check(object):
+ '''Base class for defining linting checks.'''
+ ID = None
+
+ def run(self, name, meta, source):
+ return True
diff --git a/tools/lint/lib/checks/__init__.py b/tools/lint/lib/checks/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/lint/lib/checks/__init__.py
diff --git a/tools/lint/lib/checks/frontmatter.py b/tools/lint/lib/checks/frontmatter.py
new file mode 100644
index 000000000..a528f746a
--- /dev/null
+++ b/tools/lint/lib/checks/frontmatter.py
@@ -0,0 +1,42 @@
+from ..check import Check
+
+_REQUIRED_FIELDS = set(['description'])
+_OPTIONAL_FIELDS = set([
+ 'author', 'es5id', 'es6id', 'esid', 'features', 'flags', 'includes',
+ 'info', 'negative', 'timeout'
+])
+_VALID_FIELDS = _REQUIRED_FIELDS | _OPTIONAL_FIELDS
+
+class CheckFrontmatter(Check):
+ '''Ensure tests have the expected YAML-formatted metadata.'''
+ ID = 'FRONTMATTER'
+
+ def run(self, name, meta, source):
+ if name.endswith('_FIXTURE.js'):
+ if meta is not None:
+ return '"Fixture" files cannot specify metadata'
+ return
+
+ if meta is None:
+ return 'No valid YAML-formatted frontmatter'
+
+ fields = set(meta.keys())
+
+ missing = _REQUIRED_FIELDS - fields
+ if len(missing) > 0:
+ return 'Required fields missing: %s' % ', '.join(list(missing))
+
+ unrecognized = fields - _VALID_FIELDS
+ if len(unrecognized) > 0:
+ return 'Unrecognized fields: %s' % ', '.join(list(unrecognized))
+
+ if 'negative' in meta:
+ negative = meta['negative']
+ if not isinstance(negative, dict):
+ return '"negative" must be a dictionary with fields "type" and "phase"'
+
+ if not 'type' in negative:
+ return '"negative" must specify a "type" field'
+
+ if not 'phase' in negative:
+ return '"negative" must specify a "phase" field'
diff --git a/tools/lint/lib/checks/license.py b/tools/lint/lib/checks/license.py
new file mode 100644
index 000000000..364393963
--- /dev/null
+++ b/tools/lint/lib/checks/license.py
@@ -0,0 +1,43 @@
+import re
+
+from ..check import Check
+
+_MIN_YEAR = 2009
+_MAX_YEAR = 2030
+
+_LICENSE_PATTERN = re.compile(
+ r'\/\/ Copyright( \([cC]\))? (\w+) .+\. {1,2}All rights reserved\.[\r\n]{1,2}' +
+ r'(' +
+ r'\/\/ (' +
+ r'This code is governed by the( BSD)? license found in the LICENSE file\.' +
+ r'|' +
+ r'See LICENSE for details' +
+ r')' +
+ r'|' +
+ r'\/\/ Use of this source code is governed by a BSD-style license that can be[\r\n]{1,2}' +
+ r'\/\/ found in the LICENSE file\.' +
+ r'|' +
+ r'\/\/ See LICENSE or https://github\.com/tc39/test262/blob/master/LICENSE' +
+ r')', re.IGNORECASE)
+
+class CheckLicense(Check):
+ '''Ensure tests declare valid license information.'''
+ ID = 'LICENSE'
+
+ def run(self, name, meta, source):
+ if meta and 'flags' in meta and 'generated' in meta['flags']:
+ return
+
+ match = _LICENSE_PATTERN.search(source)
+
+ if not match:
+ return 'No license information found.'
+
+ year_str = match.group(2)
+ try:
+ year = int(year_str)
+
+ if year < _MIN_YEAR or year > _MAX_YEAR:
+ raise ValueError()
+ except ValueError:
+ return 'Invalid year: %s' % year_str
diff --git a/tools/lint/lib/collect_files.py b/tools/lint/lib/collect_files.py
new file mode 100644
index 000000000..e94b2880f
--- /dev/null
+++ b/tools/lint/lib/collect_files.py
@@ -0,0 +1,20 @@
+import os
+
+def collect_files(path):
+ '''Given a path to a file, yield that path. Given a path to a directory,
+ yield the path of all files within that directory recursively, omitting any
+ that begin with a period (.) character.'''
+
+ if os.path.isfile(path):
+ yield path
+ return
+
+ if not os.path.isdir(path):
+ raise ValueError('Not found: "%s"' % path)
+
+ for root, dirs, file_names in os.walk(path):
+ for file_name in file_names:
+ if file_name.startswith('.'):
+ continue
+
+ yield os.path.join(root, file_name)
diff --git a/tools/lint/lib/eprint.py b/tools/lint/lib/eprint.py
new file mode 100644
index 000000000..d38678e23
--- /dev/null
+++ b/tools/lint/lib/eprint.py
@@ -0,0 +1,5 @@
+from __future__ import print_function
+import sys
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
diff --git a/tools/lint/lib/frontmatter.py b/tools/lint/lib/frontmatter.py
new file mode 100644
index 000000000..34edd1eac
--- /dev/null
+++ b/tools/lint/lib/frontmatter.py
@@ -0,0 +1,16 @@
+import re
+import yaml
+
+def parse(src):
+ '''Parse the YAML-formatted metadata found in a given string of source
+ code. Tolerate missing or invalid metadata; those conditions are handled by
+ a dedicated "Check" instance.'''
+
+ match = re.search(r'/\*---(.*)---\*/', src, re.DOTALL)
+ if not match:
+ return None
+
+ try:
+ return yaml.load(match.group(1))
+ except (yaml.scanner.ScannerError, yaml.parser.ParserError):
+ return None
diff --git a/tools/lint/lib/whitelist.py b/tools/lint/lib/whitelist.py
new file mode 100644
index 000000000..fc8b649ac
--- /dev/null
+++ b/tools/lint/lib/whitelist.py
@@ -0,0 +1,24 @@
+def parse(handle):
+ '''Parse the contents of the provided file descriptor as a linting
+ whitelist file. Return a dictionary whose keys are test file names and
+ whose values are Python sets of "Check" ID strings.'''
+
+ whitelist = dict()
+
+ for line in handle:
+ if line.startswith('#'):
+ continue
+
+ parts = line.split()
+ file_name = parts[0]
+ check_names = set(parts[1:])
+
+ assert file_name not in whitelist, (
+ 'Whitelist should have a single entry for each file')
+
+ assert len(check_names) > 0, (
+ 'Each whitelist entry should specify at least on check')
+
+ whitelist[file_name] = check_names
+
+ return whitelist
diff --git a/tools/lint/lint.py b/tools/lint/lint.py
new file mode 100755
index 000000000..b26569516
--- /dev/null
+++ b/tools/lint/lint.py
@@ -0,0 +1,74 @@
+#!/usr/bin/env python
+# Copyright (C) 2017 Mike Pennisi. All rights reserved.
+# This code is governed by the BSD license found in the LICENSE file.
+
+import argparse
+import sys
+
+from lib.collect_files import collect_files
+from lib.checks.frontmatter import CheckFrontmatter
+from lib.checks.license import CheckLicense
+from lib.eprint import eprint
+import lib.frontmatter
+import lib.whitelist
+
+parser = argparse.ArgumentParser(description='Test262 linting tool')
+parser.add_argument('--whitelist',
+ type=argparse.FileType('r'),
+ help='file containing expected linting errors')
+parser.add_argument('path',
+ nargs='+',
+ help='file name or directory of files to lint')
+
+checks = [CheckFrontmatter(), CheckLicense()]
+
+def lint(file_names):
+ errors = dict()
+
+ for file_name in file_names:
+ with open(file_name, 'r') as f:
+ content = f.read()
+ meta = lib.frontmatter.parse(content)
+ for check in checks:
+ error = check.run(file_name, meta, content)
+
+ if error is not None:
+ if file_name not in errors:
+ errors[file_name] = dict()
+ errors[file_name][check.ID] = error
+
+ return errors
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+ if args.whitelist:
+ whitelist = lib.whitelist.parse(args.whitelist)
+ else:
+ whitelist = dict()
+
+ files = [path for _path in args.path for path in collect_files(_path)]
+ file_count = len(files)
+ print 'Linting %s file%s.' % (file_count, 's' if file_count != 1 else '')
+
+ all_errors = lint(files)
+ unexpected_errors = dict(all_errors)
+
+ for file_name, failures in all_errors.iteritems():
+ if file_name not in whitelist:
+ continue
+ if set(failures.keys()) == whitelist[file_name]:
+ del unexpected_errors[file_name]
+
+ error_count = len(unexpected_errors)
+ s = 's' if error_count != 1 else ''
+
+ print 'Linting complete. %s error%s found.' % (error_count, s)
+
+ if error_count == 0:
+ sys.exit(0)
+
+ for file_name, failures in unexpected_errors.iteritems():
+ for ID, message in failures.iteritems():
+ eprint('%s: %s - %s' % (file_name, ID, message))
+
+ sys.exit(1)
diff --git a/tools/lint/requirements.txt b/tools/lint/requirements.txt
new file mode 100644
index 000000000..efb082d8d
--- /dev/null
+++ b/tools/lint/requirements.txt
@@ -0,0 +1 @@
+PyYAML==3.11
diff --git a/tools/lint/test/fixtures/frontmatter_invalid_yaml.js b/tools/lint/test/fixtures/frontmatter_invalid_yaml.js
new file mode 100644
index 000000000..9209b8247
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_invalid_yaml.js
@@ -0,0 +1,15 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+es6id: 12.14.1
+description: Applied to a "covered" YieldExpression
+info: This is some information
+features: [generators
+---*/
+
+function* g() {
+ yield 23;
+}
diff --git a/tools/lint/test/fixtures/frontmatter_missing_desc.js b/tools/lint/test/fixtures/frontmatter_missing_desc.js
new file mode 100644
index 000000000..03f705e0a
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_missing_desc.js
@@ -0,0 +1,13 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+es6id: 12.14.1
+info: This is some information
+---*/
+
+function* g() {
+ yield 23;
+}
diff --git a/tools/lint/test/fixtures/frontmatter_module_FIXTURE.js b/tools/lint/test/fixtures/frontmatter_module_FIXTURE.js
new file mode 100644
index 000000000..6c6a408e6
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_module_FIXTURE.js
@@ -0,0 +1,7 @@
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+function* g() {
+ yield 23;
+}
diff --git a/tools/lint/test/fixtures/frontmatter_module_with_meta_FIXTURE.js b/tools/lint/test/fixtures/frontmatter_module_with_meta_FIXTURE.js
new file mode 100644
index 000000000..35fbf4d1a
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_module_with_meta_FIXTURE.js
@@ -0,0 +1,15 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+es6id: 12.14.1
+description: Applied to a "covered" YieldExpression
+info: This is some information
+features: [generators]
+---*/
+
+function* g() {
+ yield 23;
+}
diff --git a/tools/lint/test/fixtures/frontmatter_negative_missing_phase.js b/tools/lint/test/fixtures/frontmatter_negative_missing_phase.js
new file mode 100644
index 000000000..8315882ca
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_negative_missing_phase.js
@@ -0,0 +1,12 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. 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
+negative:
+ type: SyntaxError
+---*/
+
+!!!
diff --git a/tools/lint/test/fixtures/frontmatter_negative_missing_type.js b/tools/lint/test/fixtures/frontmatter_negative_missing_type.js
new file mode 100644
index 000000000..6e3ec6551
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_negative_missing_type.js
@@ -0,0 +1,12 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. 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
+negative:
+ phase: early
+---*/
+
+!!!
diff --git a/tools/lint/test/fixtures/frontmatter_negative_string.js b/tools/lint/test/fixtures/frontmatter_negative_string.js
new file mode 100644
index 000000000..b11d19a3e
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_negative_string.js
@@ -0,0 +1,11 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. 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
+negative: SyntaxError
+---*/
+
+!!!
diff --git a/tools/lint/test/fixtures/frontmatter_negative_valid.js b/tools/lint/test/fixtures/frontmatter_negative_valid.js
new file mode 100644
index 000000000..f1b5cc05d
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_negative_valid.js
@@ -0,0 +1,12 @@
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. 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
+negative:
+ type: SyntaxError
+ phase: early
+---*/
+
+!!!
diff --git a/tools/lint/test/fixtures/frontmatter_omitted.js b/tools/lint/test/fixtures/frontmatter_omitted.js
new file mode 100644
index 000000000..7b3b31c64
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_omitted.js
@@ -0,0 +1,8 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+function* g() {
+ yield 23;
+}
diff --git a/tools/lint/test/fixtures/frontmatter_unrecognized.js b/tools/lint/test/fixtures/frontmatter_unrecognized.js
new file mode 100644
index 000000000..48b0c857d
--- /dev/null
+++ b/tools/lint/test/fixtures/frontmatter_unrecognized.js
@@ -0,0 +1,15 @@
+FRONTMATTER
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+es6id: 12.14.1
+description: Applied to a "covered" YieldExpression
+info: This is some information
+unrecognized_attr: foo
+---*/
+
+function* g() {
+ yield 23;
+}
diff --git a/tools/lint/test/fixtures/license_alternate_1.js b/tools/lint/test/fixtures/license_alternate_1.js
new file mode 100644
index 000000000..8283f80e6
--- /dev/null
+++ b/tools/lint/test/fixtures/license_alternate_1.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// Copyright (c) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the license found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_alternate_2.js b/tools/lint/test/fixtures/license_alternate_2.js
new file mode 100644
index 000000000..345f2c83c
--- /dev/null
+++ b/tools/lint/test/fixtures/license_alternate_2.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// Copyright 2017 Mike Pennisi. All rights reserved.
+// See LICENSE for details.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_alternate_3.js b/tools/lint/test/fixtures/license_alternate_3.js
new file mode 100644
index 000000000..c66cd3e66
--- /dev/null
+++ b/tools/lint/test/fixtures/license_alternate_3.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// copyright (c) 2017 mike pennisi. 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
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_alternate_4,js b/tools/lint/test/fixtures/license_alternate_4,js
new file mode 100644
index 000000000..684f0b197
--- /dev/null
+++ b/tools/lint/test/fixtures/license_alternate_4,js
@@ -0,0 +1,10 @@
+^ expected errors | v input
+// Copyright (c) 2017 Mike Pennisi. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_alternate_5.js b/tools/lint/test/fixtures/license_alternate_5.js
new file mode 100644
index 000000000..27605c320
--- /dev/null
+++ b/tools/lint/test/fixtures/license_alternate_5.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// See LICENSE or https://github.com/tc39/test262/blob/master/LICENSE
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Minimal test
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_generated.js b/tools/lint/test/fixtures/license_generated.js
new file mode 100644
index 000000000..67aaf0126
--- /dev/null
+++ b/tools/lint/test/fixtures/license_generated.js
@@ -0,0 +1,11 @@
+^ expected errors | v input
+// This file was procedurally generated from the following sources:
+// - foo
+// - bar
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Applied to a "covered" YieldExpression
+flags: [class, generated]
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_invalid_year.js b/tools/lint/test/fixtures/license_invalid_year.js
new file mode 100644
index 000000000..abe53b869
--- /dev/null
+++ b/tools/lint/test/fixtures/license_invalid_year.js
@@ -0,0 +1,10 @@
+LICENSE
+^ expected errors | v input
+// Copyright (C) 2199 Mike Pennisi. 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: Applied to a "covered" YieldExpression
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/license_missing.js b/tools/lint/test/fixtures/license_missing.js
new file mode 100644
index 000000000..3d90cf27f
--- /dev/null
+++ b/tools/lint/test/fixtures/license_missing.js
@@ -0,0 +1,8 @@
+LICENSE
+^ expected errors | v input
+/*---
+esid: sec-assignment-operators-static-semantics-early-errors
+description: Applied to a "covered" YieldExpression
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/valid_es5id.js b/tools/lint/test/fixtures/valid_es5id.js
new file mode 100644
index 000000000..2ca0a4d8d
--- /dev/null
+++ b/tools/lint/test/fixtures/valid_es5id.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+es5id: 12.14.1
+description: Minimal test
+---*/
+
+function f() {}
diff --git a/tools/lint/test/fixtures/valid_es6id.js b/tools/lint/test/fixtures/valid_es6id.js
new file mode 100644
index 000000000..4d71e162f
--- /dev/null
+++ b/tools/lint/test/fixtures/valid_es6id.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+es6id: 12.14.1
+description: Minimal test
+---*/
+
+function f() {}
diff --git a/tools/lint/test/fixtures/valid_esid.js b/tools/lint/test/fixtures/valid_esid.js
new file mode 100644
index 000000000..6b3b35c92
--- /dev/null
+++ b/tools/lint/test/fixtures/valid_esid.js
@@ -0,0 +1,9 @@
+^ expected errors | v input
+// Copyright (C) 2017 Mike Pennisi. 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
+---*/
+
+function f() {}
diff --git a/tools/lint/test/run.py b/tools/lint/test/run.py
new file mode 100755
index 000000000..9c83c7945
--- /dev/null
+++ b/tools/lint/test/run.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+# Copyright (C) 2017 Mike Pennisi. All rights reserved.
+# This code is governed by the BSD license found in the LICENSE file.
+
+import shutil, subprocess, sys, os, unittest, tempfile
+
+testDir = os.path.dirname(os.path.relpath(__file__))
+OUT_DIR = os.path.join(testDir, 'out')
+ex = os.path.join(testDir, '..', 'lint.py')
+
+class TestLinter(unittest.TestCase):
+ maxDiff = None
+
+ def fixture(self, name, content):
+ fspath = os.path.join(OUT_DIR, name)
+ with open(fspath, 'w') as f:
+ f.write(content)
+ return fspath
+
+ def lint(self, args):
+ args[:0] = [ex]
+ sp = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = sp.communicate()
+ return dict(stdout=stdout, stderr=stderr, returncode=sp.returncode)
+
+ def setUp(self):
+ os.mkdir(OUT_DIR)
+
+ def tearDown(self):
+ shutil.rmtree(OUT_DIR, ignore_errors=True)
+
+ def test_no_file(self):
+ result = self.lint(['non-existent-file.js'])
+ self.assertNotEqual(result["returncode"], 0)
+
+ def test_whitelist_single(self):
+ test_content = ('// Copyright (C) 2017 Mike Pennisi. All rights reserved.\n' +
+ '// This code is governed by the BSD license found in the LICENSE file.')
+ test_file = self.fixture('input.js', test_content)
+ whitelist_content = test_file + ' FRONTMATTER'
+ whitelist_file = self.fixture('lint.whitelist', whitelist_content)
+
+ result = self.lint([test_file])
+
+ self.assertNotEqual(result['returncode'], 0)
+
+ result = self.lint(['--whitelist', whitelist_file, test_file])
+
+ self.assertEqual(result['returncode'], 0)
+
+ def test_whitelist_comment(self):
+ test_content = ('// Copyright (C) 2017 Mike Pennisi. All rights reserved.\n' +
+ '// This code is governed by the BSD license found in the LICENSE file.')
+ test_file = self.fixture('input.js', test_content)
+ whitelist_content = ('# One comment\n' +
+ '# Another comment\n' +
+ test_file + ' FRONTMATTER')
+ whitelist_file = self.fixture('lint.whitelist', whitelist_content)
+
+ result = self.lint([test_file])
+
+ self.assertNotEqual(result['returncode'], 0)
+
+ result = self.lint(['--whitelist', whitelist_file, test_file])
+
+ self.assertEqual(result['returncode'], 0)
+
+def create_file_test(name, fspath):
+ '''Dynamically generate a function that may be used as a test method with
+ the Python `unittest` module.'''
+
+ def test(self):
+ with open(fspath, 'r') as f:
+ contents = f.read()
+ expected, input = contents.split('^ expected errors | v input\n')
+ expected = expected.split()
+ tmp_file = self.fixture(name, input)
+ result = self.lint([tmp_file])
+ if len(expected) == 0:
+ self.assertEqual(result['returncode'], 0)
+ self.assertEqual(result['stderr'], '')
+ else:
+ self.assertNotEqual(result['returncode'], 0)
+ for err in expected:
+ self.assertIn(err, result['stderr'])
+
+ return test
+
+dirname = os.path.join(os.path.abspath(testDir), 'fixtures')
+for file_name in os.listdir(dirname):
+ full_path = os.path.join(dirname, file_name)
+ if not os.path.isfile(full_path) or file_name.startswith('.'):
+ continue
+
+ t = create_file_test(file_name, full_path)
+ t.__name__ = 'test_' + file_name
+ setattr(TestLinter, t.__name__, t)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/scripts/ci.sh b/tools/scripts/ci_build.sh
index 478880c2a..478880c2a 100644..100755
--- a/tools/scripts/ci.sh
+++ b/tools/scripts/ci_build.sh
diff --git a/tools/scripts/ci_lint.sh b/tools/scripts/ci_lint.sh
new file mode 100755
index 000000000..75b3fb6a0
--- /dev/null
+++ b/tools/scripts/ci_lint.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
+ paths=$(git diff --diff-filter ACMR --name-only $TRAVIS_BRANCH -- test/)
+
+ if [ "$paths" == "" ]; then
+ echo No test files added or modified. Exiting.
+ exit 0
+ fi
+
+ echo New or modified test files:
+ echo "$paths"
+
+else
+ paths="test/"
+fi
+
+./tools/lint/lint.py --whitelist lint.whitelist $paths
diff --git a/tools/scripts/deploy.sh b/tools/scripts/deploy.sh
index 44c6f968b..44c6f968b 100644..100755
--- a/tools/scripts/deploy.sh
+++ b/tools/scripts/deploy.sh