//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

var helpers = function helpers() {
  //private
  var undefinedAsString = "undefined";

  var isInBrowser = function isInBrowser() {
    return typeof (document) !== undefinedAsString;
  };

  return {
    // public
    getDummyObject: function () {
      //return isInBrowser() ? document : {};
      return {};
    },

    writeln: function writeln() {
      var line = "", i;
      for (i = 0; i < arguments.length; i += 1) {
        line = line.concat(arguments[i])
      }
      if (!isInBrowser()) {
        WScript.Echo(line);
      } else {
        document.writeln(line);
        document.writeln("<br/>");
      }
    },

    printObject: function printObject(o) {
      var name;
      for (name in o) {
        this.writeln(name, o.hasOwnProperty(name) ? "" : " (inherited)", ": ", o[name]);
      }
    }
  }
} (); // helpers module.

var testRunner = function testRunner() {
  var executedTestCount = 0;
  var passedTestCount = 0;
  var passName;

  return {
    // Runs provided tests.
    // passes is a collection of {name, prep}, where prep is a function to prepare for the pass.
    // The 'testsToRun' is an object that has enumerable properties,
    // each property is an object that has 'name' and 'body' properties.
    runTests: function runTests(passes, testsToRun) {
      for (var p in passes) {
        var pass = passes[p];
        passName = pass.name;
        if (pass.prep) {
            pass.prep();
        }
        for (var i in testsToRun) {
            var test = tests[i];

            //
            // * If test.disabled (e.g., temp bug), skip it.
            // * If test.pass specifies a pass name, only run it for that pass.
            // * If test.pass not defined, run it for any non "runonce" pass.
            //
            if (!test.disabled && (test.pass === passName || (!test.pass && !pass.runonce))) {
                this.runTest(i, test.name, test.body);
            }
        }
      }

      helpers.writeln("Summary of tests: total executed: ", executedTestCount,
        "; passed: ", passedTestCount, "; failed: ", executedTestCount - passedTestCount);
    },

    // Runs test body catching all exceptions.
    // Result: prints PASSED/FAILED to the output.
    runTest: function runTest(testIndex, testName, testBody) {
      helpers.writeln("*** ", passName, " (", testIndex, "): ", testName);

      var isSuccess = true;
      try {
        testBody();
      } catch (ex) {
        var message = ex.message !== undefined ? ex.message : ex;
        helpers.writeln("Test threw exception: ", message);
        isSuccess = false;
      }
      if (isSuccess) {
        helpers.writeln("PASSED");
        ++passedTestCount;
      } else {
        helpers.writeln("FAILED");
      }

      ++executedTestCount;
    }
  }
}(); // testRunner.

