<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL uniform array Conformance Tests</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<canvas id="example" width="2" height="2"> </canvas>
<script id="vshader" type="x-shader/x-vertex">
    attribute vec4 a_position;
    void main()
    {
        gl_Position = a_position;
    }
</script>

<script id="fshader" type="x-shader/x-fragment">
    precision mediump float;
    uniform $(type) color[3];
    void main()
    {
        gl_FragColor = vec4(color[0]$(elem), color[1]$(elem), color[2]$(elem), 1);
    }
</script>
<script id="vshader300" type="x-shader/x-vertex">
    #version 300 es
    in vec4 a_position;
    void main()
    {
        gl_Position = a_position;
    }
</script>

<script id="fshader300" type="x-shader/x-fragment">
    #version 300 es
    precision mediump float;
    uniform $(type) color[3];
    out vec4 o_FragColor;
    void main()
    {
        o_FragColor = vec4(color[0]$(elem), color[1]$(elem), color[2]$(elem), 1);
    }
</script>
<script>
"use strict";
description();
debug("");
// MaxInt32 is 2^32-1. We need +1 of that to test overflow conditions.
var MaxInt32PlusOne = 4294967296;
var wtu = WebGLTestUtils;
var gl = wtu.create3DContext("example");

const contextVersion = wtu.getDefault3DContextVersion();

let [vElemId, fElemId] = ["vshader", "fshader"];
if (contextVersion >= 2) {
  [vElemId, fElemId] = ["vshader300", "fshader300"];
}

var vSrc = wtu.getScript(vElemId).trim();
var fTemplate = wtu.getScript(fElemId).trim();

