tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

glsl-constructor-tests-generator.js (27595B)


      1 /*
      2 Copyright (c) 2019 The Khronos Group Inc.
      3 Use of this source code is governed by an MIT-style license that can be
      4 found in the LICENSE.txt file.
      5 */
      6 
      7 
      8 var GLSLConstructorTestsGenerator = (function() {
      9 
     10 var wtu = WebGLTestUtils;
     11 
     12 // Shader code templates
     13 var constructorVertexTemplate = [
     14  "attribute vec4 vPosition;",
     15 
     16  "precision mediump int;",
     17  "precision mediump float;",
     18 
     19  // Colors used to signal correctness of component values comparison
     20  "const vec4 green = vec4(0.0, 1.0, 0.0, 1.0);",
     21  "const vec4 red   = vec4(1.0, 0.0, 0.0, 1.0);",
     22 
     23  // Error bound used in comparison of floating point values
     24  "$(errorBound)",
     25 
     26  "varying vec4 vColor;",
     27 
     28  "void main() {",
     29  "  $(argsList)",
     30 
     31  "  $(type) v = $(type)($(argsConstr));",
     32 
     33  "  if ($(checkCompVals))",
     34  "    vColor = green;",
     35  "  else",
     36  "    vColor = red;",
     37 
     38  "  gl_Position = vPosition;",
     39  "}"
     40 ].join("\n");
     41 
     42 
     43 var passThroughColorFragmentShader = [
     44  "precision mediump float;",
     45 
     46  "varying vec4 vColor;",
     47 
     48  "void main() {",
     49  "    gl_FragColor = vColor;",
     50  "}"
     51 ].join('\n');
     52 
     53 
     54 var constructorFragmentTemplate = [
     55  "precision mediump int;",
     56  "precision mediump float;",
     57 
     58  // Colors used to signal correctness of component values comparison
     59  "const vec4 green = vec4(0.0, 1.0, 0.0, 1.0); ",
     60  "const vec4 red   = vec4(1.0, 0.0, 0.0, 1.0); ",
     61 
     62  // Error bound used in comparison of floating point values
     63  "$(errorBound)",
     64 
     65  "void main() {",
     66  "  $(argsList)",
     67 
     68  "  $(type) v = $(type)($(argsConstr));",
     69 
     70  "  if ($(checkCompVals))",
     71  "    gl_FragColor = green;",
     72  "  else",
     73  "    gl_FragColor = red;",
     74  "}"
     75 ].join("\n");
     76 
     77 
     78 // Coding of the different argument types
     79 // s  : scalar
     80 // v2 : vec2
     81 // v3 : vec3
     82 // v4 : vec4
     83 // m2 : mat2
     84 // m3 : mat3
     85 // m4 : mat4
     86 
     87 // Returns the dimensions of the type
     88 // Count of columns, count of rows
     89 function getTypeCodeDimensions(typeCode) {
     90  switch (typeCode) {
     91    case "s":  return [1, 1];
     92    case "v2": return [1, 2];
     93    case "v3": return [1, 3];
     94    case "v4": return [1, 4];
     95    case "m2": return [2, 2];
     96    case "m3": return [3, 3];
     97    case "m4": return [4, 4];
     98 
     99    default:
    100      wtu.error("GLSLConstructorTestsGenerator.getTypeCodeDimensions(), unknown type code");
    101      debugger;
    102  }
    103 };
    104 
    105 
    106 // Returns the component count for the type code
    107 function getTypeCodeComponentCount(typeCode) {
    108  var dim = getTypeCodeDimensions(typeCode);
    109 
    110  return dim[0] * dim[1];
    111 }
    112 
    113 
    114 // Returns glsl name of type code
    115 function getGLSLBaseTypeName(typeCode) {
    116  switch(typeCode) {
    117    case "s":  return "";
    118    case "v2": return "vec2";
    119    case "v3": return "vec3";
    120    case "v4": return "vec4";
    121    case "m2": return "mat2";
    122    case "m3": return "mat3";
    123    case "m4": return "mat4";
    124 
    125    default:
    126      wtu.error("GLSLConstructorTestsGenerator.getGLSLBaseTypeName(), unknown type code");
    127      debugger;
    128  }
    129 }
    130 
    131 
    132 // Returns the scalar glsl type name related to the structured type
    133 function getGLSLScalarType(targetType) {
    134  switch(targetType[0]) {
    135    case 'i': return "int";
    136    case 'b': return "bool";
    137 
    138    case 'v':
    139    case 'm':
    140      return "float";
    141 
    142    default:
    143      wtu.error("GLSLConstructorTestsGenerator.getGLSLScalarType(), unknown target type");
    144      debugger;
    145  }
    146 }
    147 
    148 
    149 // Returns the scalar prefix for the associated scalar type
    150 function getGLSLScalarPrefix(targetType) {
    151  switch(targetType[0]) {
    152    case 'i':
    153    case 'b':
    154      return targetType[0];
    155 
    156    case 'v':
    157    case 'm':
    158      return '';
    159 
    160    default:
    161      wtu.error("GLSLConstructorTestsGenerator.getGLSLScalarPrefix(), unknown target type");
    162      debugger;
    163  }
    164 }
    165 
    166 
    167 // Returns the type for a specified target type and argument type code
    168 function getGLSLArgumentType(typeCode, targetType) {
    169  var baseType = getGLSLBaseTypeName(typeCode);
    170  if (baseType !== "") {
    171    if (typeCode[0] === "v") {
    172      // Vectors come in different flavours
    173      return getGLSLScalarPrefix(targetType) + baseType;
    174    }
    175    else
    176      return baseType;
    177  }
    178  else
    179    return getGLSLScalarType(targetType);
    180 }
    181 
    182 
    183 // Returns the glsl type of the argument components
    184 function getGLSLArgumentComponentType(argTypeCode, targetType) {
    185  var scalarType;
    186 
    187  if (argTypeCode[0] === "m") {
    188    // Matrices are always floats
    189    scalarType = "float";
    190  }
    191  else
    192    scalarType = getGLSLScalarType(targetType);
    193 
    194  return scalarType;
    195 }
    196 
    197 
    198 function getGLSLColumnSize(targetType) {
    199  colSize = parseInt(targetType.slice(-1));
    200 
    201  if (!isNaN(colSize))
    202    return colSize;
    203 
    204  wtu.error("GLSLConstructorTestsGenerator.getGLSLColumnSize(), invalid target type");
    205    debugger;
    206 }
    207 
    208 
    209 // Returns correct string representation of scalar value
    210 function getScalarTypeValStr(val, scalarType) {
    211  if (val == null)
    212    debugger;
    213 
    214  switch (scalarType) {
    215    case "float": return val.toFixed(1);
    216    case "int":   return val;
    217    case "bool":  return (val === 0) ? "false" : "true";
    218 
    219    default:
    220      wtu.error("GLSLConstructorTestsGenerator.getScalarTypeValStr(), unknown scalar type");
    221      debugger;
    222  }
    223 }
    224 
    225 
    226 // Returns true if the glsl type name is a matrix
    227 function isGLSLTypeMatrix(type) {
    228  return (type.indexOf("mat") !== -1);
    229 }
    230 
    231 
    232 // Returns true if the glsl type name is a vector
    233 function isGLSLTypeVector(type) {
    234  return (type.indexOf("vec") !== -1);
    235 }
    236 
    237 
    238 // Returns the count of components
    239 function getGLSLTypeComponentCount(type) {
    240  var colSize = getGLSLColumnSize(type);
    241 
    242  if (isGLSLTypeMatrix(type))
    243    return colSize * colSize;
    244  else
    245    return colSize;
    246 }
    247 
    248 
    249 // Returns the constructor expression with the components set to a sequence of scalar values
    250 // Like vec3(1.0, 2.0, 3.0)
    251 function getComponentSequenceConstructorExpression(typeCode, firstCompValue, targetType) {
    252  var scalarType = getGLSLArgumentComponentType(typeCode, targetType);
    253 
    254  if (typeCode === "s") {
    255    // Scalar
    256    return getScalarTypeValStr(firstCompValue, scalarType) + ";";
    257  }
    258  else {
    259    // Structured typeargTypeCode[0] === "m"
    260    compCount = getTypeCodeComponentCount(typeCode);
    261    var constrExpParts = new Array(compCount);
    262    for (var aa = 0; aa < compCount; ++aa)
    263        constrExpParts[aa] = getScalarTypeValStr(firstCompValue + aa, scalarType);
    264 
    265    return getGLSLArgumentType(typeCode, targetType) + "(" + constrExpParts.join(", ") + ");";
    266  }
    267 }
    268 
    269 
    270 // Returns the expression to select a component of the structured type
    271 function getComponentSelectorExpStr(targetType, compIx) {
    272  if (isGLSLTypeMatrix(targetType)) {
    273    var colRowIx = getColRowIndexFromLinearIndex(compIx, getGLSLColumnSize(targetType));
    274    return "v[" + colRowIx.colIx + "][" + colRowIx.rowIx + "]";
    275  }
    276  else
    277    return "v[" + compIx + "]";
    278 }
    279 
    280 
    281 // Returns expression which validates the components set by the constructor expression
    282 function getComponentValidationExpression(refCompVals, targetType) {
    283  // Early out for invalid arguments
    284  if (refCompVals.length === 0)
    285    return "false";
    286 
    287  var scalarType = getGLSLScalarType(targetType);
    288  var checkComponentValueParts = new Array(refCompVals.length);
    289  for (var cc = 0; cc < refCompVals.length; ++cc) {
    290    var val_str = getScalarTypeValStr(refCompVals[cc], scalarType);
    291    var comp_sel_exp = getComponentSelectorExpStr(targetType, cc);
    292    if (scalarType === "float") {
    293      // Comparison of floating point values with error bound
    294      checkComponentValueParts[cc] = "abs(" + comp_sel_exp + " - " + val_str + ") <= errorBound";
    295    }
    296    else {
    297      // Simple comparison to expected value
    298      checkComponentValueParts[cc] = comp_sel_exp + " == " + val_str;
    299    }
    300  }
    301 
    302  return checkComponentValueParts.join(" && ");
    303 }
    304 
    305 
    306 // Returns substitution parts to turn the shader template into testable shader code
    307 function getTestShaderParts(targetType, argExp, firstCompValue) {
    308  // glsl code of declarations of arguments
    309  var argsListParts = new Array(argExp.length);
    310 
    311  // glsl code of constructor expression
    312  var argsConstrParts = new Array(argExp.length);
    313 
    314  // glsl type expression
    315  var typeExpParts = new Array(argExp.length);
    316  for (var aa = 0; aa < argExp.length; ++aa) {
    317    var typeCode     = argExp[aa];
    318    var argCompCount = getTypeCodeComponentCount(typeCode);
    319    var argName      = "a" + aa;
    320    var argType      = getGLSLArgumentType(typeCode, targetType);
    321    var argConstrExp = argType + " " + argName + " = " + getComponentSequenceConstructorExpression(typeCode, firstCompValue, targetType);
    322 
    323    // Add construction of one argument
    324    // Indent if not first argument
    325    argsListParts[aa] = ((aa > 0) ? "  " : "") + argConstrExp;
    326 
    327    // Add argument name to target type argument list
    328    argsConstrParts[aa] = argName;
    329 
    330    // Add type name to type expression
    331    typeExpParts[aa] = argType;
    332 
    333    // Increment argument component value so all argument component arguments have a unique value
    334    firstCompValue += argCompCount;
    335  }
    336 
    337  return {
    338    argsList:   argsListParts.join("\n") + "\n",
    339    argsConstr: argsConstrParts.join(", "),
    340    typeExp:    targetType + "(" + typeExpParts.join(", ") + ")"
    341  };
    342 }
    343 
    344 
    345 // Utility functions to manipulate the array of reference values
    346 
    347 // Returns array filled with identical values
    348 function getArrayWithIdenticalValues(size, val) {
    349  var matArray = new Array(size);
    350  for (var aa = 0; aa < size; ++aa)
    351    matArray[aa] = val;
    352 
    353  return matArray;
    354 }
    355 
    356 
    357 // Returns array filled with increasing values from a specified start value
    358 function getArrayWithIncreasingValues(size, start) {
    359  var matArray = new Array(size);
    360  for (var aa = 0; aa < size; ++aa)
    361    matArray[aa] = start + aa;
    362 
    363  return matArray;
    364 }
    365 
    366 
    367 // Utility functions to manipulate the array of reference values if the target type is a matrix
    368 
    369 // Returns an array which is the column order layout of a square matrix where the diagonal is set to a specified value
    370 function matCompArraySetDiagonal(matArray, diagVal) {
    371  // The entries for the diagonal start at array index 0 and increase
    372  // by column size + 1
    373  var colSize = Math.round(Math.sqrt(matArray.length));
    374  var dIx = 0;
    375  do {
    376    matArray[dIx] = diagVal;
    377    dIx += (colSize + 1);
    378  }
    379  while (dIx < colSize * colSize);
    380 
    381  return matArray;
    382 }
    383 
    384 
    385 // Returns an array which contains the values of an identity matrix read out in column order
    386 function matCompArrayCreateDiagonalMatrix(colSize, diagVal) {
    387  var size = colSize * colSize;
    388  var matArray = new Array(size);
    389  for (var aa = 0; aa < size; ++aa)
    390    matArray[aa] = 0;
    391 
    392  return matCompArraySetDiagonal(matArray, diagVal);
    393 }
    394 
    395 
    396 // Returns the column and row index from the linear index if the components of the matrix are stored in column order in an array
    397 // in a one dimensional array in column order
    398 function getColRowIndexFromLinearIndex(linIx, colSize) {
    399  return {
    400    colIx: Math.floor(linIx / colSize),
    401    rowIx: linIx % colSize
    402  };
    403 }
    404 
    405 
    406 // Returns the linear index for matrix column and row index for a specified matrix size
    407 function getLinearIndexFromColRowIndex(rowColIx, colSize) {
    408  return rowColIx.colIx * colSize + rowColIx.rowIx;
    409 }
    410 
    411 
    412 // Returns a matrix set from another matrix
    413 function matCompArraySetMatrixFromMatrix(dstColSize, srcMatArray) {
    414  // Overwrite components from destination with the source component values at the same col, row coordinates
    415  var dstMatArray = matCompArrayCreateDiagonalMatrix(dstColSize, 1);
    416 
    417  var srcColSize = Math.round(Math.sqrt(srcMatArray.length));
    418 
    419  for (var c_ix = 0; c_ix < srcMatArray.length; ++c_ix) {
    420    var srcMatIx = getColRowIndexFromLinearIndex(c_ix, srcColSize);
    421    if (srcMatIx.colIx < dstColSize && srcMatIx.rowIx < dstColSize) {
    422      // Source matrix coordinates are valid destination matrix coordinates
    423      dstMatArray[getLinearIndexFromColRowIndex(srcMatIx, dstColSize)] = srcMatArray[c_ix];
    424    }
    425  }
    426 
    427  return dstMatArray;
    428 }
    429 
    430 
    431 // Returns the glsl code to verify if the components are set correctly
    432 // and the message to display for the test
    433 function getConstructorExpressionInfo(targetType, argExp, firstCompValue) {
    434  var argCompCountsSum = 0;
    435  var argCompCounts = new Array(argExp.length);
    436  for (var aa = 0; aa < argExp.length; ++aa) {
    437    argCompCounts[aa] = getTypeCodeComponentCount(argExp[aa]);
    438    argCompCountsSum += argCompCounts[aa];
    439  }
    440 
    441  var targetCompCount = getGLSLTypeComponentCount(targetType);
    442 
    443  var refCompVals;
    444  var testMsg;
    445  var valid;
    446 
    447  if (argCompCountsSum === 0) {
    448    // A constructor needs at least one argument
    449    refCompVals = [];
    450    testMsg     = "invalid (no arguments)";
    451    valid       = false;
    452  }
    453  else {
    454    if (isGLSLTypeVector(targetType)) {
    455      if (argCompCountsSum === 1) {
    456        // One scalar argument
    457        // Vector constructor with one scalar argument set all components to the same value
    458        refCompVals = getArrayWithIdenticalValues(targetCompCount, firstCompValue);
    459        testMsg     = "valid (all components set to the same value)";
    460        valid       = true;
    461      }
    462      else {
    463        // Not one scalar argument
    464        if (argCompCountsSum < targetCompCount) {
    465          // Not all components set
    466          refCompVals = [];
    467          testMsg     = "invalid (not enough arguments)";
    468          valid       = false;
    469        }
    470        else {
    471          // argCompCountsSum >= targetCompCount
    472          // All components set
    473          var lastArgFirstCompIx = argCompCountsSum - argCompCounts[argCompCounts.length - 1];
    474 
    475          if (lastArgFirstCompIx < targetCompCount) {
    476            // First component of last argument is used
    477            refCompVals = getArrayWithIncreasingValues(targetCompCount, firstCompValue);
    478            testMsg     = "valid";
    479            valid       = true;
    480          }
    481          else {
    482            // First component of last argument is not used
    483            refCompVals = [];
    484            testMsg     = "invalid (unused argument)";
    485            valid       = false;
    486          }
    487        }
    488      }
    489    }
    490    else {
    491      // Matrix target type
    492      if (argCompCountsSum === 1) {
    493        // One scalar argument
    494        // Matrix constructors with one scalar set all components on the diagonal to the same value
    495        // All other components are set to zero
    496        refCompVals = matCompArrayCreateDiagonalMatrix(Math.round(Math.sqrt(targetCompCount)), firstCompValue);
    497        testMsg     = "valid (diagonal components set to the same value, off-diagonal components set to zero)";
    498        valid       = true;
    499      }
    500      else {
    501        // Not one scalar argument
    502        if (argExp.length === 1 && argExp[0][0] === "m") {
    503          // One single matrix argument
    504          var dstColSize = getGLSLColumnSize(targetType);
    505          refCompVals = matCompArraySetMatrixFromMatrix(dstColSize, getArrayWithIncreasingValues(getTypeCodeComponentCount(argExp[0]), firstCompValue));
    506          testMsg     = "valid, components at corresponding col, row indices are set from argument, other components are set from identity matrix";
    507          valid       = true;
    508        }
    509        else {
    510          // More than one argument or one argument not of type matrix
    511          // Can be treated in the same manner
    512          // Arguments can not be of type matrix
    513          var matFound = false;
    514          for (var aa = 0; aa < argExp.length; ++aa)
    515            if (argExp[aa][0] === "m")
    516              matFound = true;
    517 
    518          if (matFound) {
    519            refCompVals = [];
    520            testMsg     = "invalid, argument list greater than one contains matrix type";
    521            valid       = false;
    522          }
    523          else {
    524            if (argCompCountsSum < targetCompCount) {
    525              refCompVals = [];
    526              testMsg     = "invalid (not enough arguments)";
    527              valid       = false;
    528            }
    529            else {
    530              // argCompCountsSum >= targetCompCount
    531              // All components set
    532              var lastArgFirstCompIx = argCompCountsSum - argCompCounts[argCompCounts.length - 1];
    533 
    534              if (lastArgFirstCompIx < targetCompCount) {
    535                // First component of last argument is used
    536                refCompVals = getArrayWithIncreasingValues(targetCompCount, firstCompValue);
    537                testMsg     = "valid";
    538                valid       = true;
    539              }
    540              else {
    541                // First component of last argument is not used
    542                refCompVals = [];
    543                testMsg     = "invalid (unused argument)";
    544                valid       = false;
    545              }
    546            }
    547          }
    548        }
    549      }
    550    }
    551  }
    552 
    553  // Check if no case is missed
    554  if (testMsg == null || valid == null) {
    555    wtu.error("GLSLConstructorTestsGenerator.getConstructorExpressionInfo(), info not set");
    556    debugger;
    557  }
    558 
    559  return {
    560    refCompVals: refCompVals,
    561    testMsg:     testMsg,
    562    valid:       valid
    563  };
    564 }
    565 
    566 
    567 // Returns a vertex shader testcase and a fragment shader testcase
    568 function getVertexAndFragmentShaderTestCase(targetType, argExp) {
    569  var firstCompValue = 0;
    570  if (isGLSLTypeMatrix(targetType)) {
    571    // Use value different from 0 and 1
    572    // 0 and 1 are values used by matrix constructed from a matrix or a single scalar
    573    firstCompValue = 2;
    574  }
    575 
    576  var argCode = getTestShaderParts          (targetType, argExp, firstCompValue);
    577  var expInfo = getConstructorExpressionInfo(targetType, argExp, firstCompValue);
    578 
    579  var substitutions = {
    580    type:          targetType,
    581    errorBound:    (getGLSLScalarType(targetType) === "float") ? "const float errorBound = 1.0E-5;" : "",
    582    argsList:      argCode.argsList,
    583    argsConstr:    argCode.argsConstr,
    584    checkCompVals: getComponentValidationExpression(expInfo.refCompVals, targetType)
    585  };
    586 
    587  return [ {
    588      // Test constructor argument list in vertex shader
    589      vShaderSource:  wtu.replaceParams(constructorVertexTemplate, substitutions),
    590      vShaderSuccess: expInfo.valid,
    591      fShaderSource:  passThroughColorFragmentShader,
    592      fShaderSuccess: true,
    593      linkSuccess:    expInfo.valid,
    594      passMsg:        "Vertex shader : " + argCode.typeExp + ", " + expInfo.testMsg,
    595      render:         expInfo.valid
    596    }, {
    597      // Test constructor argument list in fragment shader
    598      fShaderSource:  wtu.replaceParams(constructorFragmentTemplate, substitutions),
    599      fShaderSuccess: expInfo.valid,
    600      linkSuccess:    expInfo.valid,
    601      passMsg:        "Fragment shader : " + argCode.typeExp + ", " + expInfo.testMsg,
    602      render:         expInfo.valid
    603    }
    604  ];
    605 }
    606 
    607 
    608 // Incrementing the argument expressions
    609 // Utility object which defines the order of incrementing the argument types
    610 var typeCodeIncrementer = {
    611  s:     { typeCode: "v2", order: 0 },
    612  v2:    { typeCode: "v3", order: 1 },
    613  v3:    { typeCode: "v4", order: 2 },
    614  v4:    { typeCode: "m2", order: 3 },
    615  m2:    { typeCode: "m3", order: 4 },
    616  m3:    { typeCode: "m4", order: 5 },
    617  m4:    { typeCode: "s",  order: 6 },
    618  first: "s"
    619 }
    620 
    621 
    622 // Returns the next argument sequence
    623 function getNextArgumentSequence(inSeq) {
    624  var nextSeq;
    625  if (inSeq.length === 0) {
    626    // Current argument sequence is empty, add first argument
    627    nextSeq = [typeCodeIncrementer.first];
    628  }
    629  else {
    630    nextSeq = new Array(inSeq.length);
    631    var overflow = true;
    632    for (var aa = 0; aa < inSeq.length; ++aa) {
    633      var currArg = inSeq[aa];
    634      if (overflow) {
    635        // Increment the current argument type
    636        var nextArg = typeCodeIncrementer[currArg].typeCode;
    637        nextSeq[aa] = nextArg;
    638        overflow = (nextArg === typeCodeIncrementer.first);
    639      }
    640      else {
    641        // Copy remainder of sequence
    642        nextSeq[aa] = currArg;
    643      }
    644    }
    645 
    646    if (overflow) {
    647      nextSeq.push(typeCodeIncrementer.first);
    648    }
    649  }
    650 
    651  return nextSeq;
    652 }
    653 
    654 
    655 // Returns true if two argument expressions are equal
    656 function areArgExpEqual(expA, expB) {
    657  if (expA.length !== expB.length)
    658    return false;
    659 
    660  for (var aa = 0; aa < expA.length; ++aa)
    661    if (expA[aa] !== expB[aa])
    662      return false;
    663 
    664  return true;
    665 }
    666 
    667 
    668 // Returns true if first argument expression is smaller
    669 // (comes before the second one in iterating order)
    670 // compared to the second argument expression
    671 function isArgExpSmallerOrEqual(argExpA, argExpB) {
    672  var aLen = argExpA.length;
    673  var bLen = argExpB.length;
    674  if (aLen !== bLen)
    675    return (aLen < bLen);
    676 
    677  // Argument type expression lengths are equal
    678  for (var aa = aLen - 1; aa >= 0; --aa) {
    679    var argA = argExpA[aa];
    680    var argB = argExpB[aa];
    681 
    682    if (argA !== argB) {
    683      var aOrder = typeCodeIncrementer[argA].order;
    684      var bOrder = typeCodeIncrementer[argB].order;
    685      if (aOrder !== bOrder)
    686        return (aOrder < bOrder);
    687    }
    688  }
    689 
    690  // Argument type expressions are equal
    691  return true;
    692 }
    693 
    694 
    695 // Returns the next argument expression from sequence set
    696 // Returns null if end is reached
    697 function getNextArgumentExpression(testExp, testSet) {
    698  var testInterval = testSet[testExp.ix];
    699 
    700  if (areArgExpEqual(testExp.argExp, testInterval[1])) {
    701    // End of current interval reached
    702    if (testExp.ix === testSet.length - 1) {
    703      // End of set reached
    704      return null;
    705    }
    706    else {
    707      // Return first argument expression of next interval
    708      var nextIx = testExp.ix + 1;
    709      return { ix: nextIx, argExp: testSet[nextIx][0] };
    710    }
    711  }
    712  else {
    713    // Return next expression in current interval
    714    return { ix: testExp.ix, argExp: getNextArgumentSequence(testExp.argExp) };
    715  }
    716 }
    717 
    718 
    719 // Returns an array of the parts in the string separated by commas and with the white space trimmed
    720 function convertCsvToArray(str) {
    721  // Checks type codes in input
    722  function checkInput(el, ix, arr) {
    723    var typeCode = el.trim();
    724    if (!(typeCode in typeCodeIncrementer) && typeCode !== "first") {
    725      wtu.error("GLSLConstructorTestsGenerator.convertCsvToArray(), unknown type code" + typeCode);
    726      debugger;
    727    }
    728 
    729    arr[ix] = typeCode;
    730  }
    731 
    732  var spArr = str.split(",");
    733 
    734  // Convert empty string to empty array
    735  if (spArr.length === 1 && spArr[0].trim() === "")
    736    spArr = [];
    737 
    738  spArr.forEach(checkInput);
    739 
    740  return spArr;
    741 }
    742 
    743 
    744 // Processes the set of specified test sequences
    745 function processInputs(testSequences) {
    746  var testSet = new Array(testSequences.length);
    747  for (var tt = 0; tt < testSequences.length; ++tt) {
    748    var interval = testSequences[tt];
    749    var bounds = interval.split("-");
    750    var begin = convertCsvToArray(bounds[0]);
    751    var end   = convertCsvToArray(bounds[bounds.length - 1]);
    752 
    753    // Check if interval is valid
    754    if (!isArgExpSmallerOrEqual(begin, end)) {
    755      wtu.error("GLSLConstructorTestsGenerator.processInputs(), interval not valid");
    756      debugger;
    757    }
    758 
    759    testSet[tt] = [ begin, end ];
    760  }
    761 
    762  return testSet;
    763 }
    764 
    765 
    766 /**
    767 * Returns list of test cases for vector types
    768 * All combinations of arguments up to one unused argument of one component are tested
    769 * @param {targetType} Name of target type to test the constructor expressions on
    770 * @param {testSet}    Set of intervals of argument sequences to test
    771 */
    772 function getConstructorTests(targetType, testSequences) {
    773  // List of tests to return
    774  var testInfos = [];
    775 
    776  // List of argument types
    777  var testSet = processInputs(testSequences);
    778  var testExp = { ix: 0, argExp: testSet[0][0] };
    779 
    780  do {
    781    // Add one vertex shader test case and one fragment shader test case
    782    testInfos = testInfos.concat(getVertexAndFragmentShaderTestCase(targetType, testExp.argExp));
    783 
    784    // Generate next argument expression
    785    testExp = getNextArgumentExpression(testExp, testSet);
    786  }
    787  while (testExp != null);
    788 
    789  return testInfos;
    790 }
    791 
    792 
    793 // Returns default test argument expression set
    794 // For details on input format : see bottom of file
    795 function getDefaultTestSet(targetType) {
    796  switch(targetType) {
    797    case "vec2":
    798    case "ivec2":
    799    case "bvec2":
    800      return [
    801        // No arguments and all single argument expressions
    802        " - m4",
    803 
    804        // All two argument expressions with a scalar as second argument
    805        "s, s - m4, s",
    806 
    807        // All two arguments expressions with a scalar as first argument
    808        "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
    809 
    810        // Three argument expression
    811        "s, s, s"
    812      ];
    813 
    814    case "vec3":
    815    case "ivec3":
    816    case "bvec3":
    817      return [
    818        // No arguments and all single argument expressions
    819        " - m4",
    820 
    821        // All two argument expressions with a scalar as second argument
    822        "s, s - m4, s",
    823 
    824        // All two argument expressions with a scalar as first argument
    825        "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
    826 
    827        // All three argument expressions with two scalars as second and third argument
    828        "s, s, s - m4, s, s",
    829 
    830        // All three argument expressions with two scalars as first and second argument
    831        "s, s, v2", "s, s, v3", "s, s, v4", "s, s, m2", "s, s, m3", "s, s, m4",
    832 
    833        // Four argument expression
    834        "s, s, s, s"
    835      ];
    836 
    837    case "vec4":
    838    case "ivec4":
    839    case "bvec4":
    840    case "mat2":
    841      return [
    842        // No arguments and all single argument expressions
    843        " - m4",
    844 
    845        // All two argument expressions with a scalar as second argument
    846        "s, s - m4, s",
    847 
    848        // All two argument expressions with a scalar as first argument
    849        "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
    850 
    851        // All three argument expressions with two scalars as second and third argument
    852        "s, s, s - m4, s, s",
    853 
    854        // All three argument expressions with two scalars as first and second argument
    855        "s, s, v2", "s, s, v3", "s, s, v4", "s, s, m2", "s, s, m3", "s, s, m4",
    856 
    857        // All four argument expressions with three scalars as second, third and fourth argument
    858        "s, s, s, s - m4, s, s, s",
    859 
    860        // All four argument expressions with three scalars as first, second and third argument
    861        "s, s, s, v2", "s, s, s, v3", "s, s, s, v4", "s, s, s, m2", "s, s, s, m3", "s, s, s, m4",
    862 
    863        // Five argument expression
    864        "s, s, s, s, s"
    865      ];
    866 
    867    case "mat3":
    868    case "mat4":
    869      return [
    870        // No arguments and all single argument expressions
    871        " - m4",
    872 
    873        // All two argument expressions with a scalar as second argument
    874        "s, s - m4, s",
    875 
    876        // All two argument expressions with a scalar as first argument
    877        "s, v2", "s, v3", "s, v4", "s, m2", "s, m3", "s, m4",
    878 
    879        // Several argument sequences
    880        "v4, s, v4", "v4, s, v3, v2", "v4, v4, v3, v2", "v4, v4, v4, v4", "v2, v2, v2, v2, v2", "v2, v2, v2, v2, v2, v2, v2, v2",
    881        "v3, v3, v3", "v3, v3, v3, s", "v3, v3, v3, v3, v3, s", "v3, v3, v3, v3, v3, s, s",
    882      ];
    883  }
    884 }
    885 
    886 
    887 // Return publics
    888 return {
    889  getConstructorTests: getConstructorTests,
    890  getDefaultTestSet:   getDefaultTestSet
    891 };
    892 
    893 }());
    894 
    895 
    896 // Input is an array of intervals of argument types
    897 // The generated test argument sequences are from (including) the lower interval boundary
    898 // until (including) the upper boundary
    899 // Coding and order of the different argument types :
    900 // s  : scalar
    901 // v2 : vec2
    902 // v3 : vec3
    903 // v4 : vec4
    904 // m2 : mat2
    905 // m3 : mat3
    906 // m4 : mat4
    907 
    908 // One interval is put in one string
    909 // Low and high bound are separated by a dash.
    910 // If there is no dash it is regarded as an interval of one expression
    911 // The individual argument codes are separated by commas
    912 // The individual arguments are incremented from left to right
    913 // The left most argument is the one which is incremented first
    914 // Once the left most arguments wraps the second argument is increased
    915 // Examples :
    916 // "s - m4"        : All single arguments from scalar up to (including) mat4
    917 // "m2, s - m4, s" : All two argument expressions with a matrix argument as first argument and a scalar as second argument
    918 // " - m4, m4"     : The empty argument, all one arguments and all two argument expressions
    919 // "m2, s, v3, m4" : One 4 argument expression : mat2, scalar, vec3, mat4