var assert = function assert() {
  // private
  var isObject = function isObject(x) {
    return x instanceof Object && typeof x !== "function";
  };

  var compare = function compare(expected, actual) {
    if (isObject(expected)) {
      if (!isObject(actual)) return "actual is not an object";
      var expectedFieldCount = 0, actualFieldCount = 0;
      for (var i in expected) {
        var compareResult = compare(expected[i], actual[i]);
        if (compareResult !== true) return compareResult;
        ++expectedFieldCount;
      }
      for (var i in actual) {
        ++actualFieldCount;
      }
      if (expectedFieldCount !== actualFieldCount) {
        return "actual has different number of fields than expected";
      }
      return true;
    } else {
      if (isObject(actual)) return "actual is an object";
      if (expected === actual) return true;
      return "expected: " + expected + " actual: " + actual;
    }
  };

  var addMessage = function addMessage(baseMessage, message) {
    if (message !== undefined) {
      baseMessage += ": " + message;
    }
    return baseMessage;
  }

  return {
    // Performs deep comparison of arguments.
    // This works for objects and simple types.
    // TODO: account for other types?
    // TODO: account for missing vs undefined fields.
    areEqual: function areEqual(expected, actual, message) {
      var compareResult = compare(expected, actual);
      if (compareResult !== true) {
        throw addMessage("assert.areEqual failed: " + compareResult, message);
      }
    },

    areNotEqual: function areNotEqual(expected, actual, message) {
      var compareResult = compare(expected, actual);
      if (compareResult === true) {
        throw addMessage("assert.areNotEqual failed", message);
      }
    },

    // Makes sure that the function specified by the 'testFunction' parameter
    // throws the exception specified by the 'expectedException' parameter.
    // Note: currently we check only for specific exception and not "all exceptions derived from specified".
    // Example:
    // assert.throws(function() { eval("{"); }, SyntaxError, "expected SyntaxError")
    throws: function throws(testFunction, expectedException, message) {
      var exception = null;
      try {
        testFunction();
      } catch (ex) {
        exception = ex;
      }
      if (!(exception instanceof Object && exception.constructor === expectedException)) {
        var expectedString = expectedException.toString().replace(/\n/g, "").replace(/.*function (.*)\(.*/g, "$1");
        throw addMessage("assert.throws failed: expected: " + expectedString + ", actual: " + exception, message);
      }
    },

    // Can be used to fail the test.
    fail: function fail(message) {
      throw message;
    }
  }
}(); // assert.

var tests = {
    // Note: each test has name (string) and body (function) properties.
    //       Success is when the body does not throw, failure -- when it throws.

    //---------------------- normal identifier property names -------------------------------
    test01: {
      name: "8.12.9.4.a (variation 1): define generic property, check default attrbitues",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo02";
        var pd = {};
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: undefined, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test02: {
      name: "8.12.9.4.a (variation 2): define data property, check default attrbitues",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo03";
        var pd = { value: 0 };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 0, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test03: {
      name: "8.12.9.4.a (variation 3): define generic property by specifying some attributes, check attrbitues",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo02";
        var pd = { configurable: true, writable: false };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: undefined, configurable: true, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test04: {
      name: "8.12.9.4.b: define accessor property, check default attrbitues",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo04";
        var getter = function () { return this.Value };
        var pd = { get: getter };
        Object.defineProperty(o, propertyName, pd);
        var expected = { get: getter, set: undefined, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test05: {
      name: "8.12.9.5: re-define property: use descriptor with all fields absent, check that nothing happens to previous descriptor",
      body: function () {
        var propertyName = "foo05";
        var o = { foo05: 1 };
        var pd = {};
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: true, value: 1, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test06: {
      name: "8.12.9.6: re-define property: use equal descriptor with data field, check that nothing happens to previous descriptor",
      body: function () {
        var propertyName = "foo06";
        var o = { foo06: 1 };
        var pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: true, value: 1, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same

    test07: {
      name: "8.12.9.7.a: re-define property: current descriptor is not configurable and descriptor is configurable, check that it throws TypeError",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo07";
        var pd = { value: 0, configurable: false };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1, configurable: true };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    test08: {
      name: "8.12.9.7.b (variation 1): re-define property: current descriptor is not configurable and descriptor enumerable is specified and it's negation of current enumerable, check that it throws TypeError",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo08";
        var pd = { value: 0 };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1, enumerable: true };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    test09: {
      name: "8.12.9.7.b (variation 2): re-define property: current descriptor is not configurable and descriptor enumerable is not specified, check that it does not throw",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo09";
        var pd = { value: 0, writable: true }; // set writable to true to avoid throw code path.
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1, writable: false };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 1, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test10: {
      name: "8.12.9.7.b (variation 3): re-define property: current descriptor is not configurable and descriptor enumerable is same as current enumerable, check that it does not throw",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo10";
        var pd = { value: 0, writable: true }; // set writable to true to avoid throw code path.
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1, enumerable: false, writable: false };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 1, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test11: {
      name: "8.12.9.8: re-define property: descriptor is not empty, generic and is different from current",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo11";
        var pd = { value: 0, configurable: true };
        Object.defineProperty(o, propertyName, pd);
        pd = { enumerable: true }; // change enumerable to make sure that descriptor is different from current.
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 0, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same
    // - descriptor.IsData != current.IsData

    test12: {
      name: "8.12.9.9.a: re-define property: descriptor.IsData != current.IsData and current is not configurable, check that it throws TypeError",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo12";
        var pd = { value: 0, configurable: false };
        Object.defineProperty(o, propertyName, pd);
        pd = { get: function () { return this.Value; } };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    test13: {
      name: "8.12.9.9.b (variation 1): re-define property: convert from data to accessor descriptor, check that configurable/enumerable (true) are preserved",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo13";
        var pd = { value: 0, configurable: true, enumerable: true };
        Object.defineProperty(o, propertyName, pd);
        var getter = function() { return this.Value; };
        pd = { get: getter };
        Object.defineProperty(o, propertyName, pd);
        var expected = { get: getter, set: undefined, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test14: {
      name: "8.12.9.9.b (variation 2): re-define property: convert from data to accessor descriptor, check that enumerable (false) is preserved",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo14";
        var pd = { value: 0, configurable: true, enumerable: false };
        Object.defineProperty(o, propertyName, pd);
        var getter = function () { return this.Value; };
        pd = { get: getter };
        Object.defineProperty(o, propertyName, pd);
        var expected = { get: getter, set: undefined, configurable: true, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test15: {
      name: "8.12.9.9.b (variation 3): re-define property: convert from data to accessor descriptor, check that configurable/enumerable not preserved when specified by descriptor",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo15";
        var pd = { value: 0, configurable: true, enumerable: true };
        Object.defineProperty(o, propertyName, pd);
        var getter = function () { return this.Value; };
        pd = { get: getter, configurable: false };
        Object.defineProperty(o, propertyName, pd);
        var expected = { get: getter, set: undefined, configurable: false, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test16: {
      name: "8.12.9.9.c (variation 1): re-define property: convert from accessor to data descriptor, check that configurable/enumerable (true) are preserved",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo16";
        var pd = {
          set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
          configurable: true,
          enumerable: true
        };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 1, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test17: {
      name: "8.12.9.9.c (variation 2): re-define property: convert from accessor to data descriptor, check that enumerable (false) is preserved",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo17";
        var pd = {
          set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
          configurable: true,
          enumerable: false
        };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 1, configurable: true, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test18: {
      name: "8.12.9.9.c (variation 3): re-define property: convert from accessor to data descriptor, check that configurable/enumerable (true/false) not preserved when specified by descriptor (false/absent)",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo18";
        var pd = {
          set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
          configurable: true,
          enumerable: false
        };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1, configurable: false };
        Object.defineProperty(o, propertyName, pd);
        // expected: configurable/enumerable = false/false.
        var expected = { writable: false, value: 1, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test19: {
      name: "8.12.9.9.c (variation 4): re-define property: convert from accessor to data descriptor, check that configurable/enumerable (true/true) not preserved when specified by descriptor (absent/false)",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo19";
        var pd = {
          set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
          configurable: true,
          enumerable: true
        };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1, enumerable: false };
        Object.defineProperty(o, propertyName, pd);
        // expected: configurable/enumerable = true/false.
        var expected = { writable: false, value: 1, configurable: true, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same
    // - descriptor is data, current is data

    test20: {
      name: "8.12.9.10.a (variation 1): re-define data property: current is not configurable/not writable and descriptor writable is absent/value is same",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo20";
        var pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 1, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test21: {
      name: "8.12.9.10.a.i: re-define data property: current is not configurable/not writable and descriptor is writable, check that it throws TypeError",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo21";
        var pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 2, writable: true };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
        return true;
      }
    },

    test22: {
      name: "8.12.9.10.a.ii: re-define data property: current is not configurable/not writable and descriptor writable is false and value is different, check that it throws TypeError",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo22";
        var pd = { value: 1 };
        Object.defineProperty(o, propertyName, pd);
        pd = { value: 2, writable: false };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    test23: {
      name: "8.12.9.10.a (variation 2): re-define data property: current is configurable",
      body: function () {
        var propertyName = "foo23";
        var o = { foo23: 1 };
        var pd = { value: 2, writable: false };
        Object.defineProperty(o, propertyName, pd);
        var expected = { writable: false, value: 2, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same
    // - descriptor is accessor, current is accessor
    test24: {
      name: "Test: 8.12.9.11 (variation 1): re-define accessor property: current configurable is true: valid case",
      body: function () {
        var propertyName = "foo24";
        var o = {
          get foo24() { return this.Value; },
          set foo24(arg) { helpers.writeln("old setter"); this.Value = arg; }
        };
        var newGetter = function() { return 2; };
        var newSetter = function(arg) { helpers.writeln("new setter"); }
        var pd = { get: newGetter, set: newSetter };
        Object.defineProperty(o, propertyName, pd);
        var expected = { get: newGetter,  set: newSetter, configurable: true, enumerable: true };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test25: {
      name: "8.12.9.11.a.i: re-define accessor property: current configurable is false, descriptor specifies setter as different, expect TypeError",
      body: function () {
        var propertyName = "foo25";
        var o = helpers.getDummyObject();
        var pd = { set: function(arg) { helpers.writeln("old setter"); this.Value = arg; } };
        Object.defineProperty(o, propertyName, pd);

        pd = { set: function(arg) { helpers.writeln("new setter"); } };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    test26: {
      name: "8.12.9.11.a.ii: re-define accessor property: current configurable is false, descriptor specifies getter as different, expect TypeError",
      body: function () {
        var propertyName = "foo26";
        var o = helpers.getDummyObject();
        var pd = { get: function() { return this.Value; }, };
        Object.defineProperty(o, propertyName, pd);

        pd = { get: function() { helpers.writeln("new getter"); return 2; } };
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    test27: {
      name: "8.12.9.11 (variation 2): re-define accessor property: current configurable is true and no getter, descriptor specifies getter as undefined, setter as same",
      body: function () {
        var propertyName = "foo27";
        var o = helpers.getDummyObject();
        var setter = function(arg) { helpers.writeln("setter") };
        var pd = { set: setter };
        Object.defineProperty(o, propertyName, pd);

        pd = { get: undefined, set: setter };
        Object.defineProperty(o, propertyName, pd);
        var expected = { get: undefined, set: setter, configurable: false, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
      }
    },

    test28: {
      name: "Re-define property from data to accessor property. Make sure that setter is called when setting the value.",
      body: function () {
        // define a data property.
        var propertyName = "foo28";
        var o = helpers.getDummyObject();
        var pd = { value: 1, configurable: true };
        Object.defineProperty(o, propertyName, pd);

        // re-define the property to be accessor property.
        var log = "";
        var getter = function() { log += "getter was called."; return this.Value; }
        var setter = function(arg) { log += "setter was called."; this.Value = arg; };
        pd = { get: getter, set: setter };
        Object.defineProperty(o, propertyName, pd);

        // set the value and get it.
        var newValue = 2;
        o[propertyName] = newValue;
        var actualValue = o[propertyName];

        // validate.
        var expected = { get: getter, set: setter, configurable: true, enumerable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        assert.areEqual("setter was called.getter was called.", log, "wrong log");
        assert.areEqual(newValue, actualValue, "wrong value");
      }
    },

    test29: {
      name: "Define property 'length' as accessor property on array: check that it throws TypeError.",
      body: function () {
        assert.throws(
          function() { Object.defineProperty([], "length", {configurable: false, get: function() {return 2;}}); },
          TypeError);
        assert.throws(
          function() { Object.defineProperty(Array.prototype, "length", {configurable: false, get: function() {return 2;}}); },
          TypeError);
      }
    },

    // Where we are: some tests for specific issues.
    test30: {
      name: "Define property with getter specified as undefined, then access the property (WOOB bug 1123281)",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo30";
        var pd = { get: undefined };
        Object.defineProperty(o, propertyName, pd);
        assert.areEqual(undefined, o[propertyName]);
      }
    },

    test31: {
      name: "Define property with setter specified as undefined, then set the property (WOOB bug 1123281)",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo31";
        var pd = { set: undefined };
        Object.defineProperty(o, propertyName, pd);
        o[propertyName] = 1; // Make sure this does not throw.
        assert.areEqual(undefined, o[propertyName]); // Just in case try to access the property.
      }
    },

    test32: {
      name: "Convert data to accessor property with getter specified as undefined, then access the property (WOOB bug 1123281)",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo32";
        var pd = { configurable: true, value: 0 };
        Object.defineProperty(o, propertyName, pd);

        pd = { get: undefined };
        Object.defineProperty(o, propertyName, pd);
        assert.areEqual(undefined, o[propertyName]);
      }
    },

    test33: {
      name: "Convert data to accessor property with setter specified as undefined, then set the property (WOOB bug 1123281)",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "foo33";
        var pd = { configurable: true, value: 0 };
        Object.defineProperty(o, propertyName, pd);

        pd = { set: undefined };
        Object.defineProperty(o, propertyName, pd);
        o[propertyName] = 1; // Make sure this does not throw.
        assert.areEqual(undefined, o[propertyName]); // Just in case try to access the property.
      }
    },

    // Note: this test irreversibly changes the dummy object (that's important when dummy object is document/window),
    //       it should in the very end.
    test34: {
      name: "8.12.9.3: define property for non-extensible object, check that it throws TypeError",
      body: function () {
        var o = helpers.getDummyObject();
        Object.preventExtensions(o);
        var propertyName = "foo01";
        var pd = {};
        assert.throws(function() { Object.defineProperty(o, propertyName, pd); }, TypeError);
      }
    },

    //---------------------- numeric property names -------------------------------
    test_101: {
        name: "8.12.9.4.a (variation 1): define generic property, check default attrbitues",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "2";
            var pd = {};
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: undefined, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_102: {
        name: "8.12.9.4.a (variation 2): define data property, check default attrbitues",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "3";
            var pd = { value: 0 };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 0, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_103: {
        name: "8.12.9.4.a (variation 3): define generic property by specifying some attributes, check attrbitues",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "2";
            var pd = { configurable: true, writable: false };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: undefined, configurable: true, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_104: {
        name: "8.12.9.4.b: define accessor property, check default attrbitues",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "4";
            var getter = function () { return this.Value };
            var pd = { get: getter };
            Object.defineProperty(o, propertyName, pd);
            var expected = { get: getter, set: undefined, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_105: {
        name: "8.12.9.5: re-define property: use descriptor with all fields absent, check that nothing happens to previous descriptor",
        body: function () {
            var propertyName = "5";
            var o = { 5: 1 };
            var pd = {};
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: true, value: 1, configurable: true, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_106: {
        name: "8.12.9.6: re-define property: use equal descriptor with data field, check that nothing happens to previous descriptor",
        body: function () {
            var propertyName = "6";
            var o = { 6: 1 };
            var pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: true, value: 1, configurable: true, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same

    test_107: {
        name: "8.12.9.7.a: re-define property: current descriptor is not configurable and descriptor is configurable, check that it throws TypeError",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "7";
            var pd = { value: 0, configurable: false };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1, configurable: true };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    test_108: {
        name: "8.12.9.7.b (variation 1): re-define property: current descriptor is not configurable and descriptor enumerable is specified and it's negation of current enumerable, check that it throws TypeError",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "8";
            var pd = { value: 0 };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1, enumerable: true };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    test_109: {
        name: "8.12.9.7.b (variation 2): re-define property: current descriptor is not configurable and descriptor enumerable is not specified, check that it does not throw",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "9";
            var pd = { value: 0, writable: true }; // set writable to true to avoid throw code path.
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1, writable: false };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 1, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_110: {
        name: "8.12.9.7.b (variation 3): re-define property: current descriptor is not configurable and descriptor enumerable is same as current enumerable, check that it does not throw",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "10";
            var pd = { value: 0, writable: true }; // set writable to true to avoid throw code path.
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1, enumerable: false, writable: false };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 1, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_111: {
        name: "8.12.9.8: re-define property: descriptor is not empty, generic and is different from current",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "11";
            var pd = { value: 0, configurable: true };
            Object.defineProperty(o, propertyName, pd);
            pd = { enumerable: true }; // change enumerable to make sure that descriptor is different from current.
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 0, configurable: true, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same
    // - descriptor.IsData != current.IsData

    test_112: {
        name: "8.12.9.9.a: re-define property: descriptor.IsData != current.IsData and current is not configurable, check that it throws TypeError",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "12";
            var pd = { value: 0, configurable: false };
            Object.defineProperty(o, propertyName, pd);
            pd = { get: function () { return this.Value; } };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    test_113: {
        name: "8.12.9.9.b (variation 1): re-define property: convert from data to accessor descriptor, check that configurable/enumerable (true) are preserved",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "13";
            var pd = { value: 0, configurable: true, enumerable: true };
            Object.defineProperty(o, propertyName, pd);
            var getter = function () { return this.Value; };
            pd = { get: getter };
            Object.defineProperty(o, propertyName, pd);
            var expected = { get: getter, set: undefined, configurable: true, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_114: {
        name: "8.12.9.9.b (variation 2): re-define property: convert from data to accessor descriptor, check that enumerable (false) is preserved",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "14";
            var pd = { value: 0, configurable: true, enumerable: false };
            Object.defineProperty(o, propertyName, pd);
            var getter = function () { return this.Value; };
            pd = { get: getter };
            Object.defineProperty(o, propertyName, pd);
            var expected = { get: getter, set: undefined, configurable: true, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_115: {
        name: "8.12.9.9.b (variation 3): re-define property: convert from data to accessor descriptor, check that configurable/enumerable not preserved when specified by descriptor",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "15";
            var pd = { value: 0, configurable: true, enumerable: true };
            Object.defineProperty(o, propertyName, pd);
            var getter = function () { return this.Value; };
            pd = { get: getter, configurable: false };
            Object.defineProperty(o, propertyName, pd);
            var expected = { get: getter, set: undefined, configurable: false, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_116: {
        name: "8.12.9.9.c (variation 1): re-define property: convert from accessor to data descriptor, check that configurable/enumerable (true) are preserved",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "16";
            var pd = {
                set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
                configurable: true,
                enumerable: true
            };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 1, configurable: true, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_117: {
        name: "8.12.9.9.c (variation 2): re-define property: convert from accessor to data descriptor, check that enumerable (false) is preserved",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "17";
            var pd = {
                set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
                configurable: true,
                enumerable: false
            };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 1, configurable: true, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_118: {
        name: "8.12.9.9.c (variation 3): re-define property: convert from accessor to data descriptor, check that configurable/enumerable (true/false) not preserved when specified by descriptor (false/absent)",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "18";
            var pd = {
                set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
                configurable: true,
                enumerable: false
            };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1, configurable: false };
            Object.defineProperty(o, propertyName, pd);
            // expected: configurable/enumerable = false/false.
            var expected = { writable: false, value: 1, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_119: {
        name: "8.12.9.9.c (variation 4): re-define property: convert from accessor to data descriptor, check that configurable/enumerable (true/true) not preserved when specified by descriptor (absent/false)",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "19";
            var pd = {
                set: function (arg) { helpers.writeln("setter was called"); this.Value = arg; },
                configurable: true,
                enumerable: true
            };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1, enumerable: false };
            Object.defineProperty(o, propertyName, pd);
            // expected: configurable/enumerable = true/false.
            var expected = { writable: false, value: 1, configurable: true, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same
    // - descriptor is data, current is data

    test_120: {
        name: "8.12.9.10.a (variation 1): re-define data property: current is not configurable/not writable and descriptor writable is absent/value is same",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "20";
            var pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 1, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_121: {
        name: "8.12.9.10.a.i: re-define data property: current is not configurable/not writable and descriptor is writable, check that it throws TypeError",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "21";
            var pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 2, writable: true };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
            return true;
        }
    },

    test_122: {
        name: "8.12.9.10.a.ii: re-define data property: current is not configurable/not writable and descriptor writable is false and value is different, check that it throws TypeError",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "22";
            var pd = { value: 1 };
            Object.defineProperty(o, propertyName, pd);
            pd = { value: 2, writable: false };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    test_123: {
        name: "8.12.9.10.a (variation 2): re-define data property: current is configurable",
        body: function () {
            var propertyName = "23";
            var o = { 23: 1 };
            var pd = { value: 2, writable: false };
            Object.defineProperty(o, propertyName, pd);
            var expected = { writable: false, value: 2, configurable: true, enumerable: true };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    // where we are:
    // - re-define
    // - desc is not empty
    // - desc and current are not the same
    // - descriptor is accessor, current is accessor

    test_124: {
        name: "Test: 8.12.9.11 (variation 1): re-define accessor property: current configurable is true: valid case",
        body: function () {
          var propertyName = "24";
          var o = {
            get 24() { return this.Value; },
            set 24(arg) { helpers.writeln("old setter"); this.Value = arg; }
          };
          var newGetter = function() { return 2; };
          var newSetter = function(arg) { helpers.writeln("new setter"); }
          var pd = { get: newGetter, set: newSetter };
          Object.defineProperty(o, propertyName, pd);
          var expected = { get: newGetter,  set: newSetter, configurable: true, enumerable: true };
          assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_125: {
        name: "8.12.9.11.a.i: re-define accessor property: current configurable is false, descriptor specifies setter as different, expect TypeError",
        body: function () {
            var propertyName = "25";
            var o = helpers.getDummyObject();
            var pd = { set: function (arg) { helpers.writeln("old setter"); this.Value = arg; } };
            Object.defineProperty(o, propertyName, pd);

            pd = { set: function (arg) { helpers.writeln("new setter"); } };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    test_126: {
        name: "8.12.9.11.a.ii: re-define accessor property: current configurable is false, descriptor specifies getter as different, expect TypeError",
        body: function () {
            var propertyName = "26";
            var o = helpers.getDummyObject();
            var pd = { get: function () { return this.Value; } };
            Object.defineProperty(o, propertyName, pd);

            pd = { get: function () { helpers.writeln("new getter"); return 2; } };
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    test_127: {
        name: "8.12.9.11 (variation 2): re-define accessor property: current configurable is true and no getter, descriptor specifies getter as undefined, setter as same",
        body: function () {
            var propertyName = "27";
            var o = helpers.getDummyObject();
            var setter = function (arg) { helpers.writeln("setter") };
            var pd = { set: setter };
            Object.defineProperty(o, propertyName, pd);

            pd = { get: undefined, set: setter };
            Object.defineProperty(o, propertyName, pd);
            var expected = { get: undefined, set: setter, configurable: false, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        }
    },

    test_128: {
        name: "Re-define property from data to accessor property. Make sure that setter is called when setting the value.",
        body: function () {
            // define a data property.
            var propertyName = "28";
            var o = helpers.getDummyObject();
            var pd = { value: 1, configurable: true };
            Object.defineProperty(o, propertyName, pd);

            // re-define the property to be accessor property.
            var log = "";
            var getter = function () { log += "getter was called."; return this.Value; }
            var setter = function (arg) { log += "setter was called."; this.Value = arg; };
            pd = { get: getter, set: setter };
            Object.defineProperty(o, propertyName, pd);

            // set the value and get it.
            var newValue = 2;
            o[propertyName] = newValue;
            var actualValue = o[propertyName];

            // validate.
            var expected = { get: getter, set: setter, configurable: true, enumerable: false };
            assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
            assert.areEqual("setter was called.getter was called.", log, "wrong log");
            assert.areEqual(newValue, actualValue, "wrong value");
        }
    },

    test_129: {
        name: "Define property 'length' as accessor property on array: check that it throws TypeError.",
        body: function () {
            assert.throws(
        function () { Object.defineProperty([], "length", { configurable: false, get: function () { return 2; } }); },
        TypeError);
            assert.throws(
        function () { Object.defineProperty(Array.prototype, "length", { configurable: false, get: function () { return 2; } }); },
        TypeError);
        }
    },

    // Where we are: some tests for specific issues.
    test_130: {
        name: "Define property with getter specified as undefined, then access the property (WOOB bug 1123281)",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "30";
            var pd = { get: undefined };
            Object.defineProperty(o, propertyName, pd);
            assert.areEqual(undefined, o[propertyName]);
        }
    },

    test_131: {
        name: "Define property with setter specified as undefined, then set the property (WOOB bug 1123281)",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "31";
            var pd = { set: undefined };
            Object.defineProperty(o, propertyName, pd);
            o[propertyName] = 1; // Make sure this does not throw.
            assert.areEqual(undefined, o[propertyName]); // Just in case try to access the property.
        }
    },

    test_132: {
        name: "Convert data to accessor property with getter specified as undefined, then access the property (WOOB bug 1123281)",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "32";
            var pd = { configurable: true, value: 0 };
            Object.defineProperty(o, propertyName, pd);

            pd = { get: undefined };
            Object.defineProperty(o, propertyName, pd);
            assert.areEqual(undefined, o[propertyName]);
        }
    },

    test_133: {
        name: "Convert data to accessor property with setter specified as undefined, then set the property (WOOB bug 1123281)",
        body: function () {
            var o = helpers.getDummyObject();
            var propertyName = "33";
            var pd = { configurable: true, value: 0 };
            Object.defineProperty(o, propertyName, pd);

            pd = { set: undefined };
            Object.defineProperty(o, propertyName, pd);
            o[propertyName] = 1; // Make sure this does not throw.
            assert.areEqual(undefined, o[propertyName]); // Just in case try to access the property.
        }
    },

    // Note: this test irreversibly changes the dummy object (that's important when dummy object is document/window),
    //       it should in the very end.
    test_134: {
        name: "8.12.9.3: define property for non-extensible object, check that it throws TypeError",
        body: function () {
            var o = helpers.getDummyObject();
            Object.preventExtensions(o);
            var propertyName = "1";
            var pd = {};
            assert.throws(function () { Object.defineProperty(o, propertyName, pd); }, TypeError);
        }
    },

    // --------------------- misc adhoc tests -------------------------------------
    test_301: {
        name: "set property whose writable is false",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 17;
            o[propName] = 100;
            Object.defineProperty(o, propName, {writable: false});
            o[propName] = 200; // should have no effect
            assert.areEqual(100, o[propName]);
        }
    },

    test_302: {
        name: "delete index property",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 123;

            assert.areEqual(true, delete o[1], "delete non-exist property should return true");

            o[propName] = 123;
            assert.areEqual(true, delete o[propName], "delete this property should return true");
            assert.areEqual(undefined, o[propName], "deleted property value should become undefined");

            Object.defineProperty(o, propName, {get:function(){return 123;}, configurable: true});
            assert.areEqual(123, o[propName], "Property value should be from getter");
            assert.areEqual(true, delete o[propName], "delete this property should return true");
            assert.areEqual(undefined, o[propName], "deleted property value should become undefined");

            Object.defineProperty(o, propName, {value: 123, configurable: false});
            assert.areEqual(123, o[propName], "Property value should be the value");
            assert.areEqual(false, delete o[propName], "delete this property should return false, not configurable");
            assert.areEqual(123, o[propName], "Property value should not be changed");
        }
    },

    test_303: {
        name: "delete a data property then set",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 303;
            Object.defineProperty(o, propName, {
                value: 100,
                configurable: true
            });
            assert.areEqual(delete o[propName], true, "delete should succeed on configurable data property");
            o[propName] = 200;
            assert.areEqual(200, o[propName]);
        }
    },
    test_304: {
        name: "delete a getter property then set",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 303;
            Object.defineProperty(o, propName, {
                get: function() { return 100; },
                configurable: true
            });
            assert.areEqual(true, delete o[propName], "delete should succeed on configurable accessor property");
            o[propName] = 200;
            assert.areEqual(200, o[propName]);
        }
    },
    test_305: {
        name: "delete a setter property then set",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 303;
            Object.defineProperty(o, propName, {
                set: function(arg) { return 100; },
                configurable: true
            });
            assert.areEqual(true, delete o[propName], "delete should succeed on configurable accessor property");
            o[propName] = 200;
            assert.areEqual(200, o[propName]);
        }
    },

    test_306: {
        name: "Set a property while prototype has a getter",
        body: function() {
            var propName = "abc";
            try {
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = helpers.getDummyObject();
                o[propName] = 200; // should have no effect since proto only has a getter
                assert.areEqual(100, o[propName]);
            } finally {
                delete Object.prototype[propName];
            }
        }
    },
    test_306_i: {
        name: "Set a property while prototype has a getter",
        body: function() {
            var propName = "306"; // Without quote it fails on array. Covered by 310_i
            try {
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = helpers.getDummyObject();
                o[propName] = 200; // should have no effect since proto only has a getter
                assert.areEqual(100, o[propName]);
            } finally {
                delete Object.prototype[propName];
            }
        }
    },

    test_307: {
        name: "Define a property while prototype has a getter",
        body: function() {
            var propName = "abc";
            try {
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = helpers.getDummyObject();
                Object.defineProperty(o, propName, { value: 200 });
                assert.areEqual(200, o[propName]); //DefineOwnProperty succeeds
            } finally {
                delete Object.prototype[propName];
            }
        }
    },
    test_307_i: {
        name: "Define a property while prototype has a getter",
        body: function() {
            var propName = 307;
            try {
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = helpers.getDummyObject();
                Object.defineProperty(o, propName, { value: 200 });
                assert.areEqual(200, o[propName]); //DefineOwnProperty succeeds
            } finally {
                delete Object.prototype[propName];
            }
        }
    },

    test_308: {
        disabled: true, // !!! Disable due to bug (to be opened) causing assertion !!!
        pass: "misc",
        name: "Set a property via object literal while prototype has a getter",
        body: function() {
            var propName = "abc";
            try {
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = {abc: 200};
                assert.areEqual(100, o[propName]);
            } finally {
                delete Object.prototype[propName];
            }
        }
    },
    test_308_i: {
        pass: "misc",
        name: "Set a property via object literal while prototype has a getter",
        body: function() {
            var propName = 308;
            try {
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = {308: 200}; // succeeds since object literals do not check prototypes
                assert.areEqual(200, o[propName]);
            } finally {
                delete Object.prototype[propName];
            }
        }
    },

    test_309_i: {
        disabled: true, // !!! Disabled, Array doesn't honor prototype element attribute/getter/setter !!!
        pass: "misc",
        name: "Set a property while prototype property is not writable",
        body: function() {
            try {
                Object.defineProperty(Object.prototype, 1, {
                    value: 100,
                    writable: false,
                    configurable: true
                });

                var o = [];
                assert.areEqual(100, o[1]);
                o[1] = 200; // should have no effect since proto[1] is not writable
                assert.areEqual(100, o[1]);
            } finally {
                delete Object.prototype[1];
            }
        }
    },

    test_310_i: {
        disabled: true, // !!! Disabled, Array doesn't honor prototype element attribute/getter/setter !!!
        pass: "misc",
        name: "Set a property while prototype property is a getter",
        body: function() {
            try {
                Object.defineProperty(Object.prototype, 1, {
                    get: function () { return 100; },
                    configurable: true
                });

                var o = [];
                assert.areEqual(100, o[1]);
                o[1] = 200; // should have no effect since proto[1] has only getter
                assert.areEqual(100, o[1]);
            } finally {
                delete Object.prototype[1];
            }
        }
    },

    test_311_i: {
        disabled: true, // !!! Disabled, Array doesn't honor prototype element attribute/getter/setter !!!
        pass: "misc",
        name: "Set a property while prototype property is getter/setter",
        body: function() {
            try {
                var tmp = 100;
                Object.defineProperty(Object.prototype, 1, {
                    get: function () { return tmp; },
                    set: function(arg) { tmp = arg + 300; },
                    configurable: true
                });

                var o = [];
                assert.areEqual(100, o[1]);
                o[1] = 200; // should call setter
                assert.areEqual(500, o[1]);
            } finally {
                delete Object.prototype[1];
            }
        }
    },

    test_312_i: {
        name: "Test getter/setter on prototype receives the right this arg",
        body: function() {
            try {
                var propName = "1"; //avoid array fast path for now
                Object.prototype.tmp = 123;
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return this.tmp; },
                    set: function (arg) { this.tmp = arg + 300; },
                    configurable: true
                });

                var o = helpers.getDummyObject();
                assert.areEqual(123, o[propName], "Should read data on prototype");
                o[propName] = 200; // should call proto setter on o
                assert.areEqual(500, o.tmp, "setter should set data on o");
                assert.areEqual(500, o[propName], "Should read data on o");
                assert.areEqual(123, Object.prototype.tmp, "proto data unchanged");
            } finally {
                delete Object.prototype[propName];
                delete Object.prototype.tmp;
            }
        }
    },
    test_312a_i: {
        name: "Test getter on prototype receives the right this arg",
        body: function() {
            try {
                var propName = "1"; //avoid array fast path for now
                Object.prototype.tmp = 123;
                Object.defineProperty(Object.prototype, propName, {
                    get: function () { return this.tmp; },
                    configurable: true
                });

                var o = helpers.getDummyObject();
                o.length = 10; // Makes propName in length range, also prepare for indexOf
                assert.areEqual(123, o[propName], "Should read data on prototype");
                o.tmp = 456;
                assert.areEqual(456, o[propName], "Should read data on o");
                var i = Array.prototype.indexOf.apply(o, [456]);
                assert.areEqual(propName, i.toString(), "getter should find data on o, not on prototype!");
            } finally {
                delete Object.prototype[propName];
                delete Object.prototype.tmp;
            }
        }
    },

    test_313_i: {
        name: "preventExtensions with index property",
        body: function() {
            var o = helpers.getDummyObject();
            o[1] = 1;
            assert.areEqual(1, o[1]);
            assert.areEqual(true, Object.isExtensible(o), "default is extensible");
            assert.areEqual(false, Object.isSealed(o), "default not sealed");
            assert.areEqual(false, Object.isFrozen(o), "default not frozen");

            Object.preventExtensions(o);
            assert.areEqual(false, Object.isExtensible(o), "now NOT extensible");
            assert.areEqual(false, Object.isSealed(o), "still not sealed, o[1] configurable");
            assert.areEqual(false, Object.isFrozen(o), "still not frozen, o[1] configurable");

            o[1] = 11; // should succeed
            assert.areEqual(11, o[1], "write should succeed");
            o[2] = 2; // should fail
            assert.areEqual(undefined, o[2], "extend should fail");

            // verify unchanged
            assert.areEqual(false, Object.isExtensible(o), "extensible not changed");
            assert.areEqual(false, Object.isSealed(o), "sealed not changed");
            assert.areEqual(false, Object.isFrozen(o), "frozen not changed");
        }
    },
    test_314_i: {
        name: "seal with index property",
        body: function() {
            var o = helpers.getDummyObject();
            o[1] = 1;

            Object.seal(o);
            assert.areEqual(false, Object.isExtensible(o), "now NOT extensible");
            assert.areEqual(true, Object.isSealed(o), "now IS sealed");
            assert.areEqual(false, Object.isFrozen(o), "still not frozen, o[1] writable");

            o[1] = 11; // should succeed
            assert.areEqual(11, o[1], "write should succeed");
            assert.areEqual(false, delete o[1], "delete should fail, object sealed");
            assert.areEqual(11, o[1], "delete should fail");
            o[2] = 2; // should fail
            assert.areEqual(undefined, o[2], "extend should fail");

            // verify unchanged
            assert.areEqual(false, Object.isExtensible(o), "extensible not changed");
            assert.areEqual(true, Object.isSealed(o), "sealed not changed");
            assert.areEqual(false, Object.isFrozen(o), "frozen not changed");
        }
    },
    test_315_i: {
        name: "freeze with index property",
        body: function() {
            var o = helpers.getDummyObject();
            o[1] = 1;

            Object.freeze(o);
            assert.areEqual(false, Object.isExtensible(o), "now NOT extensible");
            assert.areEqual(true, Object.isSealed(o), "now IS sealed");
            assert.areEqual(true, Object.isFrozen(o), "now IS frozen");

            o[1] = 11; // should fail
            assert.areEqual(1, o[1], "write should fail");
            assert.areEqual(false, delete o[1], "delete should fail, object sealed");
            assert.areEqual(1, o[1], "delete should fail");
            o[2] = 2; // should fail
            assert.areEqual(undefined, o[2], "extend should fail");

            // verify unchanged
            assert.areEqual(false, Object.isExtensible(o), "extensible not changed");
            assert.areEqual(true, Object.isSealed(o), "sealed not changed");
            assert.areEqual(true, Object.isFrozen(o), "frozen not changed");
        }
    },

    test_316_i: {
        name: "preventExtensions on empty object -> isSealed and isFrozen",
        body: function() {
            var o = helpers.getDummyObject();
            Object.preventExtensions(o); // Haven't set any item yet, objectArray is null
            assert.areEqual(false, Object.isExtensible(o), "NOT extensible");
            assert.areEqual(true, Object.isSealed(o), "IS sealed");
            assert.areEqual(true, Object.isFrozen(o) || Array.isArray(o), "IS frozen, unless isArray (length writable)");

            o[1] = 11;
            assert.areEqual(undefined, o[1], "Write failed, not extensible");
        }
    },
    test_317_i: {
        name: "preventExtensions on object with an accessor -> isSealed and isFrozen",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 123;
            Object.defineProperty(o, propName, {get: function(){ return "123"; }, configurable: false});
            Object.preventExtensions(o);
            assert.areEqual(false, Object.isExtensible(o), "NOT extensible");
            assert.areEqual(true, Object.isSealed(o), "IS sealed");
            assert.areEqual(true, Object.isFrozen(o) || Array.isArray(o), "IS frozen, unless isArray (length writable)");

            o[1] = 11;
            assert.areEqual(undefined, o[1], "Write failed, not extensible");
            assert.areEqual(false, delete o[propName], "delete should fail, not configurable");
            assert.areEqual("123", o[propName], "delete failed, not configurable");
        }
    },
    test_318_i: {
        name: "preventExtensions on object with data -> isSealed and isFrozen",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 123;
            Object.defineProperty(o, propName, {value: 456, configurable: false, writable: true});
            Object.preventExtensions(o);
            assert.areEqual(false, Object.isExtensible(o), "NOT extensible");
            assert.areEqual(true, Object.isSealed(o), "IS sealed");
            assert.areEqual(false, Object.isFrozen(o), "NOT frozen, data writable");
        }
    },
    test_319_i: {
        name: "preventExtensions on object with data -> isSealed and isFrozen",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 123;
            Object.defineProperty(o, propName, {value: 456, configurable: false, writable: false});
            Object.preventExtensions(o);
            assert.areEqual(false, Object.isExtensible(o), "NOT extensible");
            assert.areEqual(true, Object.isSealed(o), "IS sealed");
            assert.areEqual(true, Object.isFrozen(o) || Array.isArray(o), "IS frozen, unless isArray (length writable)");
        }
    },
    test_320_i: {
        name: "preventExtensions on object with data -> isSealed and isFrozen",
        body: function() {
            var o = helpers.getDummyObject();
            var propName = 123;
            Object.defineProperty(o, propName, {value: 456, configurable: false, writable: false});
            o[234] = 345;
            Object.preventExtensions(o);
            assert.areEqual(false, Object.isExtensible(o), "NOT extensible");
            assert.areEqual(false, Object.isSealed(o), "NOT sealed, 234 configurable");
            assert.areEqual(false, Object.isFrozen(o), "NOT frozen, 234 configurable/writable");
        }
    },

    test_321_i: {
        name: "Test prototype value is used in sort",
        body: function() {
            try {
                var propName = 1;
                Object.defineProperty(Array.prototype, propName, {
                    value: 321,
                    writable: true, configurable: true, enumerable: true
                });

                var o = helpers.getDummyObject();
                o[0] = 10;
                o.length = 3;
                o.sort = Array.prototype.sort;
                o.join = Array.prototype.join;
                o.toString = Array.prototype.toString;

                o.sort();
                assert.areEqual("10,321,", o.toString(), "sort result mismatch?");
            } finally {
                delete Array.prototype[propName];
            }
        }
    },

    test_322_i: {
      name: "Convert accessor to a data property for non-extensible object (WIN8 bug 463559) but for numeric property",
      body: function () {
        var o = helpers.getDummyObject();
        var propertyName = "1";

        Object.defineProperty(o, propertyName, {
          get: function() { return 0; },
          set: function(val) { helpers.writeln("setter was called although it shouldn't"); },
          configurable: true
        });
        Object.preventExtensions(o);
        var val = 1;
        Object.defineProperty(o, propertyName, { value: val, });

        var expected = { value: val, configurable: true, enumerable: false, writable: false };
        assert.areEqual(expected, Object.getOwnPropertyDescriptor(o, propertyName), "wrong value of getOwnPropertyDescriptor");
        assert.areEqual(val, o[propertyName], "the property value is wrong");
        assert.areEqual(false, Object.isExtensible(o), "isExtensible() changed");
      }
    },
};  // tests.

var passes = {
    pass1: {
        name: "obj",
        prep: function() {
            helpers.getDummyObject = function() {
                return {}; // a normal object
            };
        }
    },

    pass2: {
        name: "arr",
        prep: function() {
            helpers.getDummyObject = function() {
                return []; // a normal array
            };
        }
    },

    pass3: {
        name: "es5arr",
        prep: function() {
            helpers.getDummyObject = function() {
                var arr = [];
                Object.defineProperty(arr, "12345", {
                    get: function() {
                        helpers.writeln("dummy called");
                    },
                    configurable: true
                });
                delete arr[12345];
                return arr; // an ES5 array
            };
        }
    },

    pass4: {
        name: "misc",
        runonce: true, // Run misc tests only once
        prep: function() {
            helpers.getDummyObject = function() {
                return null; // Misc tests do not use helpers.getDummyObject
            };
        }
    }

};

testRunner.runTests(passes, tests);