const typeInfos = [
  { type: 'float',
    jsTypeOf: 'number',
    setter: 'uniform1fv',
    elem: '',
    numSrcValues: 3,
    invalidSet: function(loc) {
      gl.uniform2fv(loc, [1, 2]);
    },
    srcValueAsString: function(index, srcValues) {
      return srcValues[index].toString();
    },
    returnValueAsString: function(value) {
      return value === null ? 'null' : value.toString();
    },
    checkType: function(value) {
      return typeof value === 'number';
    },
    checkValue: function(typeInfo, index, value) {
      return typeInfo.srcValues[index] == value;
    },
    srcValues: [16, 15, 14],
    srcValuesLess: [],
    srcValuesLessMultiple: [16],
    srcValuesMoreMultiple: [16, 15, 14, 13],
    srcValuesNonMultiple: null,
  },
  { type: 'vec2',
    jsTypeOf: 'Float32Array',
    setter: 'uniform2fv',
    elem: '[1]',
    numSrcValues: 3,
    invalidSet: function(loc) {
      gl.uniform1fv(loc, [2]);
    },
    illegalSet: function(loc) {
      gl.uniform1fv(loc, 2);
    },
    srcValueAsString: function(index, srcValues) {
      return "[" + srcValues[index * 2 + 0].toString() + ", " +
                   srcValues[index * 2 + 1].toString() + "]";
    },
    returnValueAsString: function(value) {
      return value === null ? 'null' : ("[" + value[0] + ", " + value[1] + "]");
    },
    checkType: function(value) {
      return value &&
             typeof value.length === 'number' &&
             value.length == 2;
    },
    checkValue: function(typeInfo, index, value) {
      return value !== null &&
             typeInfo.srcValues[index * 2 + 0] == value[0] &&
             typeInfo.srcValues[index * 2 + 1] == value[1];
    },
    srcValues: [16, 15, 14, 13, 12, 11],
    srcValuesLess: [16],
    srcValuesLessMultiple: [16, 15, 14, 13],
    srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
    srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10],
  },
  { type: 'vec3',
    jsTypeOf: 'Float32Array',
    setter: 'uniform3fv',
    elem: '[2]',
    numSrcValues: 3,
    invalidSet: function(loc) {
      gl.uniform1fv(loc, [2]);
    },
    illegalSet: function(loc) {
      gl.uniform1fv(loc, 2);
    },
    srcValueAsString: function(index, srcValues) {
      return "[" + srcValues[index * 3 + 0].toString() + ", " +
                   srcValues[index * 3 + 1].toString() + ", " +
                   srcValues[index * 3 + 2].toString() + "]";
    },
    returnValueAsString: function(value) {
      return value === null ? 'null' :
          ("[" + value[0] + ", " + value[1] + ", " + value[2] + "]");
    },
    checkType: function(value) {
      return value &&
             typeof value.length === 'number' &&
             value.length == 3;
    },
    checkValue: function(typeInfo, index, value) {
      return value !== null &&
             typeInfo.srcValues[index * 3 + 0] == value[0] &&
             typeInfo.srcValues[index * 3 + 1] == value[1] &&
             typeInfo.srcValues[index * 3 + 2] == value[2];
    },
    srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8],
    srcValuesLess: [16, 15],
    srcValuesLessMultiple: [16, 15, 14, 13, 12, 11],
    srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
    srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7],
  },
  { type: 'vec4',
    jsTypeOf: 'Float32Array',
    setter: 'uniform4fv',
    elem: '[3]',
    numSrcValues: 3,
    invalidSet: function(loc) {
      gl.uniform1fv(loc, [2]);
    },
    illegalSet: function(loc) {
      gl.uniform1fv(loc, 2);
    },
    srcValueAsString: function(index, srcValues) {
      return "[" + srcValues[index * 4 + 0].toString() + ", " +
                   srcValues[index * 4 + 1].toString() + ", " +
                   srcValues[index * 4 + 2].toString() + ", " +
                   srcValues[index * 4 + 3].toString() + "]";
    },
    returnValueAsString: function(value) {
      return value === null ? 'null' :
          ("[" + value[0] + ", " + value[1] +
           ", " + value[2] + ", " + value[3] + "]");
    },
    checkType: function(value) {
      return value &&
             typeof value.length === 'number' &&
             value.length == 4;
    },
    checkValue: function(typeInfo, index, value) {
      return value !== null &&
             typeInfo.srcValues[index * 4 + 0] == value[0] &&
             typeInfo.srcValues[index * 4 + 1] == value[1] &&
             typeInfo.srcValues[index * 4 + 2] == value[2] &&
             typeInfo.srcValues[index * 4 + 3] == value[3];
    },
    srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5],
    srcValuesLess: [16, 15, 14],
    srcValuesLessMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
    srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
    srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4],
  }
];

