From f38748efc161e5b5d03306baaa9fe2bb01b42ac7 Mon Sep 17 00:00:00 2001
From: Mike Pennisi <mike@mikepennisi.com>
Date: Wed, 25 Sep 2019 12:01:27 -0400
Subject: [PATCH 1/3] lint: Increase isolation between tests and project

Prior to this commit, the tests for the linter partially depended on
project files. This coupling risks churn in the tests: if the needs of
the project change (e.g. a harness file is modified), then the linter
tests would need to be updated. It also made the tests more difficult to
understand because the input was both larger and more complicated than
necessary to exercise the relevant functionality.

Execute the tests in the context of the fixture directory and introduce
minimal support files that serve the same purpose as the corresponding
project files.
---
 tools/lint/test/fixtures/features.txt         |   5 +
 .../fixtures/harness/detachArrayBuffer.js     |  16 +
 tools/lint/test/fixtures/harness/features.yml |   2 +
 .../test/fixtures/harness/propertyHelper.js   | 229 +++++++++
 .../test/fixtures/harness/testTypedArray.js   |  89 ++++
 .../test/fixtures/harness/typeCoercion.js     | 451 ++++++++++++++++++
 tools/lint/test/run.py                        |  11 +-
 7 files changed, 800 insertions(+), 3 deletions(-)
 create mode 100644 tools/lint/test/fixtures/features.txt
 create mode 100644 tools/lint/test/fixtures/harness/detachArrayBuffer.js
 create mode 100644 tools/lint/test/fixtures/harness/features.yml
 create mode 100644 tools/lint/test/fixtures/harness/propertyHelper.js
 create mode 100644 tools/lint/test/fixtures/harness/testTypedArray.js
 create mode 100644 tools/lint/test/fixtures/harness/typeCoercion.js

