From e43ce56105a23c9a43ee26dab705b482c36df049 Mon Sep 17 00:00:00 2001
From: Leo Balter <leonardo.balter@gmail.com>
Date: Sat, 15 Apr 2017 12:56:29 -0400
Subject: [PATCH] Add Verify Property harness (#979)

---
 harness/propertyHelper.js                     | 72 ++++++++++++++++++-
 test/harness/assert-throws-incorrect-ctor.js  |  4 +-
 test/harness/verifyProperty-arguments.js      | 25 +++++++
 .../verifyProperty-desc-is-not-object.js      | 35 +++++++++
 test/harness/verifyProperty-noproperty.js     | 21 ++++++
 .../verifyProperty-restore-accessor-symbol.js | 42 +++++++++++
 .../verifyProperty-restore-accessor.js        | 42 +++++++++++
 test/harness/verifyProperty-restore-symbol.js | 33 +++++++++
 test/harness/verifyProperty-restore.js        | 33 +++++++++
 test/harness/verifyProperty-string-prop.js    | 51 +++++++++++++
 test/harness/verifyProperty-symbol-prop.js    | 51 +++++++++++++
 test/harness/verifyProperty-undefined-desc.js | 32 +++++++++
 12 files changed, 438 insertions(+), 3 deletions(-)
 create mode 100644 test/harness/verifyProperty-arguments.js
 create mode 100644 test/harness/verifyProperty-desc-is-not-object.js
 create mode 100644 test/harness/verifyProperty-noproperty.js
 create mode 100644 test/harness/verifyProperty-restore-accessor-symbol.js
 create mode 100644 test/harness/verifyProperty-restore-accessor.js
 create mode 100644 test/harness/verifyProperty-restore-symbol.js
 create mode 100644 test/harness/verifyProperty-restore.js
 create mode 100644 test/harness/verifyProperty-string-prop.js
 create mode 100644 test/harness/verifyProperty-symbol-prop.js
 create mode 100644 test/harness/verifyProperty-undefined-desc.js

diff --git a/harness/propertyHelper.js b/harness/propertyHelper.js
index 6d67e40511..54fadd1925 100644
--- a/harness/propertyHelper.js
+++ b/harness/propertyHelper.js
@@ -1,4 +1,74 @@
 
+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, '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.sameValue(failures.length, 0, failures.join('; '));
+
+  if (options && options.restore) {
+    Object.defineProperty(obj, name, originalDesc);
+  }
+
+  return true;
+}
+
 function isConfigurable(obj, name) {
   try {
     delete obj[name];
@@ -11,7 +81,7 @@ function isConfigurable(obj, name) {
 }
 
 function isEnumerable(obj, name) {
-  var stringCheck;
+  var stringCheck = false;
 
   if (typeof name === "string") {
     for (var x in obj) {
diff --git a/test/harness/assert-throws-incorrect-ctor.js b/test/harness/assert-throws-incorrect-ctor.js
index 47f4f0ba76..b12f06ee77 100644
--- a/test/harness/assert-throws-incorrect-ctor.js
+++ b/test/harness/assert-throws-incorrect-ctor.js
@@ -3,8 +3,8 @@
 
 /*---
 description: >
-    Functions that throw values whose constructor does not match the specified
-    constructor do not satisfy the assertion.
+  Functions that throw values whose constructor does not match the specified
+  constructor do not satisfy the assertion.
 ---*/
 
 var threw = false;
diff --git a/test/harness/verifyProperty-arguments.js b/test/harness/verifyProperty-arguments.js
new file mode 100644
index 0000000000..9c2c9faaec
--- /dev/null
+++ b/test/harness/verifyProperty-arguments.js
@@ -0,0 +1,25 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  verifyProperty should receive at least 3 arguments: obj, name, and descriptor
+includes: [propertyHelper.js]
+---*/
+
+// monkeypatch the API
+$ERROR = function $ERROR(message) {
+  throw new Test262Error(message);
+};
+
+assert.throws(Test262Error, () => {
+  verifyProperty();
+}, "0 arguments");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(Object);
+}, "1 argument");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(Object, 'foo');
+}, "2 arguments");
diff --git a/test/harness/verifyProperty-desc-is-not-object.js b/test/harness/verifyProperty-desc-is-not-object.js
new file mode 100644
index 0000000000..6df9700a97
--- /dev/null
+++ b/test/harness/verifyProperty-desc-is-not-object.js
@@ -0,0 +1,35 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  The desc argument should be an object or undefined
+includes: [propertyHelper.js]
+---*/
+
+// monkeypatch the API
+$ERROR = function $ERROR(message) {
+  throw new Test262Error(message);
+};
+
+var sample = { foo: 42 };
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, "foo", 'configurable');
+}, "string");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, 'foo', true);
+}, "boolean");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, 'foo', 42);
+}, "number");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, 'foo', null);
+}, "null");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, 'foo', Symbol(1));
+}, "symbol");
diff --git a/test/harness/verifyProperty-noproperty.js b/test/harness/verifyProperty-noproperty.js
new file mode 100644
index 0000000000..bc63830675
--- /dev/null
+++ b/test/harness/verifyProperty-noproperty.js
@@ -0,0 +1,21 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  The first argument should have an own property
+includes: [propertyHelper.js]
+---*/
+
+// monkeypatch the API
+$ERROR = function $ERROR(message) {
+  throw new Test262Error(message);
+};
+
+assert.throws(Test262Error, () => {
+  verifyProperty(Object, 'JeanPaulSartre', {});
+}, "inexisting property");
+
+assert.throws(Test262Error, () => {
+  verifyProperty({}, 'hasOwnProperty', {});
+}, "inexisting own property");
diff --git a/test/harness/verifyProperty-restore-accessor-symbol.js b/test/harness/verifyProperty-restore-accessor-symbol.js
new file mode 100644
index 0000000000..77d726d542
--- /dev/null
+++ b/test/harness/verifyProperty-restore-accessor-symbol.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  verifyProperty allows restoring the original accessor descriptor
+includes: [propertyHelper.js]
+---*/
+
+var obj;
+var prop = Symbol(1);
+var desc = { enumerable: true, configurable: true, get() { return 42; }, set() {} };
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc);
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  false
+);
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc, { restore: true });
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  true
+);
+assert.sameValue(obj[prop], 42);
+assert.sameValue(
+  Object.getOwnPropertyDescriptor(obj, prop).get,
+  desc.get
+);
+
+assert.sameValue(
+  Object.getOwnPropertyDescriptor(obj, prop).set,
+  desc.set
+);
diff --git a/test/harness/verifyProperty-restore-accessor.js b/test/harness/verifyProperty-restore-accessor.js
new file mode 100644
index 0000000000..0a8b84dd34
--- /dev/null
+++ b/test/harness/verifyProperty-restore-accessor.js
@@ -0,0 +1,42 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  verifyProperty allows restoring the original accessor descriptor
+includes: [propertyHelper.js]
+---*/
+
+var obj;
+var prop = "prop";
+var desc = { enumerable: true, configurable: true, get() { return 42; }, set() {} };
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc);
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  false
+);
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc, { restore: true });
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  true
+);
+assert.sameValue(obj[prop], 42);
+assert.sameValue(
+  Object.getOwnPropertyDescriptor(obj, prop).get,
+  desc.get
+);
+
+assert.sameValue(
+  Object.getOwnPropertyDescriptor(obj, prop).set,
+  desc.set
+);
diff --git a/test/harness/verifyProperty-restore-symbol.js b/test/harness/verifyProperty-restore-symbol.js
new file mode 100644
index 0000000000..a407412e56
--- /dev/null
+++ b/test/harness/verifyProperty-restore-symbol.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  verifyProperty allows restoring the original descriptor
+includes: [propertyHelper.js]
+---*/
+
+var obj;
+var prop = Symbol(1);
+var desc = { enumerable: true, configurable: true, writable: true, value: 42 };
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc);
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  false
+);
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc, { restore: true });
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  true
+);
+assert.sameValue(obj[prop], 42);
diff --git a/test/harness/verifyProperty-restore.js b/test/harness/verifyProperty-restore.js
new file mode 100644
index 0000000000..56c01e8e3e
--- /dev/null
+++ b/test/harness/verifyProperty-restore.js
@@ -0,0 +1,33 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  verifyProperty allows restoring the original descriptor
+includes: [propertyHelper.js]
+---*/
+
+var obj;
+var prop = 'prop';
+var desc = { enumerable: true, configurable: true, writable: true, value: 42 };
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc);
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  false
+);
+
+obj = {};
+Object.defineProperty(obj, prop, desc);
+
+verifyProperty(obj, prop, desc, { restore: true });
+
+assert.sameValue(
+  Object.prototype.hasOwnProperty.call(obj, prop),
+  true
+);
+assert.sameValue(obj[prop], 42);
diff --git a/test/harness/verifyProperty-string-prop.js b/test/harness/verifyProperty-string-prop.js
new file mode 100644
index 0000000000..c1c0338439
--- /dev/null
+++ b/test/harness/verifyProperty-string-prop.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  Verify property descriptor
+includes: [propertyHelper.js]
+---*/
+
+var obj;
+var prop = 'prop';
+
+function reset(desc) {
+  obj = {};
+  Object.defineProperty(obj, prop, desc);
+}
+
+function checkDesc(desc) {
+  reset(desc);
+  assert(verifyProperty(obj, prop, desc));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { enumerable: desc.enumerable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { writable: desc.writable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { configurable: desc.configurable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { configurable: desc.configurable, enumerable: desc.enumerable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { configurable: desc.configurable, writable: desc.writable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { writable: desc.writable, enumerable: desc.enumerable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { enumerable: desc.enumerable, configurable: desc.configurable }));
+}
+
+checkDesc({ enumerable: true, configurable: true, writable: true });
+checkDesc({ enumerable: false, writable: false, configurable: false });
+checkDesc({ enumerable: true, writable: false, configurable: false });
+checkDesc({ enumerable: false, writable: true, configurable: false });
+checkDesc({ enumerable: false, writable: false, configurable: true });
+checkDesc({ enumerable: true, writable: false, configurable: true });
+checkDesc({ enumerable: true, writable: true, configurable: false });
+checkDesc({ enumerable: false, writable: true, configurable: true });
diff --git a/test/harness/verifyProperty-symbol-prop.js b/test/harness/verifyProperty-symbol-prop.js
new file mode 100644
index 0000000000..3a59108d8d
--- /dev/null
+++ b/test/harness/verifyProperty-symbol-prop.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  Verify symbol named property descriptor
+includes: [propertyHelper.js]
+---*/
+
+var obj;
+var prop = Symbol(1);
+
+function reset(desc) {
+  obj = {};
+  Object.defineProperty(obj, prop, desc);
+}
+
+function checkDesc(desc) {
+  reset(desc);
+  assert(verifyProperty(obj, prop, desc));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { enumerable: desc.enumerable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { writable: desc.writable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { configurable: desc.configurable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { configurable: desc.configurable, enumerable: desc.enumerable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { configurable: desc.configurable, writable: desc.writable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { writable: desc.writable, enumerable: desc.enumerable }));
+
+  reset(desc);
+  assert(verifyProperty(obj, prop, { enumerable: desc.enumerable, configurable: desc.configurable }));
+}
+
+checkDesc({ enumerable: true, configurable: true, writable: true });
+checkDesc({ enumerable: false, writable: false, configurable: false });
+checkDesc({ enumerable: true, writable: false, configurable: false });
+checkDesc({ enumerable: false, writable: true, configurable: false });
+checkDesc({ enumerable: false, writable: false, configurable: true });
+checkDesc({ enumerable: true, writable: false, configurable: true });
+checkDesc({ enumerable: true, writable: true, configurable: false });
+checkDesc({ enumerable: false, writable: true, configurable: true });
diff --git a/test/harness/verifyProperty-undefined-desc.js b/test/harness/verifyProperty-undefined-desc.js
new file mode 100644
index 0000000000..315061666d
--- /dev/null
+++ b/test/harness/verifyProperty-undefined-desc.js
@@ -0,0 +1,32 @@
+// Copyright (C) 2017 Leo Balter. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+
+/*---
+description: >
+  Verify an undefined descriptor
+includes: [propertyHelper.js]
+---*/
+
+// monkeypatch the API
+$ERROR = function $ERROR(message) {
+  throw new Test262Error(message);
+};
+
+var sample = {
+  bar: undefined,
+  get baz() {}
+};
+
+assert.sameValue(
+  verifyProperty(sample, "foo", undefined),
+  true,
+  "returns true if desc and property descriptor are both undefined"
+);
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, 'bar', undefined);
+}, "dataDescriptor value is undefined");
+
+assert.throws(Test262Error, () => {
+  verifyProperty(sample, 'baz', undefined);
+}, "accessor returns undefined");
-- 
GitLab