if (contextVersion >= 2) {
  const more = [
    { type: 'int',
      jsTypeOf: 'number',
      setter: 'uniform1iv',
      elem: '',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform2iv(loc, [1, 2]);
      },
      srcValueAsString: function(index, srcValues) {
        return srcValues[index].toString();
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' : value.toString();
      },
      checkType: function(value) {
        return typeof value === 'number';
      },
      checkValue: function(typeInfo, index, value) {
        return typeInfo.srcValues[index] == value;
      },
      srcValues: [16, 15, 14],
      srcValuesLess: [],
      srcValuesLessMultiple: [16],
      srcValuesMoreMultiple: [16, 15, 14, 13],
      srcValuesNonMultiple: null,
    },
    { type: 'ivec2',
      jsTypeOf: 'Int32Array',
      setter: 'uniform2iv',
      elem: '[1]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1iv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1iv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 2 + 0].toString() + ", " +
                     srcValues[index * 2 + 1].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' : ("[" + value[0] + ", " + value[1] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 2;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 2 + 0] == value[0] &&
               typeInfo.srcValues[index * 2 + 1] == value[1];
      },
      srcValues: [16, 15, 14, 13, 12, 11],
      srcValuesLess: [16],
      srcValuesLessMultiple: [16, 15, 14, 13],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10],
    },
    { type: 'ivec3',
      jsTypeOf: 'Int32Array',
      setter: 'uniform3iv',
      elem: '[2]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1iv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1iv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 3 + 0].toString() + ", " +
                     srcValues[index * 3 + 1].toString() + ", " +
                     srcValues[index * 3 + 2].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' :
            ("[" + value[0] + ", " + value[1] + ", " + value[2] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 3;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 3 + 0] == value[0] &&
               typeInfo.srcValues[index * 3 + 1] == value[1] &&
               typeInfo.srcValues[index * 3 + 2] == value[2];
      },
      srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8],
      srcValuesLess: [16, 15],
      srcValuesLessMultiple: [16, 15, 14, 13, 12, 11],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7],
    },
    { type: 'ivec4',
      jsTypeOf: 'Int32Array',
      setter: 'uniform4iv',
      elem: '[3]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1iv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1iv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 4 + 0].toString() + ", " +
                     srcValues[index * 4 + 1].toString() + ", " +
                     srcValues[index * 4 + 2].toString() + ", " +
                     srcValues[index * 4 + 3].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' :
            ("[" + value[0] + ", " + value[1] +
             ", " + value[2] + ", " + value[3] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 4;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 4 + 0] == value[0] &&
               typeInfo.srcValues[index * 4 + 1] == value[1] &&
               typeInfo.srcValues[index * 4 + 2] == value[2] &&
               typeInfo.srcValues[index * 4 + 3] == value[3];
      },
      srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5],
      srcValuesLess: [16, 15, 14],
      srcValuesLessMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4],
    },


    { type: 'uint',
      jsTypeOf: 'number',
      setter: 'uniform1uiv',
      elem: '',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform2uiv(loc, [1, 2]);
      },
      srcValueAsString: function(index, srcValues) {
        return srcValues[index].toString();
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' : value.toString();
      },
      checkType: function(value) {
        return typeof value === 'number';
      },
      checkValue: function(typeInfo, index, value) {
        return typeInfo.srcValues[index] == value;
      },
      srcValues: [16, 15, 14],
      srcValuesLess: [],
      srcValuesLessMultiple: [16],
      srcValuesMoreMultiple: [16, 15, 14, 13],
      srcValuesNonMultiple: null,
    },
    { type: 'uvec2',
      jsTypeOf: 'Uint32Array',
      setter: 'uniform2uiv',
      elem: '[1]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1uiv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1uiv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 2 + 0].toString() + ", " +
                     srcValues[index * 2 + 1].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' : ("[" + value[0] + ", " + value[1] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 2;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 2 + 0] == value[0] &&
               typeInfo.srcValues[index * 2 + 1] == value[1];
      },
      srcValues: [16, 15, 14, 13, 12, 11],
      srcValuesLess: [16],
      srcValuesLessMultiple: [16, 15, 14, 13],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10],
    },
    { type: 'uvec3',
      jsTypeOf: 'Uint32Array',
      setter: 'uniform3uiv',
      elem: '[2]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1uiv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1uiv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 3 + 0].toString() + ", " +
                     srcValues[index * 3 + 1].toString() + ", " +
                     srcValues[index * 3 + 2].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' :
            ("[" + value[0] + ", " + value[1] + ", " + value[2] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 3;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 3 + 0] == value[0] &&
               typeInfo.srcValues[index * 3 + 1] == value[1] &&
               typeInfo.srcValues[index * 3 + 2] == value[2];
      },
      srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8],
      srcValuesLess: [16, 15],
      srcValuesLessMultiple: [16, 15, 14, 13, 12, 11],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7],
    },
    { type: 'uvec4',
      jsTypeOf: 'Uint32Array',
      setter: 'uniform4uiv',
      elem: '[3]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1uiv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1uiv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 4 + 0].toString() + ", " +
                     srcValues[index * 4 + 1].toString() + ", " +
                     srcValues[index * 4 + 2].toString() + ", " +
                     srcValues[index * 4 + 3].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' :
            ("[" + value[0] + ", " + value[1] +
             ", " + value[2] + ", " + value[3] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 4;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 4 + 0] == value[0] &&
               typeInfo.srcValues[index * 4 + 1] == value[1] &&
               typeInfo.srcValues[index * 4 + 2] == value[2] &&
               typeInfo.srcValues[index * 4 + 3] == value[3];
      },
      srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5],
      srcValuesLess: [16, 15, 14],
      srcValuesLessMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4],
    },


    { type: 'bool',
      jsTypeOf: 'boolean',
      setter: 'uniform1iv',
      elem: '',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform2iv(loc, [1, 2]);
      },
      srcValueAsString: function(index, srcValues) {
        return srcValues[index].toString();
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' : value.toString();
      },
      checkType: function(value) {
        return typeof value === 'boolean';
      },
      checkValue: function(typeInfo, index, value) {
        return typeInfo.srcValues[index] == value;
      },
      srcValues: [true, true, true],
      srcValuesLess: [],
      srcValuesLessMultiple: [16],
      srcValuesMoreMultiple: [16, 15, 14, 13],
      srcValuesNonMultiple: null,
    },
    { type: 'bvec2',
      jsTypeOf: 'Float32Array',
      setter: 'uniform2fv',
      elem: '[1]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1iv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1iv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 2 + 0].toString() + ", " +
                     srcValues[index * 2 + 1].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' : ("[" + value[0] + ", " + value[1] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 2;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 2 + 0] == value[0] &&
               typeInfo.srcValues[index * 2 + 1] == value[1];
      },
      srcValues: [true, true, true, true, true, true],
      srcValuesLess: [16],
      srcValuesLessMultiple: [16, 15, 14, 13],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10],
    },
    { type: 'bvec3',
      jsTypeOf: 'Int32Array',
      setter: 'uniform3iv',
      elem: '[2]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1iv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1iv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 3 + 0].toString() + ", " +
                     srcValues[index * 3 + 1].toString() + ", " +
                     srcValues[index * 3 + 2].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' :
            ("[" + value[0] + ", " + value[1] + ", " + value[2] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 3;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 3 + 0] == value[0] &&
               typeInfo.srcValues[index * 3 + 1] == value[1] &&
               typeInfo.srcValues[index * 3 + 2] == value[2];
      },
      srcValues: [true, true, true, true, true, true, true, true, true],
      srcValuesLess: [16, 15],
      srcValuesLessMultiple: [16, 15, 14, 13, 12, 11],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7],
    },
    { type: 'bvec4',
      jsTypeOf: 'Uint32Array',
      setter: 'uniform4uiv',
      elem: '[3]',
      numSrcValues: 3,
      invalidSet: function(loc) {
        gl.uniform1iv(loc, [2]);
      },
      illegalSet: function(loc) {
        gl.uniform1iv(loc, 2);
      },
      srcValueAsString: function(index, srcValues) {
        return "[" + srcValues[index * 4 + 0].toString() + ", " +
                     srcValues[index * 4 + 1].toString() + ", " +
                     srcValues[index * 4 + 2].toString() + ", " +
                     srcValues[index * 4 + 3].toString() + "]";
      },
      returnValueAsString: function(value) {
        return value === null ? 'null' :
            ("[" + value[0] + ", " + value[1] +
             ", " + value[2] + ", " + value[3] + "]");
      },
      checkType: function(value) {
        return value &&
               typeof value.length === 'number' &&
               value.length == 4;
      },
      checkValue: function(typeInfo, index, value) {
        return value !== null &&
               typeInfo.srcValues[index * 4 + 0] == value[0] &&
               typeInfo.srcValues[index * 4 + 1] == value[1] &&
               typeInfo.srcValues[index * 4 + 2] == value[2] &&
               typeInfo.srcValues[index * 4 + 3] == value[3];
      },
      srcValues: [true, true, true, true, true, true, true, true, true, true, true, true],
      srcValuesLess: [16, 15, 14],
      srcValuesLessMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
      srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
      srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4],
    }
  ];
  typeInfos.push(...more);
}

