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

// ES7 SharedArrayBuffer tests

WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");

var typedArrayList = [
    Int8Array,
    Uint8Array,
    Int16Array,
    Uint16Array,
    Int32Array,
    Uint32Array,
    Uint8ClampedArray,
    Float32Array,
    Float64Array
];

var tests = [{
		name : "SharedArrayBuffer sanity validation",
		body : function () {
			assert.doesNotThrow(() => new SharedArrayBuffer(1), "SharedArrayBuffer is valid type");
			assert.areEqual(typeof SharedArrayBuffer.prototype, "object", "SharedArrayBuffer has prototype object");

			assert.throws(() => SharedArrayBuffer(1), TypeError, "Calling SharedArrayBuffer without 'new' is an error", "SharedArrayBuffer: cannot be called without the new keyword");
			var sab = new SharedArrayBuffer(1);
			assert.areEqual(sab.byteLength, 1, "SharedArrayBuffer's object has byteLength property");
			assert.areEqual(sab.constructor, SharedArrayBuffer, "SharedArrayBuffer's object has constructor property");
			assert.areEqual(sab.slice, SharedArrayBuffer.prototype.slice, "SharedArrayBuffer's object has slice property");
			assert.areEqual(typeof sab.slice, "function", "slice is function");
			assert.areEqual(sab.slice.length, 2, "SharedArrayBuffer.prototype.slice.length == 2");
			assert.areEqual(String(sab), "[object SharedArrayBuffer]", "Calling toString on SharedArrayBuffer's object will return '[object SharedArrayBuffer]'");

			assert.doesNotThrow(() => new SharedArrayBuffer(), "Calling SharedArrayBuffer without any arguments is valid syntax");
		}
	},
    {
        name: "SharedArrayBuffer.prototype.slice behavior with non-SharedArrayBuffer parameters",
        body: function() {
            assert.throws(function () { SharedArrayBuffer.prototype.slice.apply('string'); }, TypeError, "SharedArrayBuffer.prototype.slice throws TypeError if this parameter is not an SharedArrayBuffer", "SharedArrayBuffer object expected");
            assert.throws(function () { SharedArrayBuffer.prototype.slice.apply(); }, TypeError, "SharedArrayBuffer.prototype.slice throws TypeError if there is no this parameter", "SharedArrayBuffer object expected");
            assert.throws(function () { SharedArrayBuffer.prototype.slice.call(); }, TypeError, "SharedArrayBuffer.prototype.slice throws TypeError if it is called directly", "SharedArrayBuffer object expected");
            assert.throws(function () { SharedArrayBuffer.prototype.slice.call(undefined); }, TypeError, "SharedArrayBuffer.prototype.slice throws TypeError if it is called directly", "SharedArrayBuffer object expected");
            assert.throws(function () { SharedArrayBuffer.prototype.slice(); }, TypeError, "SharedArrayBuffer.prototype.slice throws TypeError if it is called directly", "SharedArrayBuffer object expected");
            assert.throws(function () { SharedArrayBuffer.prototype.slice(undefined); }, TypeError, "SharedArrayBuffer.prototype.slice throws TypeError if it is called directly", "SharedArrayBuffer object expected");
        }
    },
    {
		name : "Validating SharedArrayBuffer with different size",
		body : function () {
			function validate(size, expectedSize) {
				var sab = new SharedArrayBuffer(size);
				assert.areEqual(sab.byteLength, expectedSize, "byteLength should be " + expectedSize);
			}
			validate(0, 0);
			validate(1024, 1024);
			validate(false, 0);
			validate(undefined, 0);
			validate(NaN, 0);
			validate("16", 16);
			validate("hello", 0);
			validate(1.1, 1);
			validate({ valueOf : () => 10 }, 10);
			validate({ toString : () => 10 }, 10);

			assert.doesNotThrow(() => new SharedArrayBuffer(1, 2, 3), "Calling SharedArrayBuffer with more than one arguments is a valid syntax");
			assert.throws(() => new SharedArrayBuffer(-1), RangeError, "Calling SharedArrayBuffer with negative size is an error", "Array length must be a finite positive integer");
		}
	},
    {
		name : "Validating SharedArrayBuffer's byteLength does not change",
		body : function () {
			var sab = new SharedArrayBuffer(1);
			sab.byteLength = 12;
			assert.areEqual(sab.byteLength, 1, "Explicit change to byteLength does not modify the byteLength");
		}
	},
    {
		name : "TypedArray on SharedArrayBuffer",
		body : function () {
            function validate(constructor, elements, range) {
                var sab = new SharedArrayBuffer(elements*constructor.BYTES_PER_ELEMENT);
                    var view = new constructor(sab);
                    assert.areEqual(view.length, elements, constructor.name + " with length " + elements);
                    assert.areEqual(view.buffer, sab, constructor.name + " with buffer");
                    assert.areEqual(view.byteOffset, 0, constructor.name + " with byteOffset " + view.byteOffset);
                    assert.areEqual(view.byteLength, elements*constructor.BYTES_PER_ELEMENT, constructor.name + " with byteLength " + elements*constructor.BYTES_PER_ELEMENT);
                
                for ([offset, len] of range) {
                    var v = new constructor(sab, offset*constructor.BYTES_PER_ELEMENT, len);
                    assert.areEqual(v.length, len, constructor.name + " with length " + len);
                    assert.areEqual(v.buffer, sab, constructor.name + " with buffer");
                    assert.areEqual(v.byteOffset, offset*constructor.BYTES_PER_ELEMENT, constructor.name + " with byteOffset " + view.byteOffset);
                    assert.areEqual(v.byteLength, len * constructor.BYTES_PER_ELEMENT, constructor.name + " with byteLength " + elements*constructor.BYTES_PER_ELEMENT);
                    if (len > 0) {
                        v[0] = 10;
                        assert.areEqual(v[0], view[offset], constructor.name);
                    }
                }
            };
            for (var i of typedArrayList) {
                validate(i, 8, [[0, 0], [0, 1], [6, 0], [6, 2], [0, 8]]);
            }
            
            function validateThrows(constructor, elements, range) {
                var sab = new SharedArrayBuffer(elements*constructor.BYTES_PER_ELEMENT);
                for ([offset, len] of range) {
                    assert.throws(() => new constructor(sab, offset*constructor.BYTES_PER_ELEMENT, len), RangeError);
                }
            }

            for (var i of typedArrayList) {
                validateThrows(i, 8, [[-1, 0], [20, 1], [6, -1], [6, 5]]);
            }
		}
	},
    {
		name : "TypedArray indices validation on SharedArrayBuffer",
		body : function () {
            function validate(constructor, elements, data) {
                var sab = new SharedArrayBuffer(elements*constructor.BYTES_PER_ELEMENT);
                var view = new constructor(sab);
                for ([index, test, expected] of data) {
                    view[index] = test;
                    assert.areEqual(view[index], expected, constructor.name);
                }
            };
            validate(Int8Array, 8, [[0, 10, 10], [7, 0x7F, 0x7F], [7, 0x80, -128], [1, 1000, -24]]);
            validate(Uint8Array, 8, [[0, 10, 10], [7, 0xFF, 0xFF], [7, -20, 236], [1, 1000, 232]]);
            validate(Int16Array, 8, [[0, 10, 10], [7, 0x7FFF, 0x7FFF], [7, 0x8000, -0x8000], [1, (0xFFFF + 1) *2 + 23, 23]]);
            validate(Uint16Array, 8, [[0, 10, 10], [7, 0xFFFF, 0xFFFF], [7, -120, 0x10000 - 120], [1, (0xFFFF + 1) *2 + 23, 23]]);
            validate(Int32Array, 8, [[0, 10, 10], [7, 0x7FFFFFFF, 0x7FFFFFFF], [7, 0x80000000, -0x80000000], [1, (0xFFFFFFFF + 1) *2 + 23, 23]]);
            validate(Uint32Array, 8, [[0, 10, 10], [7, 0xFFFFFFFF, 0xFFFFFFFF], [7, -20, 0xFFFFFFFF - 20 + 1], [1, (0xFFFFFFFF + 1) *2 + 23, 23]]);
            
            for (var i of typedArrayList) {
                validate(i, 8, [[-1, 20, undefined], ["-1", 20, undefined], [NaN, 20, undefined]]);
            }
		}
	},
    {
		name : "DataView functionality on SharedArrayBuffer",
		body : function () {
            
            function validate(dataView, setFunc, getFunc, offset, value) {
                dataView[setFunc](offset, value);
                assert.areEqual(value, dataView[getFunc](offset), "DataView "+ setFunc + " and " + getFunc + " validation");
            }

            [['setInt8', 'getInt8', 0x10], ['setUint8', 'getUint8', 0x9F], ['setInt16', 'getInt16', 0x6FFF],
            ['setUint16', 'getUint16', 0x9FFF], ['setInt32', 'getInt32', 0x6FFFFFFF], ['setUint32', 'getUint32', 0x9FFFFFFF],
            ['setFloat32', 'getFloat32', 2], ['setFloat64', 'getFloat64', 4]].forEach(function([setFunc, getFunc, v]) {
                [[0, 8], [8, 8]].forEach(function([byteOffset, byteLength]) {
                    var sab = new SharedArrayBuffer(16);
                    var dv = new DataView(sab, byteOffset, byteLength);
                    validate(dv, setFunc, getFunc, 0, v);
                });
            });
		}
	},
];

testRunner.runTests(tests, {
	verbose : WScript.Arguments[0] != "summary"
});