diff --git a/tools/lint/test/fixtures/features.txt b/tools/lint/test/fixtures/features.txt
new file mode 100644
index 00000000000..a7d911ea606
--- /dev/null
+++ b/tools/lint/test/fixtures/features.txt
@@ -0,0 +1,5 @@
+async-functions
+object-spread
+generators
+BigInt
+TypedArray
diff --git a/tools/lint/test/fixtures/harness/detachArrayBuffer.js b/tools/lint/test/fixtures/harness/detachArrayBuffer.js
new file mode 100644
index 00000000000..81af99f3b04
--- /dev/null
+++ b/tools/lint/test/fixtures/harness/detachArrayBuffer.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 the V8 project authors.  All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+    A function used in the process of asserting correctness of TypedArray objects.
+
+    $262.detachArrayBuffer is defined by a host.
+defines: [$DETACHBUFFER]
+---*/
+
+function $DETACHBUFFER(buffer) {
+  if (!$262 || typeof $262.detachArrayBuffer !== "function") {
+    throw new Test262Error("No method available to detach an ArrayBuffer");
+  }
+  $262.detachArrayBuffer(buffer);
+}
diff --git a/tools/lint/test/fixtures/harness/features.yml b/tools/lint/test/fixtures/harness/features.yml
new file mode 100644
index 00000000000..04af707a4d8
--- /dev/null
+++ b/tools/lint/test/fixtures/harness/features.yml
@@ -0,0 +1,2 @@
+typeCoercion.js: [Symbol.toPrimitive, BigInt]
+testTypedArray.js: [TypedArray]
diff --git a/tools/lint/test/fixtures/harness/propertyHelper.js b/tools/lint/test/fixtures/harness/propertyHelper.js
new file mode 100644
index 00000000000..4a4081fcf40
--- /dev/null
+++ b/tools/lint/test/fixtures/harness/propertyHelper.js
@@ -0,0 +1,229 @@
+// Copyright (C) 2017 Ecma International.  All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+    Collection of functions used to safely verify the correctness of
+    property descriptors.
+defines:
+  - verifyProperty
+  - verifyEqualTo
+  - verifyWritable
+  - verifyNotWritable
+  - verifyEnumerable
+  - verifyNotEnumerable
+  - verifyConfigurable
+  - verifyNotConfigurable
+---*/
+
+// @ts-check
+
+/**
+ * @param {object} obj
+ * @param {string|symbol} name
+ * @param {PropertyDescriptor|undefined} desc
+ * @param {object} [options]
+ * @param {boolean} [options.restore]
+ */
+function verifyProperty(obj, name, desc, options) {
+  assert(
+    arguments.length > 2,
+    'verifyProperty should receive at least 3 arguments: obj, name, and descriptor'
+  );
+
+  var originalDesc = Object.getOwnPropertyDescriptor(obj, name);
+  var nameStr = String(name);
+
+  // Allows checking for undefined descriptor if it's explicitly given.
+  if (desc === undefined) {
+    assert.sameValue(
+      originalDesc,
+      undefined,
+      "obj['" + nameStr + "'] descriptor should be undefined"
+    );
+
+    // desc and originalDesc are both undefined, problem solved;
+    return true;
+  }
+
+  assert(
+    Object.prototype.hasOwnProperty.call(obj, name),
+    "obj should have an own property " + nameStr
+  );
+
+  assert.notSameValue(
+    desc,
+    null,
+    "The desc argument should be an object or undefined, null"
+  );
+
+  assert.sameValue(
+    typeof desc,
+    "object",
+    "The desc argument should be an object or undefined, " + String(desc)
+  );
+
+  var failures = [];
+
+  if (Object.prototype.hasOwnProperty.call(desc, 'value')) {
+    if (!isSameValue(desc.value, originalDesc.value)) {
+      failures.push("descriptor value should be " + desc.value);
+    }
+  }
+
+  if (Object.prototype.hasOwnProperty.call(desc, 'enumerable')) {
+    if (desc.enumerable !== originalDesc.enumerable ||
+        desc.enumerable !== isEnumerable(obj, name)) {
+      failures.push('descriptor should ' + (desc.enumerable ? '' : 'not ') + 'be enumerable');
+    }
+  }
+
+  if (Object.prototype.hasOwnProperty.call(desc, 'writable')) {
+    if (desc.writable !== originalDesc.writable ||
+        desc.writable !== isWritable(obj, name)) {
+      failures.push('descriptor should ' + (desc.writable ? '' : 'not ') + 'be writable');
+    }
+  }
+
+  if (Object.prototype.hasOwnProperty.call(desc, 'configurable')) {
+    if (desc.configurable !== originalDesc.configurable ||
+        desc.configurable !== isConfigurable(obj, name)) {
+      failures.push('descriptor should ' + (desc.configurable ? '' : 'not ') + 'be configurable');
+    }
+  }
+
+  assert(!failures.length, failures.join('; '));
+
+  if (options && options.restore) {
+    Object.defineProperty(obj, name, originalDesc);
+  }
+
+  return true;
+}
+
+function isConfigurable(obj, name) {
+  var hasOwnProperty = Object.prototype.hasOwnProperty;
+  try {
+    delete obj[name];
+  } catch (e) {
+    if (!(e instanceof TypeError)) {
+      $ERROR("Expected TypeError, got " + e);
+    }
+  }
+  return !hasOwnProperty.call(obj, name);
+}
+
+function isEnumerable(obj, name) {
+  var stringCheck = false;
+
+  if (typeof name === "string") {
+    for (var x in obj) {
+      if (x === name) {
+        stringCheck = true;
+        break;
+      }
+    }
+  } else {
+    // skip it if name is not string, works for Symbol names.
+    stringCheck = true;
+  }
+
+  return stringCheck &&
+    Object.prototype.hasOwnProperty.call(obj, name) &&
+    Object.prototype.propertyIsEnumerable.call(obj, name);
+}
+
+function isSameValue(a, b) {
+  if (a === 0 && b === 0) return 1 / a === 1 / b;
+  if (a !== a && b !== b) return true;
+
+  return a === b;
+}
+
+function isWritable(obj, name, verifyProp, value) {
+  var newValue = value || "unlikelyValue";
+  var hadValue = Object.prototype.hasOwnProperty.call(obj, name);
+  var oldValue = obj[name];
+  var writeSucceeded;
+
+  try {
+    obj[name] = newValue;
+  } catch (e) {
+    if (!(e instanceof TypeError)) {
+      $ERROR("Expected TypeError, got " + e);
+    }
+  }
+
+  writeSucceeded = isSameValue(obj[verifyProp || name], newValue);
+
+  // Revert the change only if it was successful (in other cases, reverting
+  // is unnecessary and may trigger exceptions for certain property
+  // configurations)
+  if (writeSucceeded) {
+    if (hadValue) {
+      obj[name] = oldValue;
+    } else {
+      delete obj[name];
+    }
+  }
+
+  return writeSucceeded;
+}
+
+function verifyEqualTo(obj, name, value) {
+  if (!isSameValue(obj[name], value)) {
+    $ERROR("Expected obj[" + String(name) + "] to equal " + value +
+           ", actually " + obj[name]);
+  }
+}
+
+function verifyWritable(obj, name, verifyProp, value) {
+  if (!verifyProp) {
+    assert(Object.getOwnPropertyDescriptor(obj, name).writable,
+         "Expected obj[" + String(name) + "] to have writable:true.");
+  }
+  if (!isWritable(obj, name, verifyProp, value)) {
+    $ERROR("Expected obj[" + String(name) + "] to be writable, but was not.");
+  }
+}
+
+function verifyNotWritable(obj, name, verifyProp, value) {
+  if (!verifyProp) {
+    assert(!Object.getOwnPropertyDescriptor(obj, name).writable,
+         "Expected obj[" + String(name) + "] to have writable:false.");
+  }
+  if (isWritable(obj, name, verifyProp)) {
+    $ERROR("Expected obj[" + String(name) + "] NOT to be writable, but was.");
+  }
+}
+
+function verifyEnumerable(obj, name) {
+  assert(Object.getOwnPropertyDescriptor(obj, name).enumerable,
+       "Expected obj[" + String(name) + "] to have enumerable:true.");
+  if (!isEnumerable(obj, name)) {
+    $ERROR("Expected obj[" + String(name) + "] to be enumerable, but was not.");
+  }
+}
+
+function verifyNotEnumerable(obj, name) {
+  assert(!Object.getOwnPropertyDescriptor(obj, name).enumerable,
+       "Expected obj[" + String(name) + "] to have enumerable:false.");
+  if (isEnumerable(obj, name)) {
+    $ERROR("Expected obj[" + String(name) + "] NOT to be enumerable, but was.");
+  }
+}
+
+function verifyConfigurable(obj, name) {
+  assert(Object.getOwnPropertyDescriptor(obj, name).configurable,
+       "Expected obj[" + String(name) + "] to have configurable:true.");
+  if (!isConfigurable(obj, name)) {
+    $ERROR("Expected obj[" + String(name) + "] to be configurable, but was not.");
+  }
+}
+
+function verifyNotConfigurable(obj, name) {
+  assert(!Object.getOwnPropertyDescriptor(obj, name).configurable,
+       "Expected obj[" + String(name) + "] to have configurable:false.");
+  if (isConfigurable(obj, name)) {
+    $ERROR("Expected obj[" + String(name) + "] NOT to be configurable, but was.");
+  }
+}
diff --git a/tools/lint/test/fixtures/harness/testTypedArray.js b/tools/lint/test/fixtures/harness/testTypedArray.js
new file mode 100644
index 00000000000..13b23878358
--- /dev/null
+++ b/tools/lint/test/fixtures/harness/testTypedArray.js
@@ -0,0 +1,89 @@
+// Copyright (C) 2015 André Bargull. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+    Collection of functions used to assert the correctness of TypedArray objects.
+defines:
+  - typedArrayConstructors
+  - floatArrayConstructors
+  - intArrayConstructors
+  - TypedArray
+  - testWithTypedArrayConstructors
+  - testTypedArrayConversions
+---*/
+
+/**
+ * Array containing every typed array constructor.
+ */
+var typedArrayConstructors = [
+  Float64Array,
+  Float32Array,
+  Int32Array,
+  Int16Array,
+  Int8Array,
+  Uint32Array,
+  Uint16Array,
+  Uint8Array,
+  Uint8ClampedArray
+];
+
+var floatArrayConstructors = typedArrayConstructors.slice(0, 2);
+var intArrayConstructors = typedArrayConstructors.slice(2, 7);
+
+/**
+ * The %TypedArray% intrinsic constructor function.
+ */
+var TypedArray = Object.getPrototypeOf(Int8Array);
+
+/**
+ * Callback for testing a typed array constructor.
+ *
+ * @callback typedArrayConstructorCallback
+ * @param {Function} Constructor the constructor object to test with.
+ */
+
+/**
+ * Calls the provided function for every typed array constructor.
+ *
+ * @param {typedArrayConstructorCallback} f - the function to call for each typed array constructor.
+ * @param {Array} selected - An optional Array with filtered typed arrays
+ */
+function testWithTypedArrayConstructors(f, selected) {
+  var constructors = selected || typedArrayConstructors;
+  for (var i = 0; i < constructors.length; ++i) {
+    var constructor = constructors[i];
+    try {
+      f(constructor);
+    } catch (e) {
+      e.message += " (Testing with " + constructor.name + ".)";
+      throw e;
+    }
+  }
+}
+
+/**
+ * Helper for conversion operations on TypedArrays, the expected values
+ * properties are indexed in order to match the respective value for each
+ * TypedArray constructor
+ * @param  {Function} fn - the function to call for each constructor and value.
+ *                         will be called with the constructor, value, expected
+ *                         value, and a initial value that can be used to avoid
+ *                         a false positive with an equivalent expected value.
+ */
+function testTypedArrayConversions(byteConversionValues, fn) {
+  var values = byteConversionValues.values;
+  var expected = byteConversionValues.expected;
+
+  testWithTypedArrayConstructors(function(TA) {
+    var name = TA.name.slice(0, -5);
+
+    return values.forEach(function(value, index) {
+      var exp = expected[name][index];
+      var initial = 0;
+      if (exp === 0) {
+        initial = 1;
+      }
+      fn(TA, value, exp, initial);
+    });
+  });
+}
diff --git a/tools/lint/test/fixtures/harness/typeCoercion.js b/tools/lint/test/fixtures/harness/typeCoercion.js
new file mode 100644
index 00000000000..dc1089c3c71
--- /dev/null
+++ b/tools/lint/test/fixtures/harness/typeCoercion.js
@@ -0,0 +1,451 @@
+// Copyright (C) 2017 Josh Wolfe. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+    Functions to help generate test cases for testing type coercion abstract
+    operations like ToNumber.
+defines:
+  - testCoercibleToIndexZero
+  - testCoercibleToIndexOne
+  - testCoercibleToIndexFromIndex
+  - testCoercibleToIntegerZero
+  - testCoercibleToIntegerOne
+  - testCoercibleToNumberZero
+  - testCoercibleToNumberNan
+  - testCoercibleToNumberOne
+  - testCoercibleToIntegerFromInteger
+  - testPrimitiveWrappers
+  - testCoercibleToPrimitiveWithMethod
+  - testNotCoercibleToIndex
+  - testNotCoercibleToInteger
+  - testNotCoercibleToNumber
+  - testNotCoercibleToPrimitive
+  - testCoercibleToString
+  - testNotCoercibleToString
+  - testCoercibleToBooleanTrue
+  - testCoercibleToBooleanFalse
+  - testCoercibleToBigIntZero
+  - testCoercibleToBigIntOne
+  - testCoercibleToBigIntFromBigInt
+  - testNotCoercibleToBigInt
+---*/
+
+function testCoercibleToIndexZero(test) {
+  testCoercibleToIntegerZero(test);
+}
+
+function testCoercibleToIndexOne(test) {
+  testCoercibleToIntegerOne(test);
+}
+
+function testCoercibleToIndexFromIndex(nominalIndex, test) {
+  assert(Number.isInteger(nominalIndex));
+  assert(0 <= nominalIndex && nominalIndex <= 2**53 - 1);
+  testCoercibleToIntegerFromInteger(nominalIndex, test);
+}
+
+function testCoercibleToIntegerZero(test) {
+  testCoercibleToNumberZero(test);
+
+  testCoercibleToIntegerFromInteger(0, test);
+
+  // NaN -> +0
+  testCoercibleToNumberNan(test);
+
+  // When toString() returns a string that parses to NaN:
+  test({});
+  test([]);
+}
+
+function testCoercibleToIntegerOne(test) {
+  testCoercibleToNumberOne(test);
+
+  testCoercibleToIntegerFromInteger(1, test);
+
+  // When toString() returns "1"
+  test([1]);
+  test(["1"]);
+}
+
+function testCoercibleToNumberZero(test) {
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+  }
+
+  testPrimitiveValue(null);
+  testPrimitiveValue(false);
+  testPrimitiveValue(0);
+  testPrimitiveValue("0");
+}
+
+function testCoercibleToNumberNan(test) {
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+  }
+
+  testPrimitiveValue(undefined);
+  testPrimitiveValue(NaN);
+  testPrimitiveValue("");
+  testPrimitiveValue("foo");
+  testPrimitiveValue("true");
+}
+
+function testCoercibleToNumberOne(test) {
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+  }
+
+  testPrimitiveValue(true);
+  testPrimitiveValue(1);
+  testPrimitiveValue("1");
+}
+
+function testCoercibleToIntegerFromInteger(nominalInteger, test) {
+  assert(Number.isInteger(nominalInteger));
+
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+
+    // Non-primitive values that coerce to the nominal integer:
+    // toString() returns a string that parsers to a primitive value.
+    test([value]);
+  }
+
+  function testPrimitiveNumber(number) {
+    testPrimitiveValue(number);
+    // ToNumber: String -> Number
+    testPrimitiveValue(number.toString());
+  }
+
+  testPrimitiveNumber(nominalInteger);
+
+  // ToInteger: floor(abs(number))
+  if (nominalInteger >= 0) {
+    testPrimitiveNumber(nominalInteger + 0.9);
+  }
+  if (nominalInteger <= 0) {
+    testPrimitiveNumber(nominalInteger - 0.9);
+  }
+}
+
+function testPrimitiveWrappers(primitiveValue, hint, test) {
+  if (primitiveValue != null) {
+    // null and undefined result in {} rather than a proper wrapper,
+    // so skip this case for those values.
+    test(Object(primitiveValue));
+  }
+
+  testCoercibleToPrimitiveWithMethod(hint, function() {
+    return primitiveValue;
+  }, test);
+}
+
+function testCoercibleToPrimitiveWithMethod(hint, method, test) {
+  var methodNames;
+  if (hint === "number") {
+    methodNames = ["valueOf", "toString"];
+  } else if (hint === "string") {
+    methodNames = ["toString", "valueOf"];
+  } else {
+    throw new Test262Error();
+  }
+  // precedence order
+  test({
+    [Symbol.toPrimitive]: method,
+    [methodNames[0]]: function() { throw new Test262Error(); },
+    [methodNames[1]]: function() { throw new Test262Error(); },
+  });
+  test({
+    [methodNames[0]]: method,
+    [methodNames[1]]: function() { throw new Test262Error(); },
+  });
+  if (hint === "number") {
+    // The default valueOf returns an object, which is unsuitable.
+    // The default toString returns a String, which is suitable.
+    // Therefore this test only works for valueOf falling back to toString.
+    test({
+      // this is toString:
+      [methodNames[1]]: method,
+    });
+  }
+
+  // GetMethod: if func is undefined or null, return undefined.
+  test({
+    [Symbol.toPrimitive]: undefined,
+    [methodNames[0]]: method,
+    [methodNames[1]]: method,
+  });
+  test({
+    [Symbol.toPrimitive]: null,
+    [methodNames[0]]: method,
+    [methodNames[1]]: method,
+  });
+
+  // if methodNames[0] is not callable, fallback to methodNames[1]
+  test({
+    [methodNames[0]]: null,
+    [methodNames[1]]: method,
+  });
+  test({
+    [methodNames[0]]: 1,
+    [methodNames[1]]: method,
+  });
+  test({
+    [methodNames[0]]: {},
+    [methodNames[1]]: method,
+  });
+
+  // if methodNames[0] returns an object, fallback to methodNames[1]
+  test({
+    [methodNames[0]]: function() { return {}; },
+    [methodNames[1]]: method,
+  });
+  test({
+    [methodNames[0]]: function() { return Object(1); },
+    [methodNames[1]]: method,
+  });
+}
+
+function testNotCoercibleToIndex(test) {
+  function testPrimitiveValue(value) {
+    test(RangeError, value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", function(value) {
+      test(RangeError, value);
+    });
+  }
+
+  // Let integerIndex be ? ToInteger(value).
+  testNotCoercibleToInteger(test);
+
+  // If integerIndex < 0, throw a RangeError exception.
+  testPrimitiveValue(-1);
+  testPrimitiveValue(-2.5);
+  testPrimitiveValue("-2.5");
+  testPrimitiveValue(-Infinity);
+
+  // Let index be ! ToLength(integerIndex).
+  // If SameValueZero(integerIndex, index) is false, throw a RangeError exception.
+  testPrimitiveValue(2 ** 53);
+  testPrimitiveValue(Infinity);
+}
+
+function testNotCoercibleToInteger(test) {
+  // ToInteger only throws from ToNumber.
+  testNotCoercibleToNumber(test);
+}
+
+function testNotCoercibleToNumber(test) {
+  function testPrimitiveValue(value) {
+    test(TypeError, value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", function(value) {
+      test(TypeError, value);
+    });
+  }
+
+  // ToNumber: Symbol -> TypeError
+  testPrimitiveValue(Symbol("1"));
+
+  if (typeof BigInt !== "undefined") {
+    // ToNumber: BigInt -> TypeError
+    testPrimitiveValue(BigInt(0));
+  }
+
+  // ToPrimitive
+  testNotCoercibleToPrimitive("number", test);
+}
+
+function testNotCoercibleToPrimitive(hint, test) {
+  function MyError() {}
+
+  // ToPrimitive: input[@@toPrimitive] is not callable (and non-null)
+  test(TypeError, {[Symbol.toPrimitive]: 1});
+  test(TypeError, {[Symbol.toPrimitive]: {}});
+
+  // ToPrimitive: input[@@toPrimitive] returns object
+  test(TypeError, {[Symbol.toPrimitive]: function() { return Object(1); }});
+  test(TypeError, {[Symbol.toPrimitive]: function() { return {}; }});
+
+  // ToPrimitive: input[@@toPrimitive] throws
+  test(MyError, {[Symbol.toPrimitive]: function() { throw new MyError(); }});
+
+  // OrdinaryToPrimitive: method throws
+  testCoercibleToPrimitiveWithMethod(hint, function() {
+    throw new MyError();
+  }, function(value) {
+    test(MyError, value);
+  });
+
+  // OrdinaryToPrimitive: both methods are unsuitable
+  function testUnsuitableMethod(method) {
+    test(TypeError, {valueOf:method, toString:method});
+  }
+  // not callable:
+  testUnsuitableMethod(null);
+  testUnsuitableMethod(1);
+  testUnsuitableMethod({});
+  // returns object:
+  testUnsuitableMethod(function() { return Object(1); });
+  testUnsuitableMethod(function() { return {}; });
+}
+
+function testCoercibleToString(test) {
+  function testPrimitiveValue(value, expectedString) {
+    test(value, expectedString);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "string", function(value) {
+      test(value, expectedString);
+    });
+  }
+
+  testPrimitiveValue(undefined, "undefined");
+  testPrimitiveValue(null, "null");
+  testPrimitiveValue(true, "true");
+  testPrimitiveValue(false, "false");
+  testPrimitiveValue(0, "0");
+  testPrimitiveValue(-0, "0");
+  testPrimitiveValue(Infinity, "Infinity");
+  testPrimitiveValue(-Infinity, "-Infinity");
+  testPrimitiveValue(123.456, "123.456");
+  testPrimitiveValue(-123.456, "-123.456");
+  testPrimitiveValue("", "");
+  testPrimitiveValue("foo", "foo");
+
+  if (typeof BigInt !== "undefined") {
+    // BigInt -> TypeError
+    testPrimitiveValue(BigInt(0), "0");
+  }
+
+  // toString of a few objects
+  test([], "");
+  test(["foo", "bar"], "foo,bar");
+  test({}, "[object Object]");
+}
+
+function testNotCoercibleToString(test) {
+  function testPrimitiveValue(value) {
+    test(TypeError, value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "string", function(value) {
+      test(TypeError, value);
+    });
+  }
+
+  // Symbol -> TypeError
+  testPrimitiveValue(Symbol("1"));
+
+  // ToPrimitive
+  testNotCoercibleToPrimitive("string", test);
+}
+
+function testCoercibleToBooleanTrue(test) {
+  test(true);
+  test(1);
+  test("string");
+  test(Symbol("1"));
+  test({});
+}
+
+function testCoercibleToBooleanFalse(test) {
+  test(undefined);
+  test(null);
+  test(false);
+  test(0);
+  test(-0);
+  test(NaN);
+  test("");
+}
+
+function testCoercibleToBigIntZero(test) {
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+  }
+
+  testCoercibleToBigIntFromBigInt(BigInt(0), test);
+  testPrimitiveValue(-BigInt(0));
+  testPrimitiveValue("-0");
+  testPrimitiveValue(false);
+  testPrimitiveValue("");
+  testPrimitiveValue("   ");
+
+  // toString() returns ""
+  test([]);
+
+  // toString() returns "0"
+  test([0]);
+}
+
+function testCoercibleToBigIntOne(test) {
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+  }
+
+  testCoercibleToBigIntFromBigInt(BigInt(1), test);
+  testPrimitiveValue(true);
+
+  // toString() returns "1"
+  test([1]);
+}
+
+function testCoercibleToBigIntFromBigInt(nominalBigInt, test) {
+  function testPrimitiveValue(value) {
+    test(value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", test);
+  }
+
+  testPrimitiveValue(nominalBigInt);
+  testPrimitiveValue(nominalBigInt.toString());
+  testPrimitiveValue("0b" + nominalBigInt.toString(2));
+  testPrimitiveValue("0o" + nominalBigInt.toString(8));
+  testPrimitiveValue("0x" + nominalBigInt.toString(16));
+  testPrimitiveValue("   " + nominalBigInt.toString() + "   ");
+
+  // toString() returns the decimal string representation
+  test([nominalBigInt]);
+  test([nominalBigInt.toString()]);
+}
+
+function testNotCoercibleToBigInt(test) {
+  function testPrimitiveValue(error, value) {
+    test(error, value);
+    // ToPrimitive
+    testPrimitiveWrappers(value, "number", function(value) {
+      test(error, value);
+    });
+  }
+
+  // Undefined, Null, Number, Symbol -> TypeError
+  testPrimitiveValue(TypeError, undefined);
+  testPrimitiveValue(TypeError, null);
+  testPrimitiveValue(TypeError, 0);
+  testPrimitiveValue(TypeError, NaN);
+  testPrimitiveValue(TypeError, Infinity);
+  testPrimitiveValue(TypeError, Symbol("1"));
+
+  // when a String parses to NaN -> SyntaxError
+  function testStringValue(string) {
+    testPrimitiveValue(SyntaxError, string);
+    testPrimitiveValue(SyntaxError, "   " + string);
+    testPrimitiveValue(SyntaxError, string + "   ");
+    testPrimitiveValue(SyntaxError, "   " + string + "   ");
+  }
+  testStringValue("a");
+  testStringValue("0b2");
+  testStringValue("0o8");
+  testStringValue("0xg");
+  testStringValue("1n");
+}
diff --git a/tools/lint/test/run.py b/tools/lint/test/run.py
index 5d2f3a50123..a81b1fa9d43 100755
--- a/tools/lint/test/run.py
+++ b/tools/lint/test/run.py
@@ -4,7 +4,7 @@
 
 import shutil, subprocess, sys, os, unittest, tempfile
 
-testDir = os.path.dirname(os.path.relpath(__file__))
+testDir = os.path.dirname(os.path.abspath(__file__))
 OUT_DIR = os.path.join(testDir, 'out')
 ex = os.path.join(testDir, '..', 'lint.py')
 
@@ -19,7 +19,11 @@ def fixture(self, name, content):
 
     def lint(self, args):
         args[:0] = [ex]
-        sp = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        sp = subprocess.Popen(args,
+                              stdout=subprocess.PIPE,
+                              stderr=subprocess.PIPE,
+                              cwd=os.path.join(testDir, 'fixtures')
+                              )
         stdout, stderr = sp.communicate()
         return dict(stdout=stdout, stderr=stderr, returncode=sp.returncode)
 
@@ -90,7 +94,8 @@ def test(self):
 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('.'):
+    if (not os.path.isfile(full_path) or file_name.startswith('.') or
+            not file_name.endswith('.js')):
         continue
 
     t = create_file_test(file_name, full_path)

From bb5a6622d456dcb7c87cc8730db310c28863e502 Mon Sep 17 00:00:00 2001
From: Mike Pennisi <mike@mikepennisi.com>
Date: Wed, 25 Sep 2019 13:17:55 -0400
Subject: [PATCH 2/3] lint: Compute ergonomic name for tests

The command-line interface to Python's "unittest" module allows users to
run tests by name, e.g.

    $ test.py TestLinter.test_foo

If the tests include a period, they cannot be referenced in this way.
Rename the tests to omit the filename extension so that they may be
referenced from the command-line in this way.
---
 tools/lint/test/run.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/lint/test/run.py b/tools/lint/test/run.py
index a81b1fa9d43..6e6d98b9021 100755
--- a/tools/lint/test/run.py
+++ b/tools/lint/test/run.py
@@ -89,6 +89,7 @@ def test(self):
             for err in expected:
                 self.assertIn(err, stderr)
 
+    test.__name__ = 'test_' + file_name.split('.')[0]
     return test
 
 dirname = os.path.join(os.path.abspath(testDir), 'fixtures')
@@ -99,7 +100,6 @@ def test(self):
         continue
 
     t = create_file_test(file_name, full_path)
-    t.__name__ = 'test_' + file_name
     setattr(TestLinter, t.__name__, t)
 
 if __name__ == '__main__':

From a9111d71442c4cc3fe3691799e18b495193ced41 Mon Sep 17 00:00:00 2001
From: Mike Pennisi <mike@mikepennisi.com>
Date: Tue, 24 Sep 2019 20:22:26 -0400
Subject: [PATCH 3/3] lint: add rule to verify use of harness files

Verify that every test file which references a harness file using the
"includes" directive also contains at least one reference to a value
defined in the harness file.

To support this check, extend each harness file with a list of values
which it defines.
---
 harness/arrayContains.js                      |  1 +
 harness/assert.js                             |  1 +
 harness/assertRelativeDateMs.js               |  1 +
 harness/async-gc.js                           |  1 +
 harness/atomicsHelper.js                      |  5 ++
 harness/byteConversionValues.js               |  1 +
 harness/compareArray.js                       |  1 +
 harness/compareIterator.js                    |  1 +
 harness/dateConstants.js                      | 11 ++++
 harness/decimalToHexString.js                 |  1 +
 harness/deepEqual.js                          |  1 +
 harness/detachArrayBuffer.js                  |  2 +-
 harness/doneprintHandle.js                    |  2 +-
 harness/fnGlobalObject.js                     |  1 +
 harness/isConstructor.js                      |  1 +
 harness/nans.js                               |  1 +
 harness/nativeFunctionMatcher.js              |  5 +-
 harness/promiseHelper.js                      |  1 +
 harness/propertyHelper.js                     |  9 +++
 harness/proxyTrapsHelper.js                   |  1 +
 harness/regExpUtils.js                        |  1 +
 harness/sta.js                                |  1 +
 harness/tcoHelper.js                          |  1 +
 harness/testAtomics.js                        |  4 ++
 harness/testBigIntTypedArray.js               |  1 +
 harness/testIntl.js                           | 18 ++++-
 harness/testTypedArray.js                     |  7 ++
 harness/timer.js                              |  1 +
 harness/typeCoercion.js                       | 24 +++++++
 harness/wellKnownIntrinsicObjects.js          |  1 +
 tools/lint/lib/checks/includes.py             | 65 +++++++++++++++++++
 tools/lint/lint.py                            |  2 +
 .../fixtures/harness/usesDetachArrayBuffer.js | 13 ++++
 .../test/fixtures/harness_features_valid.js   |  2 +-
 ...ness_verifyconfigurableproperty_invalid.js |  2 +-
 ...arness_verifyconfigurableproperty_valid.js |  2 +-
 .../test/fixtures/includes_invalid_empty.js   | 11 ++++
 .../test/fixtures/includes_invalid_noref.js   | 16 +++++
 .../test/fixtures/includes_valid_direct.js    | 10 +++
 .../test/fixtures/includes_valid_indirect.js  | 13 ++++
 40 files changed, 236 insertions(+), 7 deletions(-)
 create mode 100644 tools/lint/lib/checks/includes.py
 create mode 100644 tools/lint/test/fixtures/harness/usesDetachArrayBuffer.js
 create mode 100644 tools/lint/test/fixtures/includes_invalid_empty.js
 create mode 100644 tools/lint/test/fixtures/includes_invalid_noref.js
 create mode 100644 tools/lint/test/fixtures/includes_valid_direct.js
 create mode 100644 tools/lint/test/fixtures/includes_valid_indirect.js

diff --git a/harness/arrayContains.js b/harness/arrayContains.js
index d68a42184a0..5807320f1d8 100644
--- a/harness/arrayContains.js
+++ b/harness/arrayContains.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Verify that a subArray is contained within an array.
+defines: [arrayContains]
 ---*/
 
 /**
diff --git a/harness/assert.js b/harness/assert.js
index b71f7a88ca9..bb2db89011a 100644
--- a/harness/assert.js
+++ b/harness/assert.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Collection of assertion functions used throughout test262
+defines: [assert]
 ---*/
 
 
diff --git a/harness/assertRelativeDateMs.js b/harness/assertRelativeDateMs.js
index a4ad32c527e..b031b201a48 100644
--- a/harness/assertRelativeDateMs.js
+++ b/harness/assertRelativeDateMs.js
@@ -5,6 +5,7 @@ description: |
     Verify that the given date object's Number representation describes the
     correct number of milliseconds since the Unix epoch relative to the local
     time zone (as interpreted at the specified date).
+defines: [assertRelativeDateMs]
 ---*/
 
 /**
diff --git a/harness/async-gc.js b/harness/async-gc.js
index 90510dc43b2..daf0d08a170 100644
--- a/harness/async-gc.js
+++ b/harness/async-gc.js
@@ -6,6 +6,7 @@ description: >
 features: [Symbol, async-functions]
 flags: [non-deterministic]
 features: [FinalizationGroup]
+defines: [asyncGC, asyncGCDeref, resolveAsyncGC]
 ---*/
 
 function asyncGC(...targets) {
diff --git a/harness/atomicsHelper.js b/harness/atomicsHelper.js
index 135c16eb671..9c1217351ec 100644
--- a/harness/atomicsHelper.js
+++ b/harness/atomicsHelper.js
@@ -3,6 +3,11 @@
 /*---
 description: >
     Collection of functions used to interact with Atomics.* operations across agent boundaries.
+defines:
+  - $262.agent.getReport
+  - $262.agent.safeBroadcast
+  - $262.agent.tryYield
+  - $262.trySleep
 ---*/
 
 /**
diff --git a/harness/byteConversionValues.js b/harness/byteConversionValues.js
index bc7b9efdb2a..07e1303edb2 100644
--- a/harness/byteConversionValues.js
+++ b/harness/byteConversionValues.js
@@ -7,6 +7,7 @@ description: |
     This helper is mostly used on tests for TypedArray and DataView, and each
     array from the expected values must match the original values array on every
     index containing its original value.
+defines: [byteConversionValues]
 ---*/
 var byteConversionValues = {
   values: [
diff --git a/harness/compareArray.js b/harness/compareArray.js
index ff89ea12159..53feee10c10 100644
--- a/harness/compareArray.js
+++ b/harness/compareArray.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Compare the contents of two arrays
+defines: [compareArray]
 ---*/
 
 // @ts-check
diff --git a/harness/compareIterator.js b/harness/compareIterator.js
index 69b9246e1b7..60cc446e7d1 100644
--- a/harness/compareIterator.js
+++ b/harness/compareIterator.js
@@ -2,6 +2,7 @@
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 description: Compare the values of an iterator with an array of expected values
+defines: [assert.compareIterator]
 ---*/
 
 // Example:
diff --git a/harness/dateConstants.js b/harness/dateConstants.js
index f9e9fca0816..1e728d32858 100644
--- a/harness/dateConstants.js
+++ b/harness/dateConstants.js
@@ -3,6 +3,17 @@
 /*---
 description: |
     Collection of date-centric values
+defines:
+  - date_1899_end
+  - date_1900_start
+  - date_1969_end
+  - date_1970_start
+  - date_1999_end
+  - date_2000_start
+  - date_2099_end
+  - date_2100_start
+  - start_of_time
+  - end_of_time
 ---*/
 
 var date_1899_end = -2208988800001;
diff --git a/harness/decimalToHexString.js b/harness/decimalToHexString.js
index c531c5f88f2..74fb4877600 100644
--- a/harness/decimalToHexString.js
+++ b/harness/decimalToHexString.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Collection of functions used to assert the correctness of various encoding operations.
+defines: [decimalToHexString, decimalToPercentHexString]
 ---*/
 
 function decimalToHexString(n) {
diff --git a/harness/deepEqual.js b/harness/deepEqual.js
index 817cd8e5635..628c62ae185 100644
--- a/harness/deepEqual.js
+++ b/harness/deepEqual.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Compare two values structurally
+defines: [assert.deepEqual]
 ---*/
 
 // @ts-check
diff --git a/harness/detachArrayBuffer.js b/harness/detachArrayBuffer.js
index abc737da91d..81af99f3b04 100644
--- a/harness/detachArrayBuffer.js
+++ b/harness/detachArrayBuffer.js
@@ -5,7 +5,7 @@ description: |
     A function used in the process of asserting correctness of TypedArray objects.
 
     $262.detachArrayBuffer is defined by a host.
-
+defines: [$DETACHBUFFER]
 ---*/
 
 function $DETACHBUFFER(buffer) {
diff --git a/harness/doneprintHandle.js b/harness/doneprintHandle.js
index a6ea799eeb5..e9681240a95 100644
--- a/harness/doneprintHandle.js
+++ b/harness/doneprintHandle.js
@@ -2,7 +2,7 @@
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 description: |
-
+defines: [$DONE]
 ---*/
 
 function __consolePrintHandle__(msg) {
diff --git a/harness/fnGlobalObject.js b/harness/fnGlobalObject.js
index 1907c25209a..231897c86a9 100644
--- a/harness/fnGlobalObject.js
+++ b/harness/fnGlobalObject.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Produce a reliable global object
+defines: [fnGlobalObject]
 ---*/
 
 var __globalObject = Function("return this;")();
diff --git a/harness/isConstructor.js b/harness/isConstructor.js
index bf7433219e1..5cad4842423 100644
--- a/harness/isConstructor.js
+++ b/harness/isConstructor.js
@@ -4,6 +4,7 @@
 /*---
 description: |
     Test if a given function is a constructor function.
+defines: [isConstructor]
 ---*/
 
 function isConstructor(f) {
diff --git a/harness/nans.js b/harness/nans.js
index 7d127c51f89..521ba9d7ad3 100644
--- a/harness/nans.js
+++ b/harness/nans.js
@@ -6,6 +6,7 @@ description: |
     to create distinct bit representations on various platforms. These provide a
     weak basis for assertions regarding the consistent canonicalization of NaN
     values in Array buffers.
+defines: [NaNs]
 ---*/
 
 var NaNs = [
diff --git a/harness/nativeFunctionMatcher.js b/harness/nativeFunctionMatcher.js
index 4e02c1e4197..1d3e9589cbd 100644
--- a/harness/nativeFunctionMatcher.js
+++ b/harness/nativeFunctionMatcher.js
@@ -8,7 +8,10 @@ info: |
 
     NativeFunction :
       function _IdentifierName_ opt ( _FormalParameters_ ) { [ native code ] }
-
+defines:
+  - NATIVE_FUNCTION_RE
+  - assertToStringOrNativeFunction
+  - assertNativeFunction
 ---*/
 const NATIVE_FUNCTION_RE = /\bfunction\b[\s\S]*\([\s\S]*\)[\s\S]*\{[\s\S]*\[[\s\S]*\bnative\b[\s\S]+\bcode\b[\s\S]*\][\s\S]*\}/;
 
diff --git a/harness/promiseHelper.js b/harness/promiseHelper.js
index 2979663e577..ad426cdd1c2 100644
--- a/harness/promiseHelper.js
+++ b/harness/promiseHelper.js
@@ -6,6 +6,7 @@ description: |
     and incrementing by 1 for each entry in the array. Used by
     Promise tests to assert the order of execution in deep Promise
     resolution pipelines.
+defines: [checkSequence, checkSettledPromises]
 ---*/
 
 function checkSequence(arr, message) {
diff --git a/harness/propertyHelper.js b/harness/propertyHelper.js
index 955b1a13110..4a4081fcf40 100644
--- a/harness/propertyHelper.js
+++ b/harness/propertyHelper.js
@@ -4,6 +4,15 @@
 description: |
     Collection of functions used to safely verify the correctness of
     property descriptors.
+defines:
+  - verifyProperty
+  - verifyEqualTo
+  - verifyWritable
+  - verifyNotWritable
+  - verifyEnumerable
+  - verifyNotEnumerable
+  - verifyConfigurable
+  - verifyNotConfigurable
 ---*/
 
 // @ts-check
diff --git a/harness/proxyTrapsHelper.js b/harness/proxyTrapsHelper.js
index 387581afb11..3bd0256b07a 100644
--- a/harness/proxyTrapsHelper.js
+++ b/harness/proxyTrapsHelper.js
@@ -4,6 +4,7 @@
 description: |
     Used to assert the correctness of object behavior in the presence
     and context of Proxy objects.
+defines: [allowProxyTraps]
 ---*/
 
 function allowProxyTraps(overrides) {
diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js
index 2abfee3890a..be7039fda09 100644
--- a/harness/regExpUtils.js
+++ b/harness/regExpUtils.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Collection of functions used to assert the correctness of RegExp objects.
+defines: [buildString, testPropertyEscapes, matchValidator]
 ---*/
 
 function buildString({ loneCodePoints, ranges }) {
diff --git a/harness/sta.js b/harness/sta.js
index 9083b38f36f..42dd47e2ebb 100644
--- a/harness/sta.js
+++ b/harness/sta.js
@@ -6,6 +6,7 @@ description: |
 
     - An error class to avoid false positives when testing for thrown exceptions
     - A function to explicitly throw an exception using the Test262Error class
+defines: [Test262Error, $ERROR, $DONOTEVALUATE]
 ---*/
 
 
diff --git a/harness/tcoHelper.js b/harness/tcoHelper.js
index ac092346d69..070ce764b71 100644
--- a/harness/tcoHelper.js
+++ b/harness/tcoHelper.js
@@ -5,6 +5,7 @@ description: |
     This defines the number of consecutive recursive function calls that must be
     made in order to prove that stack frames are properly destroyed according to
     ES2015 tail call optimization semantics.
+defines: [$MAX_ITERATIONS]
 ---*/
 
 
diff --git a/harness/testAtomics.js b/harness/testAtomics.js
index d5e4fe1e21f..cf605445cdd 100644
--- a/harness/testAtomics.js
+++ b/harness/testAtomics.js
@@ -3,6 +3,10 @@
 /*---
 description: |
     Collection of functions used to assert the correctness of SharedArrayBuffer objects.
+defines:
+  - testWithAtomicsOutOfBoundsIndices
+  - testWithAtomicsInBoundsIndices
+  - testWithAtomicsNonViewValues
 ---*/
 
 
diff --git a/harness/testBigIntTypedArray.js b/harness/testBigIntTypedArray.js
index 9fcdc528d93..5db37178b96 100644
--- a/harness/testBigIntTypedArray.js
+++ b/harness/testBigIntTypedArray.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Collection of functions used to assert the correctness of BigInt TypedArray objects.
+defines: [TypedArray, testWithBigIntTypedArrayConstructors]
 ---*/
 
 /**
diff --git a/harness/testIntl.js b/harness/testIntl.js
index a4d8b09e40f..589367d3bd4 100644
--- a/harness/testIntl.js
+++ b/harness/testIntl.js
@@ -6,8 +6,24 @@ description: |
     This file contains shared functions for the tests in the conformance test
     suite for the ECMAScript Internationalization API.
 author: Norbert Lindenberg
+defines:
+  - testWithIntlConstructors
+  - taintDataProperty
+  - taintMethod
+  - taintProperties
+  - taintArray
+  - getLocaleSupportInfo
+  - getInvalidLanguageTags
+  - isCanonicalizedStructurallyValidLanguageTag
+  - getInvalidLocaleArguments
+  - testOption
+  - testForUnwantedRegExpChanges
+  - isValidNumberingSystem
+  - testNumberFormat
+  - getDateTimeComponents
+  - getDateTimeComponentValues
+  - isCanonicalizedStructurallyValidTimeZoneName
 ---*/
-
 /**
  */
 
diff --git a/harness/testTypedArray.js b/harness/testTypedArray.js
index 030c05e90b6..13b23878358 100644
--- a/harness/testTypedArray.js
+++ b/harness/testTypedArray.js
@@ -3,6 +3,13 @@
 /*---
 description: |
     Collection of functions used to assert the correctness of TypedArray objects.
+defines:
+  - typedArrayConstructors
+  - floatArrayConstructors
+  - intArrayConstructors
+  - TypedArray
+  - testWithTypedArrayConstructors
+  - testTypedArrayConversions
 ---*/
 
 /**
diff --git a/harness/timer.js b/harness/timer.js
index 27379398908..162b1e9da57 100644
--- a/harness/timer.js
+++ b/harness/timer.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     Used in website/scripts/sth.js
+defines: [setTimeout]
 ---*/
 //setTimeout is not available, hence this script was loaded
 if (Promise === undefined && this.setTimeout === undefined) {
diff --git a/harness/typeCoercion.js b/harness/typeCoercion.js
index 26599d21677..dc1089c3c71 100644
--- a/harness/typeCoercion.js
+++ b/harness/typeCoercion.js
@@ -4,6 +4,30 @@
 description: |
     Functions to help generate test cases for testing type coercion abstract
     operations like ToNumber.
+defines:
+  - testCoercibleToIndexZero
+  - testCoercibleToIndexOne
+  - testCoercibleToIndexFromIndex
+  - testCoercibleToIntegerZero
+  - testCoercibleToIntegerOne
+  - testCoercibleToNumberZero
+  - testCoercibleToNumberNan
+  - testCoercibleToNumberOne
+  - testCoercibleToIntegerFromInteger
+  - testPrimitiveWrappers
+  - testCoercibleToPrimitiveWithMethod
+  - testNotCoercibleToIndex
+  - testNotCoercibleToInteger
+  - testNotCoercibleToNumber
+  - testNotCoercibleToPrimitive
+  - testCoercibleToString
+  - testNotCoercibleToString
+  - testCoercibleToBooleanTrue
+  - testCoercibleToBooleanFalse
+  - testCoercibleToBigIntZero
+  - testCoercibleToBigIntOne
+  - testCoercibleToBigIntFromBigInt
+  - testNotCoercibleToBigInt
 ---*/
 
 function testCoercibleToIndexZero(test) {
diff --git a/harness/wellKnownIntrinsicObjects.js b/harness/wellKnownIntrinsicObjects.js
index 2c1ce9df82d..241e24c4eeb 100644
--- a/harness/wellKnownIntrinsicObjects.js
+++ b/harness/wellKnownIntrinsicObjects.js
@@ -3,6 +3,7 @@
 /*---
 description: |
     An Array of all representable Well-Known Intrinsic Objects
+defines: [WellKnownIntrinsicObjects]
 ---*/
 
 const WellKnownIntrinsicObjects = [
diff --git a/tools/lint/lib/checks/includes.py b/tools/lint/lib/checks/includes.py
new file mode 100644
index 00000000000..8a37042dfa0
--- /dev/null
+++ b/tools/lint/lib/checks/includes.py
@@ -0,0 +1,65 @@
+import os
+import re
+
+from ..check import Check
+from ..frontmatter import parse
+
+class CheckIncludes(Check):
+    '''Ensure tests make use of the harness files that they require via the
+    `includes` directive.'''
+    ID = 'INCLUDES'
+    _cache = dict()
+
+    @staticmethod
+    def _remove_frontmatter(source):
+        return re.sub(
+            r'/\*---.*---\*/', '', source, flags=re.DOTALL
+        )
+
+    @staticmethod
+    def _load(include_name):
+        if include_name not in CheckIncludes._cache:
+            with open(os.path.join('harness', include_name), 'r') as f:
+                source = f.read()
+
+            CheckIncludes._cache[include_name] = {
+                'name': include_name,
+                'source': CheckIncludes._remove_frontmatter(source),
+                'defines': parse(source)['defines']
+            }
+
+        return CheckIncludes._cache.get(include_name)
+
+    @staticmethod
+    def _has_reference(source, names):
+        for name in names:
+            if name in source:
+                return True
+        return False
+
+    def run(self, name, meta, source):
+        if not meta or 'includes' not in meta:
+            return
+
+        harness_files = [self._load(name) for name in meta['includes']]
+
+        if len(harness_files) == 0:
+            return 'If present, the `includes` tag must have at least one member'
+
+        without_frontmatter = self._remove_frontmatter(source)
+
+        for harness_file in harness_files:
+            if self._has_reference(without_frontmatter, harness_file['defines']):
+                continue
+
+            # If the test file does not reference a value defined by a given
+            # include file, inspect each of the other include files for such a
+            # reference.
+            for other_harness_file in harness_files:
+                if other_harness_file == harness_file:
+                    continue
+
+                if self._has_reference(other_harness_file['source'], harness_file['defines']):
+                    break
+            else:
+                return 'Unused include: "%s"' % harness_file['name']
diff --git a/tools/lint/lint.py b/tools/lint/lint.py
index dc320003c5a..c43e542d042 100755
--- a/tools/lint/lint.py
+++ b/tools/lint/lint.py
@@ -37,6 +37,7 @@
 from lib.checks.frontmatter import CheckFrontmatter
 from lib.checks.harnessfeatures import CheckHarnessFeatures
 from lib.checks.harness import CheckHarness
+from lib.checks.includes import CheckIncludes
 from lib.checks.license import CheckLicense
 from lib.checks.negative import CheckNegative
 from lib.checks.filename import CheckFileName
@@ -61,6 +62,7 @@
     CheckFeatures('features.txt'),
     CheckHarnessFeatures(),
     CheckHarness(),
+    CheckIncludes(),
     CheckLicense(),
     CheckNegative(),
     CheckNoPadding(),
diff --git a/tools/lint/test/fixtures/harness/usesDetachArrayBuffer.js b/tools/lint/test/fixtures/harness/usesDetachArrayBuffer.js
new file mode 100644
index 00000000000..b0de4fedf0d
--- /dev/null
+++ b/tools/lint/test/fixtures/harness/usesDetachArrayBuffer.js
@@ -0,0 +1,13 @@
+// Copyright (C) 2019 Mike Pennisi.  All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: |
+    A function used in the process of asserting correctness of TypedArray objects.
+
+    $262.detachArrayBuffer is defined by a host.
+defines: [modifiedDetachArrayBuffer]
+---*/
+
+function modifiedDetachArrayBuffer(buffer) {
+  return $DETACHBUFFER(buffer);
+}
diff --git a/tools/lint/test/fixtures/harness_features_valid.js b/tools/lint/test/fixtures/harness_features_valid.js
index 4f92cf54eb5..3a1fd09772a 100644
--- a/tools/lint/test/fixtures/harness_features_valid.js
+++ b/tools/lint/test/fixtures/harness_features_valid.js
@@ -8,4 +8,4 @@ features: [TypedArray]
 includes: [testTypedArray.js]
 ---*/
 
-// empty
+intArrayConstructors;
diff --git a/tools/lint/test/fixtures/harness_verifyconfigurableproperty_invalid.js b/tools/lint/test/fixtures/harness_verifyconfigurableproperty_invalid.js
index 925c5a66095..8d0dde0c601 100644
--- a/tools/lint/test/fixtures/harness_verifyconfigurableproperty_invalid.js
+++ b/tools/lint/test/fixtures/harness_verifyconfigurableproperty_invalid.js
@@ -5,7 +5,7 @@ HARNESS - verifyConfigurable & verifyProperty may not be used in the same file
 /*---
 esid: sec-whatever
 description: Minimal test
-includes: [verifyProperty.js]
+includes: [propertyHelper.js]
 ---*/
 
 verifyConfigurable(Object, '');
diff --git a/tools/lint/test/fixtures/harness_verifyconfigurableproperty_valid.js b/tools/lint/test/fixtures/harness_verifyconfigurableproperty_valid.js
index f01afd16e92..8cd6337c15d 100644
--- a/tools/lint/test/fixtures/harness_verifyconfigurableproperty_valid.js
+++ b/tools/lint/test/fixtures/harness_verifyconfigurableproperty_valid.js
@@ -4,7 +4,7 @@
 /*---
 esid: sec-whatever
 description: Minimal test
-includes: [verifyProperty.js]
+includes: [propertyHelper.js]
 ---*/
 
 verifyConfigurable(Object, '');
diff --git a/tools/lint/test/fixtures/includes_invalid_empty.js b/tools/lint/test/fixtures/includes_invalid_empty.js
new file mode 100644
index 00000000000..545aa101b2a
--- /dev/null
+++ b/tools/lint/test/fixtures/includes_invalid_empty.js
@@ -0,0 +1,11 @@
+INCLUDES
+^ expected errors | v input
+// Copyright (C) 2019 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
+includes: []
+---*/
+
+void 0;
diff --git a/tools/lint/test/fixtures/includes_invalid_noref.js b/tools/lint/test/fixtures/includes_invalid_noref.js
new file mode 100644
index 00000000000..49bb66aca7d
--- /dev/null
+++ b/tools/lint/test/fixtures/includes_invalid_noref.js
@@ -0,0 +1,16 @@
+INCLUDES
+^ expected errors | v input
+// Copyright (C) 2019 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
+includes: [detachArrayBuffer.js]
+---*/
+
+// This file doesn't reference any names defined by the "include" file. It
+// contains some references that don't match exactly in order to verify that
+// the linter is not susceptible to false positives.
+DETACHBUFFER();
+$DETACHBUFFE();
+$DETACH_BUFFER();
diff --git a/tools/lint/test/fixtures/includes_valid_direct.js b/tools/lint/test/fixtures/includes_valid_direct.js
new file mode 100644
index 00000000000..6f6adaea99e
--- /dev/null
+++ b/tools/lint/test/fixtures/includes_valid_direct.js
@@ -0,0 +1,10 @@
+^ expected errors | v input
+// Copyright (C) 2019 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
+includes: [detachArrayBuffer.js]
+---*/
+
+$DETACHBUFFER();
diff --git a/tools/lint/test/fixtures/includes_valid_indirect.js b/tools/lint/test/fixtures/includes_valid_indirect.js
new file mode 100644
index 00000000000..7addd6fec3e
--- /dev/null
+++ b/tools/lint/test/fixtures/includes_valid_indirect.js
@@ -0,0 +1,13 @@
+^ expected errors | v input
+// Copyright (C) 2019 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
+includes: [detachArrayBuffer.js, usesDetachArrayBuffer.js]
+---*/
+
+// This file doesn't reference any names defined by the first "include" file,
+// but the second "include" file does.
+
+modifiedDetachArrayBuffer();