let loc;
for (var tt = 0; tt < typeInfos.length; ++tt) {
  var typeInfo = typeInfos[tt];
  debug("");
  debug("check " + typeInfo.type);
  var fSrc = wtu.replaceParams(fTemplate, typeInfo);
  //debug("fSrc: " + fSrc);
  var program = wtu.loadProgram(gl, vSrc, fSrc);

  var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  assertMsg(numUniforms == 1, "1 uniform found");
  var info = gl.getActiveUniform(program, 0);
  assertMsg(info.name == "color[0]",
            "uniform name is 'color[0]' not 'color' as per OpenGL ES 2.0.24 section 2.10");
  shouldBeNull("gl.getUniformLocation(program, 'color[" + MaxInt32PlusOne + "]');");
  loc = gl.getUniformLocation(program, "color[0]");
  if (!loc) throw 'Missing loc';
  var srcValues = typeInfo.srcValues;
  var srcValuesLess = typeInfo.srcValuesLess;
  var srcValuesLessMultiple = typeInfo.srcValuesLessMultiple;
  var srcValuesMoreMultiple = typeInfo.srcValuesMoreMultiple;
  var srcValuesNonMultiple = typeInfo.srcValuesNonMultiple;

  // Try setting the value before using the program
  gl[typeInfo.setter](loc, srcValues);
  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
                  "should fail if there is no current program");

  gl.useProgram(program);
  gl[typeInfo.setter](loc, srcValuesLess);
  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
                  "should fail with insufficient array size with gl." + typeInfo.setter);
  if (srcValuesNonMultiple) {
    gl[typeInfo.setter](loc, srcValuesNonMultiple);
    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
                    "should fail with non-multiple array size with gl." + typeInfo.setter);
  }

  const validDatas = [
    `new Float32Array(${srcValues.length})`,
    `new Float32Array(new ArrayBuffer(4*${srcValues.length}))`,
  ];
  if (window.SharedArrayBuffer) {
    validDatas.push(
      `new Float32Array(new SharedArrayBuffer(4*${srcValues.length}))`
    );
  }
  for (const x of validDatas) {
    shouldNotThrow(`gl.${typeInfo.setter}(loc, ${x});`);
  }

  gl[typeInfo.setter](loc, srcValues);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                  "can set an array of uniforms with gl." + typeInfo.setter);
  gl[typeInfo.setter](loc, srcValuesLessMultiple);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                  "can partially set an array of uniforms with gl." + typeInfo.setter + " with a smaller array");
  gl[typeInfo.setter](loc, srcValuesMoreMultiple);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                  "can set an array of uniforms with gl." + typeInfo.setter + " with a larger array");

  var values = gl.getUniform(program, loc);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                  "can call gl.getUniform");
  assertMsg(typeInfo.checkType(values),
            "gl.getUniform returns the correct type. " + `(was ${values.constructor.name})`);
  for (var ii = 0; ii < typeInfo.numSrcValues; ++ii) {
    shouldBeNull("gl.getUniformLocation(program, 'color[" + (MaxInt32PlusOne + ii) + "]')");
    var elemLoc = gl.getUniformLocation(program, "color[" + ii + "]");
    wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                    "can get location of element " + ii +
                    " of array from gl.getUniformLocation");
    var value = gl.getUniform(program, elemLoc);
    wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                    "can get value of element " + ii + " of array from gl.getUniform");
    assertMsg(typeInfo.checkValue(typeInfo, ii, value),
              "value put in (" + typeInfo.srcValueAsString(ii, srcValues) +
              ") matches value pulled out (" +
              typeInfo.returnValueAsString(value) + ")");
  }
  typeInfo.invalidSet(loc);
  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
                  "using the wrong size of gl.Uniform fails");
  var exceptionCaught = false;
  if (typeInfo.illegalSet) {
    try {
      typeInfo.illegalSet(loc);
    } catch (e) {
      exceptionCaught = true;
    }
    assertMsg(exceptionCaught, "passing non-array to glUniform*fv should throw TypeError");
  }

  gl.useProgram(null);
  wtu.glErrorShouldBe(gl, gl.NO_ERROR,
                  "can call gl.useProgram(null)");
}

var successfullyParsed = true;

</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>
