Merge "js/core: Move test-encode-decode.js to have one testcase per test."
diff --git a/test/vdl/test-canonicalize.js b/test/vdl/test-canonicalize.js
index ac3deb2..31627fc 100644
--- a/test/vdl/test-canonicalize.js
+++ b/test/vdl/test-canonicalize.js
@@ -31,316 +31,461 @@
   });
   return jsval;
 }
-
-// Test basic JSValue canonicalization. Pure JSValues are used.
-// TODO(alexfandrianto): It would be good to test a JSValue inside another type.
-// For example, []JSValue or a struct with JSValues.
-test('canonicalize JSValue - basic functionality', function(t) {
-  var tests = [
-    {
-      name: 'null',
-      input: null,
-      output: null,
-      outputDeep: JS({
-        'null': {}
-      })
-    },
-    {
-      name: 'number',
-      input: 4,
-      output: 4,
-      outputDeep: JS({
-        'number': {
-          val: 4
-        }
-      })
-    },
-    {
-      name: 'string',
-      input: 'fadasa',
-      output: 'fadasa',
-      outputDeep: JS({
-        'string': {
-          val: 'fadasa'
-        }
-      })
-    },
-    {
-      name: 'list',
-      input: [3, false, null, 'abc', undefined],
-      output: [3, false, null, 'abc', null],
-      outputDeep: JS({
-        'list': {
-          val: [
-            {
-              val: JS({
-                'number': { val: 3 }
-              })
-            },
-            {
-              val: JS({
-                'boolean': { val: false }
-              })
-            },
-            {
-              val: JS({
-                'null': {}
-              })
-            },
-            {
-              val: JS({
-                'string': { val: 'abc' }
-              })
-            },
-            {
-              val: JS({
-                'null': {}
-              })
-            }
-          ]
-        }
-      })
-    },
-    {
-      name: 'map',
-      input: new Map([
-        [345, '345'],
-        [null, null]
-      ]),
-      output: new Map([
-        [345, '345'],
-        [null, null]
-      ]),
-      outputDeep: JS({
-        'map': {
-          val: [
-            {
-              key: {
-                val: JS({ 'number': { val: 345 } })
-              },
-              value: {
-                val: JS({ 'string': { val: '345' } })
-              }
-            },
-            {
-              key: {
-                val: JS({ 'null': {} })
-              },
-              value: {
-                val: JS({ 'null': {} })
-              }
-            }
-          ]
-        }
-      })
-    },
-    {
-      name: 'object',
-      input: { name: '', servers: [], mT: false },
-      output: { name: '', servers: [], mT: false },
-      outputDeep: JS({
-        'object': {
-          val: [
-            {
-              key: {
-                val: 'name'
-              },
-              value: {
-                val: JS({ 'string': { val: '' } })
-              }
-            },
-            {
-              key: {
-                val: 'servers'
-              },
-              value: {
-                val: JS({ 'list': { val: [] } })
-              }
-            },
-            {
-              key: {
-                val: 'mT'
-              },
-              value: {
-                val: JS({ 'boolean': { val: false } })
-              }
-            }
-          ]
-        }
-      })
+[
+  {
+  name: 'null',
+  input: null,
+  output: null,
+  outputDeep: JS({
+    'null': {}
+  })
+},
+{
+  name: 'number',
+  input: 4,
+  output: 4,
+  outputDeep: JS({
+    'number': {
+      val: 4
     }
-  ];
-
-  for (var i = 0; i < tests.length; i++) {
-    var name = tests[i].name;
-    var input = tests[i].input;
-    var expected = tests[i].output;
-    var expectedDeep = tests[i].outputDeep;
-    var type = types.JSVALUE;
-
-    // The input canonicalizes to the expected output.
-    var output = canonicalize.reduce(input, type);
-    t.deepEqual(output, expected, name + ' - canon match');
-
-    // Canonicalize is idempotent.
-    var output2 = canonicalize.reduce(output, type);
-    t.deepEqual(output2, output, name + ' - idempotent');
-
-    // The deep wrapped output should also match the expected deep output.
-    var outputDeep = canonicalize.fill(input, type);
-    t.deepEqual(outputDeep, expectedDeep, name + ' - deep');
-
-    // This is also idempotent.
-    var outputDeep2 = canonicalize.fill(outputDeep, type);
-    t.deepEqual(outputDeep2, outputDeep, name + ' - deep idempotent');
-
-    // DeepWrap(output) === outputDeep
-    var outputToDeep = canonicalize.fill(output, type);
-    t.deepEqual(outputToDeep, outputDeep, name + ' - shallow to deep');
-
-    // Unwrap(outputDeep) === output
-    var outputDeepToShallow = canonicalize.reduce(outputDeep, type);
-    t.deepEqual(outputDeepToShallow, output, name + ' - deep to shallow');
-
-
-    // The type of the deep output must match. (Shallow lacks type.)
-    var expectedTypeStr = stringify(type);
-    var outputDeepTypeStr = stringify(outputDeep._type);
-    t.equal(outputDeepTypeStr, expectedTypeStr,
-      name + ' - top-level type match');
-  }
-  t.end();
-});
-
-test('canonicalize JSValue - mixed JSValue and non-JSValue functionality',
-  function(t) {
-
-  var Float32 = registry.lookupOrCreateConstructor(types.FLOAT32);
-
-  var tests = [
-    {
-      name: 'list w/ typed values',
-      input: [3, false, null, 'abc', undefined, new Float32(3.14)],
-      output: [3, false, null, 'abc', null, new Float32(3.14)],
-      outputDeep: JS({
-        'list': {
-          val: [
-            {
-              val: JS({
-                'number': { val: 3 }
-              })
-            },
-            {
-              val: JS({
-                'boolean': { val: false }
-              })
-            },
-            {
-              val: JS({
-                'null': {}
-              })
-            },
-            {
-              val: JS({
-                'string': { val: 'abc' }
-              })
-            },
-            {
-              val: JS({
-                'null': {}
-              })
-            },
-            {
-              val: new Float32(3.14)// any with wrapped float32
-            }
-          ]
+  })
+},
+{
+  name: 'string',
+  input: 'fadasa',
+  output: 'fadasa',
+  outputDeep: JS({
+    'string': {
+      val: 'fadasa'
+    }
+  })
+},
+{
+  name: 'list',
+  input: [3, false, null, 'abc', undefined],
+  output: [3, false, null, 'abc', null],
+  outputDeep: JS({
+    'list': {
+      val: [
+        {
+        val: JS({
+          'number': { val: 3 }
+        })
+      },
+      {
+        val: JS({
+          'boolean': { val: false }
+        })
+      },
+      {
+        val: JS({
+          'null': {}
+        })
+      },
+      {
+        val: JS({
+          'string': { val: 'abc' }
+        })
+      },
+      {
+        val: JS({
+          'null': {}
+        })
+      }
+      ]
+    }
+  })
+},
+{
+  name: 'map',
+  input: new Map([
+    [345, '345'],
+    [null, null]
+  ]),
+  output: new Map([
+    [345, '345'],
+    [null, null]
+  ]),
+  outputDeep: JS({
+    'map': {
+      val: [
+        {
+        key: {
+          val: JS({ 'number': { val: 345 } })
+        },
+        value: {
+          val: JS({ 'string': { val: '345' } })
         }
-      })
-    },
-  ];
+      },
+      {
+        key: {
+          val: JS({ 'null': {} })
+        },
+        value: {
+          val: JS({ 'null': {} })
+        }
+      }
+      ]
+    }
+  })
+},
+{
+  name: 'object',
+  input: { name: '', servers: [], mT: false },
+  output: { name: '', servers: [], mT: false },
+  outputDeep: JS({
+    'object': {
+      val: [
+        {
+        key: {
+          val: 'name'
+        },
+        value: {
+          val: JS({ 'string': { val: '' } })
+        }
+      },
+      {
+        key: {
+          val: 'servers'
+        },
+        value: {
+          val: JS({ 'list': { val: [] } })
+        }
+      },
+      {
+        key: {
+          val: 'mT'
+        },
+        value: {
+          val: JS({ 'boolean': { val: false } })
+        }
+      }
+      ]
+    }
+  })
+}
+].forEach(function(testCase) {
+  // Test basic JSValue canonicalization. Pure JSValues are used.
+  // TODO(alexfandrianto): It would be good to test a JSValue inside another
+  // type. For example, []JSValue or a struct with JSValues.
+  test('canonicalize JSValue - basic functionality - ' +
+       testCase.name, function(t) {
+    var input = testCase.input;
+    var expected = testCase.output;
+    var expectedDeep = testCase.outputDeep;
 
-  for (var i = 0; i < tests.length; i++) {
-    var name = tests[i].name;
-    var input = tests[i].input;
-    var expected = tests[i].output;
-    var expectedDeep = tests[i].outputDeep;
     var type = types.JSVALUE;
 
     // The input canonicalizes to the expected output.
     var output = canonicalize.reduce(input, type);
-    t.deepEqual(output, expected, name);
+    t.deepEqual(output, expected, 'canon match');
 
     // Canonicalize is idempotent.
     var output2 = canonicalize.reduce(output, type);
-    t.deepEqual(output2, output, name + ' - idempotent');
+    t.deepEqual(output2, output, 'idempotent');
 
     // The deep wrapped output should also match the expected deep output.
     var outputDeep = canonicalize.fill(input, type);
-    t.deepEqual(outputDeep, expectedDeep, name + ' - deep');
+    t.deepEqual(outputDeep, expectedDeep, 'deep');
 
     // This is also idempotent.
     var outputDeep2 = canonicalize.fill(outputDeep, type);
-    t.deepEqual(outputDeep2, outputDeep, name + ' - deep idempotent');
+    t.deepEqual(outputDeep2, outputDeep, 'deep idempotent');
 
     // DeepWrap(output) === outputDeep
     var outputToDeep = canonicalize.fill(output, type);
-    t.deepEqual(outputToDeep, outputDeep, ' - shallow to deep');
+    t.deepEqual(outputToDeep, outputDeep, 'shallow to deep');
 
     // Unwrap(outputDeep) === output
     var outputDeepToShallow = canonicalize.reduce(outputDeep, type);
-    t.deepEqual(outputDeepToShallow, output, ' - deep to shallow');
+    t.deepEqual(outputDeepToShallow, output, 'deep to shallow');
 
 
     // The type of the deep output must match. (Shallow lacks type.)
     var expectedTypeStr = stringify(type);
     var outputDeepTypeStr = stringify(outputDeep._type);
-    t.equal(outputDeepTypeStr, expectedTypeStr,
-      name + ' - top-level type match');
-  }
-  t.end();
+    t.equal(outputDeepTypeStr, expectedTypeStr, 'top-level type match');
+    t.end();
+  });
 });
 
-test('canonicalize struct - basic functionality', function(t) {
-  var OptStringType = new Type({
-    kind: kind.OPTIONAL,
-    elem: types.STRING
-  });
-  var OptStr = registry.lookupOrCreateConstructor(OptStringType);
-  var AnyListType = new Type({
-    kind: kind.LIST,
-    elem: types.ANY
-  });
-  var BoolListType = new Type({
-    kind: kind.LIST,
-    elem: types.BOOL
-  });
+var Float32 = registry.lookupOrCreateConstructor(types.FLOAT32);
+[
+  {
+    name: 'list w/ typed values',
+    input: [3, false, null, 'abc', undefined, new Float32(3.14)],
+    output: [3, false, null, 'abc', null, new Float32(3.14)],
+    outputDeep: JS({
+      'list': {
+        val: [
+          {
+            val: JS({
+              'number': { val: 3 }
+            })
+          },
+          {
+            val: JS({
+              'boolean': { val: false }
+            })
+          },
+          {
+            val: JS({
+              'null': {}
+            })
+          },
+          {
+            val: JS({
+              'string': { val: 'abc' }
+            })
+          },
+          {
+            val: JS({
+              'null': {}
+            })
+          },
+          {
+            val: new Float32(3.14)// any with wrapped float32
+          }
+        ]
+      }
+    })
+  },
+].forEach(function(testCase) {
+  var name = 'canonicalize JSValue - mixed JSValue and non-JSValue ' +
+    'functionality - ' + testCase.name;
+  test(name, function(t) {
+    var input = testCase.input;
+    var expected = testCase.output;
+    var expectedDeep = testCase.outputDeep;
+    var type = types.JSVALUE;
 
-  var ComplicatedStringStructType = new Type({
-    kind: kind.STRUCT,
-    fields: [
+    // The input canonicalizes to the expected output.
+    var output = canonicalize.reduce(input, type);
+    t.deepEqual(output, expected);
+
+    // Canonicalize is idempotent.
+    var output2 = canonicalize.reduce(output, type);
+    t.deepEqual(output2, output, 'idempotent');
+
+    // The deep wrapped output should also match the expected deep output.
+    var outputDeep = canonicalize.fill(input, type);
+    t.deepEqual(outputDeep, expectedDeep, 'deep');
+
+    // This is also idempotent.
+    var outputDeep2 = canonicalize.fill(outputDeep, type);
+    t.deepEqual(outputDeep2, outputDeep, 'deep idempotent');
+
+    // DeepWrap(output) === outputDeep
+    var outputToDeep = canonicalize.fill(output, type);
+    t.deepEqual(outputToDeep, outputDeep, 'shallow to deep');
+
+    // Unwrap(outputDeep) === output
+    var outputDeepToShallow = canonicalize.reduce(outputDeep, type);
+    t.deepEqual(outputDeepToShallow, output, 'deep to shallow');
+
+
+    // The type of the deep output must match. (Shallow lacks type.)
+    var expectedTypeStr = stringify(type);
+    var outputDeepTypeStr = stringify(outputDeep._type);
+    t.equal(outputDeepTypeStr, expectedTypeStr, 'top-level type match');
+    t.end();
+  });
+});
+
+var OptStringType = new Type({
+  kind: kind.OPTIONAL,
+  elem: types.STRING
+});
+var OptStr = registry.lookupOrCreateConstructor(OptStringType);
+var AnyListType = new Type({
+  kind: kind.LIST,
+  elem: types.ANY
+});
+var BoolListType = new Type({
+  kind: kind.LIST,
+  elem: types.BOOL
+});
+
+var ComplicatedStringStructType = new Type({
+  kind: kind.STRUCT,
+  fields: [
+    {
+      name: 'JSValueString',
+      type: types.ANY
+    },
+    {
+      name: 'WrappedString',
+      type: types.STRING
+    },
+    {
+      name: 'NativeString',
+      type: types.STRING
+    },
+    {
+      name: 'AnyString',
+      type: types.ANY
+    },
+    {
+      name: 'NullOptionalAny',
+      type: OptStringType
+    },
+    {
+      name: 'OptionalToString',
+      type: OptStringType
+    },
+    {
+      name: 'UndefinedToZeroString',
+      type: types.STRING
+    },
+    {
+      name: 'UndefinedToZeroStringAny',
+      type: types.STRING
+    }
+  ]
+});
+var ComplicatedBoolAnyListType = new Type({
+  kind: kind.STRUCT,
+  fields: [
+    {
+      name: 'BoolToAny',
+      type: BoolListType
+    },
+    {
+      name: 'BoolToBool',
+      type: BoolListType
+    },
+    {
+      name: 'AnyToBool',
+      type: AnyListType
+    },
+    {
+      name: 'AnyToAny',
+      type: AnyListType
+    }
+  ]
+});
+
+var Bool = registry.lookupOrCreateConstructor(types.BOOL);
+var Str = registry.lookupOrCreateConstructor(types.STRING);
+var ComplicatedStringStruct = registry.lookupOrCreateConstructor(
+  ComplicatedStringStructType);
+var ComplicatedBoolAnyList = registry.lookupOrCreateConstructor(
+  ComplicatedBoolAnyListType);
+[
+  {
+    name: 'empty object, no fields',
+    inputObject: {},
+    inputFields: [],
+    outputObject: {},
+    outputObjectDeep: {}
+  },
+  {
+    name: 'object w/ private properties, no fields',
+    inputObject: {_private: 'I persist!'},
+    inputFields: [],
+    outputObject: {_private: 'I persist!'},
+    outputObjectDeep: {_private: 'I persist!'}
+  },
+  {
+    name: 'normal object, no extra fields',
+    inputObject: {
+      a: 4,
+      b: 'can',
+      e: 'plan'
+    },
+    inputFields: [
+      {
+        name: 'A',
+        type: types.UINT32
+      },
+      {
+        name: 'B',
+        type: types.STRING
+      },
+      {
+        name: 'E',
+        type: types.ANY
+      },
+    ],
+    outputObject: {
+      a: 4,
+      b: 'can',
+      e: 'plan'       // JSValue in ANY has no wrapping in shallow mode.
+    },
+    outputObjectDeep: {
+      a: { val: 4 },
+      b: { val: 'can' },
+      e: {            // any
+        val: {        // INFERRED: JSValue(string).
+          string: { val: 'plan' }
+        }
+      }
+    }
+  },
+  {
+    name: 'empty object, some fields',
+    inputObject: {},
+    inputFields: [
+      {
+        name: 'Man',
+        type: types.ANY
+      },
+      {
+        name: 'Ban',
+        type: types.BOOL
+      },
+      {
+        name: 'Dan',
+        type: types.COMPLEX64
+      }
+    ],
+    outputObject: {
+      man: null,
+      ban: false,
+      dan: new Complex(0, 0)
+    },
+    outputObjectDeep: {
+      man: { val: null },
+      ban: { val: false },
+      dan: { val: new Complex(0, 0) }
+    }
+  },
+  {
+    name: 'struct with internal string/any',
+    inputObject: new ComplicatedStringStruct({
+      jSValueString: 'go as JSValue',
+      wrappedString: new Str('overly wrapped input'),
+      nativeString: 'true string',
+      anyString: new Str('string any'),
+      nullOptionalAny: null,
+      optionalToString: new OptStr('non-empty string'),
+      undefinedToZeroString: undefined,
+      undefinedToZeroStringAny: undefined
+    }),
+    inputFields: [
       {
         name: 'JSValueString',
         type: types.ANY
       },
       {
         name: 'WrappedString',
-        type: types.STRING
-      },
-      {
-        name: 'NativeString',
-        type: types.STRING
-      },
-      {
-        name: 'AnyString',
         type: types.ANY
       },
       {
+        name: 'NativeString',
+        type: types.ANY
+      },
+      {
+        name: 'AnyString',
+        type: types.STRING
+      },
+      {
         name: 'NullOptionalAny',
-        type: OptStringType
+        type: types.ANY
       },
       {
         name: 'OptionalToString',
-        type: OptStringType
+        type: types.STRING
       },
       {
         name: 'UndefinedToZeroString',
@@ -348,16 +493,60 @@
       },
       {
         name: 'UndefinedToZeroStringAny',
-        type: types.STRING
+        type: types.ANY
       }
-    ]
-  });
-  var ComplicatedBoolAnyListType = new Type({
-    kind: kind.STRUCT,
-    fields: [
+    ],
+    outputObject: {
+      jSValueString: 'go as JSValue',
+      wrappedString: new Str('overly wrapped input'),
+      nativeString: new Str('true string'),
+      anyString: 'string any',
+      nullOptionalAny: {
+        val: null
+      },
+      optionalToString: 'non-empty string',
+      undefinedToZeroString: '',
+      undefinedToZeroStringAny: new Str('')
+    },
+    outputObjectDeep: {
+      jSValueString: {
+        val: {
+          string: {
+            val: 'go as JSValue'
+          }
+        }
+      },
+      wrappedString: {
+        val: new Str('overly wrapped input')
+      },
+      nativeString: {
+        val: new Str('true string')
+      },
+      anyString: new Str('string any'),
+      nullOptionalAny: {
+        val: {
+          val: null
+        }
+      },
+      optionalToString: new Str('non-empty string'),
+      undefinedToZeroString: new Str(''),
+      undefinedToZeroStringAny: {
+        val: new Str('')
+      }
+    }
+  },
+  {
+    name: 'struct with internal []any and []bool',
+    inputObject: new ComplicatedBoolAnyList({
+      boolToAny: [true, false, true],
+      boolToBool: [false, false],
+      anyToBool: [new Bool(true), new Bool(true), new Bool(false)],
+      anyToAny: [new Bool(true)]
+    }),
+    inputFields: [
       {
         name: 'BoolToAny',
-        type: BoolListType
+        type: AnyListType
       },
       {
         name: 'BoolToBool',
@@ -365,459 +554,259 @@
       },
       {
         name: 'AnyToBool',
-        type: AnyListType
+        type: BoolListType
       },
       {
         name: 'AnyToAny',
         type: AnyListType
+      },
+    ],
+    outputObject: {
+      boolToAny: [new Bool(true), new Bool(false), new Bool(true)],
+      boolToBool: [false, false],
+      anyToBool: [true, true, false],
+      anyToAny: [new Bool(true)]
+    },
+    outputObjectDeep: {
+      boolToAny: {
+        val: [
+          { val: new Bool(true) },
+          { val: new Bool(false) },
+          { val: new Bool(true) }
+        ]
+      },
+      boolToBool: {
+        val: [new Bool(false), new Bool(false)]
+      },
+      anyToBool: {
+        val: [new Bool(true), new Bool(true), new Bool(false)]
+      },
+      anyToAny: {
+        val: [{ val: new Bool(true) }]
+      },
+    }
+  },
+  {
+    name: 'simple zero values',
+    inputObject: {},
+    inputFields: [
+      {
+        name: 'Enum',
+        type: {
+          kind: kind.ENUM,
+          labels: ['Sunday', 'Monday', 'Tuesday']
+        }
+      },
+      {
+        name: 'Optional',
+        type: {
+          kind: kind.OPTIONAL,
+          elem: types.STRING
+        }
+      },
+      {
+        name: 'String',
+        type: types.STRING
+      },
+      {
+        name: 'Array',
+        type: {
+          kind: kind.ARRAY,
+          elem: types.BOOL,
+          len: 3
+        }
+      },
+      {
+        name: 'List',
+        type: {
+          kind: kind.LIST,
+          elem: types.BOOL
+        }
+      },
+      {
+        name: 'Set',
+        type: {
+          kind: kind.SET,
+          key: types.UINT64
+        }
+      },
+      {
+        name: 'Map',
+        type: {
+          kind: kind.MAP,
+          key: types.STRING,
+          elem: types.STRING
+        }
+      },
+      {
+        name: 'TypeObject',
+        type: types.TYPEOBJECT
       }
-    ]
-  });
-
-  var Bool = registry.lookupOrCreateConstructor(types.BOOL);
-  var Str = registry.lookupOrCreateConstructor(types.STRING);
-  var ComplicatedStringStruct = registry.lookupOrCreateConstructor(
-    ComplicatedStringStructType);
-  var ComplicatedBoolAnyList = registry.lookupOrCreateConstructor(
-    ComplicatedBoolAnyListType);
-
-  var tests = [
-    {
-      name: 'empty object, no fields',
-      inputObject: {},
-      inputFields: [],
-      outputObject: {},
-      outputObjectDeep: {}
+    ],
+    outputObject: {
+      'enum': 'Sunday',
+      'optional': null,
+      'string': '',
+      'array': [false, false, false],
+      'list': [],
+      'set': new Set(),
+      'map': new Map(),
+      'typeObject': types.ANY
     },
-    {
-      name: 'object w/ private properties, no fields',
-      inputObject: {_private: 'I persist!'},
-      inputFields: [],
-      outputObject: {_private: 'I persist!'},
-      outputObjectDeep: {_private: 'I persist!'}
-    },
-    {
-      name: 'normal object, no extra fields',
-      inputObject: {
-        a: 4,
-        b: 'can',
-        e: 'plan'
+    outputObjectDeep: {
+      'enum': { val: 'Sunday' },
+      'optional': { val: null },
+      'string': { val: '' },
+      'array': {
+        val: [
+          { val: false },
+          { val: false },
+          { val: false }
+        ]
       },
-      inputFields: [
-        {
-          name: 'A',
-          type: types.UINT32
-        },
-        {
-          name: 'B',
-          type: types.STRING
-        },
-        {
-          name: 'E',
-          type: types.ANY
-        },
-      ],
-      outputObject: {
-        a: 4,
-        b: 'can',
-        e: 'plan'       // JSValue in ANY has no wrapping in shallow mode.
+      'list': {
+        val: []
       },
-      outputObjectDeep: {
-        a: { val: 4 },
-        b: { val: 'can' },
-        e: {            // any
-          val: {        // INFERRED: JSValue(string).
-            string: { val: 'plan' }
-          }
+      'set': {
+        val: new Set()
+      },
+      'map': {
+        val: new Map()
+      },
+      'typeObject': types.ANY
+    }
+  },
+  {
+    name: 'byte slice',
+    inputObject: {},
+    inputFields: [
+      {
+        name: 'ByteSlice',
+        type: {
+          kind: kind.LIST,
+          elem: types.BYTE
         }
       }
+    ],
+    outputObject: {
+      'byteSlice': new Uint8Array()
     },
-    {
-      name: 'empty object, some fields',
-      inputObject: {},
-      inputFields: [
-        {
-          name: 'Man',
-          type: types.ANY
-        },
-        {
-          name: 'Ban',
-          type: types.BOOL
-        },
-        {
-          name: 'Dan',
-          type: types.COMPLEX64
-        }
-      ],
-      outputObject: {
-        man: null,
-        ban: false,
-        dan: new Complex(0, 0)
-      },
-      outputObjectDeep: {
-        man: { val: null },
-        ban: { val: false },
-        dan: { val: new Complex(0, 0) }
-      }
-    },
-    {
-      name: 'struct with internal string/any',
-      inputObject: new ComplicatedStringStruct({
-        jSValueString: 'go as JSValue',
-        wrappedString: new Str('overly wrapped input'),
-        nativeString: 'true string',
-        anyString: new Str('string any'),
-        nullOptionalAny: null,
-        optionalToString: new OptStr('non-empty string'),
-        undefinedToZeroString: undefined,
-        undefinedToZeroStringAny: undefined
-      }),
-      inputFields: [
-        {
-          name: 'JSValueString',
-          type: types.ANY
-        },
-        {
-          name: 'WrappedString',
-          type: types.ANY
-        },
-        {
-          name: 'NativeString',
-          type: types.ANY
-        },
-        {
-          name: 'AnyString',
-          type: types.STRING
-        },
-        {
-          name: 'NullOptionalAny',
-          type: types.ANY
-        },
-        {
-          name: 'OptionalToString',
-          type: types.STRING
-        },
-        {
-          name: 'UndefinedToZeroString',
-          type: types.STRING
-        },
-        {
-          name: 'UndefinedToZeroStringAny',
-          type: types.ANY
-        }
-      ],
-      outputObject: {
-        jSValueString: 'go as JSValue',
-        wrappedString: new Str('overly wrapped input'),
-        nativeString: new Str('true string'),
-        anyString: 'string any',
-        nullOptionalAny: {
-          val: null
-        },
-        optionalToString: 'non-empty string',
-        undefinedToZeroString: '',
-        undefinedToZeroStringAny: new Str('')
-      },
-      outputObjectDeep: {
-        jSValueString: {
-          val: {
-            string: {
-              val: 'go as JSValue'
-            }
-          }
-        },
-        wrappedString: {
-          val: new Str('overly wrapped input')
-        },
-        nativeString: {
-          val: new Str('true string')
-        },
-        anyString: new Str('string any'),
-        nullOptionalAny: {
-          val: {
-            val: null
-          }
-        },
-        optionalToString: new Str('non-empty string'),
-        undefinedToZeroString: new Str(''),
-        undefinedToZeroStringAny: {
-          val: new Str('')
-        }
-      }
-    },
-    {
-      name: 'struct with internal []any and []bool',
-      inputObject: new ComplicatedBoolAnyList({
-        boolToAny: [true, false, true],
-        boolToBool: [false, false],
-        anyToBool: [new Bool(true), new Bool(true), new Bool(false)],
-        anyToAny: [new Bool(true)]
-      }),
-      inputFields: [
-        {
-          name: 'BoolToAny',
-          type: AnyListType
-        },
-        {
-          name: 'BoolToBool',
-          type: BoolListType
-        },
-        {
-          name: 'AnyToBool',
-          type: BoolListType
-        },
-        {
-          name: 'AnyToAny',
-          type: AnyListType
-        },
-      ],
-      outputObject: {
-        boolToAny: [new Bool(true), new Bool(false), new Bool(true)],
-        boolToBool: [false, false],
-        anyToBool: [true, true, false],
-        anyToAny: [new Bool(true)]
-      },
-      outputObjectDeep: {
-        boolToAny: {
-          val: [
-            { val: new Bool(true) },
-            { val: new Bool(false) },
-            { val: new Bool(true) }
-          ]
-        },
-        boolToBool: {
-          val: [new Bool(false), new Bool(false)]
-        },
-        anyToBool: {
-          val: [new Bool(true), new Bool(true), new Bool(false)]
-        },
-        anyToAny: {
-          val: [{ val: new Bool(true) }]
-        },
-      }
-    },
-    {
-      name: 'simple zero values',
-      inputObject: {},
-      inputFields: [
-        {
-          name: 'Enum',
-          type: {
-            kind: kind.ENUM,
-            labels: ['Sunday', 'Monday', 'Tuesday']
-          }
-        },
-        {
-          name: 'Optional',
-          type: {
-            kind: kind.OPTIONAL,
-            elem: types.STRING
-          }
-        },
-        {
-          name: 'String',
-          type: types.STRING
-        },
-        {
-          name: 'Array',
-          type: {
-            kind: kind.ARRAY,
-            elem: types.BOOL,
-            len: 3
-          }
-        },
-        {
-          name: 'List',
-          type: {
-            kind: kind.LIST,
-            elem: types.BOOL
-          }
-        },
-        {
-          name: 'Set',
-          type: {
-            kind: kind.SET,
-            key: types.UINT64
-          }
-        },
-        {
-          name: 'Map',
-          type: {
-            kind: kind.MAP,
-            key: types.STRING,
-            elem: types.STRING
-          }
-        },
-        {
-          name: 'TypeObject',
-          type: types.TYPEOBJECT
-        }
-      ],
-      outputObject: {
-        'enum': 'Sunday',
-        'optional': null,
-        'string': '',
-        'array': [false, false, false],
-        'list': [],
-        'set': new Set(),
-        'map': new Map(),
-        'typeObject': types.ANY
-      },
-      outputObjectDeep: {
-        'enum': { val: 'Sunday' },
-        'optional': { val: null },
-        'string': { val: '' },
-        'array': {
-          val: [
-            { val: false },
-            { val: false },
-            { val: false }
-          ]
-        },
-        'list': {
-          val: []
-        },
-        'set': {
-          val: new Set()
-        },
-        'map': {
-          val: new Map()
-        },
-        'typeObject': types.ANY
-      }
-    },
-    {
-      name: 'byte slice',
-      inputObject: {},
-      inputFields: [
-        {
-          name: 'ByteSlice',
-          type: {
-            kind: kind.LIST,
-            elem: types.BYTE
-          }
-        }
-      ],
-      outputObject: {
-        'byteSlice': new Uint8Array()
-      },
-      outputObjectDeep: {
-        'byteSlice': {
-          val: new Uint8Array()
-        }
-      }
-    },
-    {
-      name: 'byte array',
-      inputObject: {},
-      inputFields: [
-        {
-          name: 'ByteArray',
-          type: {
-            kind: kind.ARRAY,
-            elem: types.BYTE,
-            len: 4
-          }
-        }
-      ],
-      outputObject: {
-        'byteArray': new Uint8Array([0, 0, 0, 0])
-      },
-      outputObjectDeep: {
-        'byteArray': {
-          val: new Uint8Array([0, 0, 0, 0])
-        }
-      }
-    },
-    {
-      name: 'recursive canonicalize - struct, union',
-      inputObject: {},
-      inputFields: [
-        {
-          name: 'Struct',
-          type: {
-            kind: kind.STRUCT,
-            fields: [
-              {
-                name: 'A',
-                type: types.BOOL
-              },
-              {
-                name: 'B',
-                type: types.UINT64
-              }
-            ]
-          }
-        },
-        {
-          name: 'Union',
-          type: {
-            kind: kind.UNION,
-            fields: [
-              {
-                name: 'A',
-                type: types.BOOL
-              },
-              {
-                name: 'B',
-                type: types.UINT64
-              }
-            ]
-          }
-        }
-      ],
-      outputObject: {
-        'struct': {
-          a: false,
-          b: new BigInt(0, new Uint8Array())
-        },
-        'union': {
-          a: false
-        }
-      },
-      outputObjectDeep: {
-        'struct': {
-          a: { val: false },
-          b: { val: new BigInt(0, new Uint8Array()) }
-        },
-        'union': {
-          a: { val: false }
-        }
+    outputObjectDeep: {
+      'byteSlice': {
+        val: new Uint8Array()
       }
     }
-  ];
-
-  for (var i = 0; i < tests.length; i++) {
-    var type = new Type({
-      kind: kind.STRUCT,
-      fields: tests[i].inputFields
-    });
-    runNativeWireTest(tests[i], type, t);
+  },
+  {
+    name: 'byte array',
+    inputObject: {},
+    inputFields: [
+      {
+        name: 'ByteArray',
+        type: {
+          kind: kind.ARRAY,
+          elem: types.BYTE,
+          len: 4
+        }
+      }
+    ],
+    outputObject: {
+      'byteArray': new Uint8Array([0, 0, 0, 0])
+    },
+    outputObjectDeep: {
+      'byteArray': {
+        val: new Uint8Array([0, 0, 0, 0])
+      }
+    }
+  },
+  {
+    name: 'recursive canonicalize - struct, union',
+    inputObject: {},
+    inputFields: [
+      {
+        name: 'Struct',
+        type: {
+          kind: kind.STRUCT,
+          fields: [
+            {
+              name: 'A',
+              type: types.BOOL
+            },
+            {
+              name: 'B',
+              type: types.UINT64
+            }
+          ]
+        }
+      },
+      {
+        name: 'Union',
+        type: {
+          kind: kind.UNION,
+          fields: [
+            {
+              name: 'A',
+              type: types.BOOL
+            },
+            {
+              name: 'B',
+              type: types.UINT64
+            }
+          ]
+        }
+      }
+    ],
+    outputObject: {
+      'struct': {
+        a: false,
+        b: new BigInt(0, new Uint8Array())
+      },
+      'union': {
+        a: false
+      }
+    },
+    outputObjectDeep: {
+      'struct': {
+        a: { val: false },
+        b: { val: new BigInt(0, new Uint8Array()) }
+      },
+      'union': {
+        a: { val: false }
+      }
+    }
   }
-  t.end();
+].forEach(function(testCase) {
+  test('canonicalize struct - basic functionality - ' + testCase.name,
+       function(t) {
+     var type = new Type({
+       kind: kind.STRUCT,
+       fields: testCase.inputFields
+     });
+     runNativeWireTest(testCase, type, t);
+     t.end();
+   });
 });
 
 function runNativeWireTest(test, type, t) {
-  var name = test.name;
   var input = test.inputObject;
   var expected = test.outputObject;
   var expectedDeep = test.outputObjectDeep;
 
   // The input object and its fields canonicalize to the expected output.
   var output = canonicalize.reduce(input, type);
-  t.deepEqual(output, expected, name);
+  t.deepEqual(output, expected);
 
   // Canonicalize is idempotent.
   var output2 = canonicalize.reduce(output, type);
-  t.deepEqual(output2, output, name + ' - idempotent');
+  t.deepEqual(output2, output, 'idempotent');
 
   // The deep wrapped output should also match the expected deep output.
   var outputDeep = canonicalize.fill(input, type);
-  t.deepEqual(outputDeep, expectedDeep, name + ' - deep');
+  t.deepEqual(outputDeep, expectedDeep, 'deep');
 
   // This is also idempotent.
   var outputDeep2 = canonicalize.fill(outputDeep, type);
-  t.deepEqual(outputDeep2, outputDeep, name + ' - deep idempotent');
+  t.deepEqual(outputDeep2, outputDeep, 'deep idempotent');
 
   // DeepWrap(output) === outputDeep
   var outputToDeep = canonicalize.fill(output, type);
@@ -827,222 +816,218 @@
   var outputDeepToShallow = canonicalize.reduce(outputDeep, type);
   t.deepEqual(outputDeepToShallow, output, ' - deep to shallow');
 }
-
-test('canonicalize union - basic functionality', function(t) {
-  var tests = [
-    {
-      name: 'filled union A, some fields',
-      inputObject: {
-        a: 4
-      },
-      inputFields: [
-        {
-          name: 'A',
-          type: types.UINT32
-        },
-        {
-          name: 'B',
-          type: types.STRING
-        },
-        {
-          name: 'E',
-          type: types.ANY
-        }
-      ],
-      outputObject: {
-        a: 4
-      },
-      outputObjectDeep: {
-        a: {
-          val: 4
-        }
-      }
+[
+  {
+    name: 'filled union A, some fields',
+    inputObject: {
+      a: 4
     },
-    {
-      name: 'filled union E, some fields',
-      inputObject: {
-        e: [4, 'asdf']
+    inputFields: [
+      {
+        name: 'A',
+        type: types.UINT32
       },
-      inputFields: [
-        {
-          name: 'A',
-          type: types.UINT32
-        },
-        {
-          name: 'B',
-          type: types.STRING
-        },
-        {
-          name: 'E',
-          type: types.ANY
-        }
-      ],
-      outputObject: {         // any with []JSValue
-        e: [4, 'asdf']
+      {
+        name: 'B',
+        type: types.STRING
       },
-      outputObjectDeep: {
-        e: {                  // any
-          val: {              // INFERRED: JSValue(list)
-            list: {
-              val: [
-                {
-                  val: {      // any
-                    number: { // JSValue(float64)
-                      val: 4
-                    }
-                  }
-                },
-                {
-                  val: {      // any
-                    string: { // JSValue(string)
-                      val: 'asdf'
-                    }
+      {
+        name: 'E',
+        type: types.ANY
+      }
+    ],
+    outputObject: {
+      a: 4
+    },
+    outputObjectDeep: {
+      a: {
+        val: 4
+      }
+    }
+  },
+  {
+    name: 'filled union E, some fields',
+    inputObject: {
+      e: [4, 'asdf']
+    },
+    inputFields: [
+      {
+        name: 'A',
+        type: types.UINT32
+      },
+      {
+        name: 'B',
+        type: types.STRING
+      },
+      {
+        name: 'E',
+        type: types.ANY
+      }
+    ],
+    outputObject: {         // any with []JSValue
+      e: [4, 'asdf']
+    },
+    outputObjectDeep: {
+      e: {                  // any
+        val: {              // INFERRED: JSValue(list)
+          list: {
+            val: [
+              {
+                val: {      // any
+                  number: { // JSValue(float64)
+                    val: 4
                   }
                 }
-              ]
-            }
+              },
+              {
+                val: {      // any
+                  string: { // JSValue(string)
+                    val: 'asdf'
+                  }
+                }
+              }
+            ]
           }
         }
       }
-    },
-    {
-      name: 'filled union with explicitly undefined fields',
-      inputObject: {
-        a: undefined,
-        b: 'and',
-        e: undefined
-      },
-      inputFields: [
-        {
-          name: 'A',
-          type: types.UINT32
-        },
-        {
-          name: 'B',
-          type: types.STRING
-        },
-        {
-          name: 'E',
-          type: types.ANY
-        }
-      ],
-      outputObject: {
-        b: 'and'
-      },
-      outputObjectDeep: {
-        b: { val: 'and' }
-      }
-    },
-    {
-      name: 'union with private properties',
-      inputObject: {
-        a: undefined,
-        b: 'foo',
-        _private1: 'I LIVE!',
-        _private2: 'ME TOO!'
-      },
-      inputFields: [
-        {
-          name: 'A',
-          type: types.UINT32
-        },
-        {
-          name: 'B',
-          type: types.STRING
-        },
-        {
-          name: 'E',
-          type: types.ANY
-        }
-      ],
-      outputObject: {
-        b: 'foo',
-        _private1: 'I LIVE!',
-        _private2: 'ME TOO!'
-      },
-      outputObjectDeep: {
-        b: { val: 'foo' },
-        _private1: 'I LIVE!',
-        _private2: 'ME TOO!'
-      }
     }
-  ];
-
-  for (var i = 0; i < tests.length; i++) {
+  },
+  {
+    name: 'filled union with explicitly undefined fields',
+    inputObject: {
+      a: undefined,
+      b: 'and',
+      e: undefined
+    },
+    inputFields: [
+      {
+        name: 'A',
+        type: types.UINT32
+      },
+      {
+        name: 'B',
+        type: types.STRING
+      },
+      {
+        name: 'E',
+        type: types.ANY
+      }
+    ],
+    outputObject: {
+      b: 'and'
+    },
+    outputObjectDeep: {
+      b: { val: 'and' }
+    }
+  },
+  {
+    name: 'union with private properties',
+    inputObject: {
+      a: undefined,
+      b: 'foo',
+      _private1: 'I LIVE!',
+      _private2: 'ME TOO!'
+    },
+    inputFields: [
+      {
+        name: 'A',
+        type: types.UINT32
+      },
+      {
+        name: 'B',
+        type: types.STRING
+      },
+      {
+        name: 'E',
+        type: types.ANY
+      }
+    ],
+    outputObject: {
+      b: 'foo',
+      _private1: 'I LIVE!',
+      _private2: 'ME TOO!'
+    },
+    outputObjectDeep: {
+      b: { val: 'foo' },
+      _private1: 'I LIVE!',
+      _private2: 'ME TOO!'
+    }
+  }
+].forEach(function(testCase) {
+  test('canonicalize union - basic functionality ' + testCase.name,
+       function(t) {
     var type = new Type({
       kind: kind.UNION,
-      fields: tests[i].inputFields
+      fields: testCase.inputFields
     });
-    runNativeWireTest(tests[i], type, t);
-  }
-  t.end();
+    runNativeWireTest(testCase, type, t);
+    t.end();
+  });
 });
 
+var loopyList = {
+  kind: kind.LIST
+};
+loopyList.elem = loopyList;
+var expectedLoopyList = {
+  name: '',
+  kind: kind.LIST
+};
+expectedLoopyList.elem = expectedLoopyList;
+
 // Ensures that valid types don't error out when canonicalizing.
-test('canonicalize type - basic functionality', function(t) {
-  var loopyList = {
-    kind: kind.LIST
-  };
-  loopyList.elem = loopyList;
-  var expectedLoopyList = {
-    name: '',
-    kind: kind.LIST
-  };
-  expectedLoopyList.elem = expectedLoopyList;
-
-  var tests = [
-    {
-      name: 'undefined type => any',
-      inputType: undefined,
-      outputType: types.ANY
+[
+  {
+    name: 'undefined type => any',
+    inputType: undefined,
+    outputType: types.ANY
+  },
+  {
+    name: 'simple list',
+    inputType: {
+      kind: kind.LIST,
+      elem: types.INT16
     },
-    {
-      name: 'simple list',
-      inputType: {
-        kind: kind.LIST,
-        elem: types.INT16
-      },
-      outputType: {
-        name: '',
-        kind: kind.LIST,
-        elem: types.INT16
-      }
-    },
-    {
-      name: 'typeobject',
-      inputType: {
-        kind: kind.TYPEOBJECT
-      },
-      outputType: types.TYPEOBJECT
-    },
-    {
-      name: 'loopyList',
-      inputType: loopyList,
-      outputType: expectedLoopyList
+    outputType: {
+      name: '',
+      kind: kind.LIST,
+      elem: types.INT16
     }
-  ];
-
-  for (var i = 0; i < tests.length; i++) {
-    var name = tests[i].name;
-    var input = tests[i].inputType;
-    var expected = tests[i].outputType;
+  },
+  {
+    name: 'typeobject',
+    inputType: {
+      kind: kind.TYPEOBJECT
+    },
+    outputType: types.TYPEOBJECT
+  },
+  {
+    name: 'loopyList',
+    inputType: loopyList,
+    outputType: expectedLoopyList
+  }
+].forEach(function(testCase) {
+  test('canonicalize type - basic functionality - ' + testCase.name,
+       function(t) {
+    var input = testCase.inputType;
+    var expected = testCase.outputType;
 
     // The input object and its fields canonicalize to the expected output.
     // Since TypeObjects can be recursive, it's best to stringify them.
     var output = canonicalize.type(input);
     var outputStr = stringify(output);
     var expectedStr = stringify(expected);
-    t.equal(outputStr, expectedStr, name);
+    t.equal(outputStr, expectedStr);
 
     // Canonicalize Type is idempotent.
     var output2 = canonicalize.type(output);
     var output2Str = stringify(output2);
-    t.equal(output2Str, expectedStr, name + ' - idempotent');
+    t.equal(output2Str, expectedStr, 'idempotent');
 
     // Post-canonicalization, the type is still a TypeObject.
-    t.deepEqual(output._type, types.TYPEOBJECT, name + ' - type is TypeObject');
-  }
-  t.end();
+    t.deepEqual(output._type, types.TYPEOBJECT, 'type is TypeObject');
+    t.end();
+  });
 });
 
 // TODO(alexfandrianto): Add a general idempotency test since we always expect
@@ -1052,144 +1037,141 @@
 // TODO(alexfandrianto): Perhaps this test is not necessary anymore; we have
 // other coverage, and it seems like it's just checking that deep wrap converts
 // to shallow wrap.
-test('canonicalize deep to shallow - basic functionality', function(t) {
-  var Int16 = registry.lookupOrCreateConstructor(types.INT16);
-  var Int64 = registry.lookupOrCreateConstructor(types.INT64);
-  var Uint32 = registry.lookupOrCreateConstructor(types.UINT32);
-  var Complex64 = registry.lookupOrCreateConstructor(types.COMPLEX64);
-  var Str = registry.lookupOrCreateConstructor(types.STRING);
-  var Uint32Uint32Map = registry.lookupOrCreateConstructor({
-    kind: kind.MAP,
-    name: '',
-    key: types.INT32,
-    elem: types.INT32
-  });
-  var KindNameStruct = registry.lookupOrCreateConstructor({
-    kind: kind.STRUCT,
-    name: '',
-    fields: [
-      {
-        name: 'kind',
-        type: types.UINT32
-      },
-      {
-        name: 'Name',
-        type: types.STRING
-      }
-    ]
-  });
-  var ABUnion = registry.lookupOrCreateConstructor({
-    kind: kind.UNION,
-    name: '',
-    fields: [
-      {
-        name: 'A',
-        type: types.UINT32
-      },
-      {
-        name: 'B',
-        type: types.STRING
-      }
-    ]
-  });
-  var ABStruct = registry.lookupOrCreateConstructor({
-    kind: kind.STRUCT,
-    name: '',
-    fields: [
-      {
-        name: 'A',
-        type: types.UINT32
-      },
-      {
-        name: 'B',
-        type: types.STRING
-      }
-    ]
-  });
-  var AnyStrStruct = registry.lookupOrCreateConstructor({
-    kind: kind.STRUCT,
-    name: '',
-    fields: [
-      {
-        name: 'Any',
-        type: types.ANY
-      },
-      {
-        name: 'Normal',
-        type: types.STRING
-      }
-    ]
-  });
-
-  var tests = [
+var Int16 = registry.lookupOrCreateConstructor(types.INT16);
+var Int64 = registry.lookupOrCreateConstructor(types.INT64);
+var Uint32 = registry.lookupOrCreateConstructor(types.UINT32);
+var Complex64 = registry.lookupOrCreateConstructor(types.COMPLEX64);
+var Uint32Uint32Map = registry.lookupOrCreateConstructor({
+  kind: kind.MAP,
+  name: '',
+  key: types.INT32,
+  elem: types.INT32
+});
+var KindNameStruct = registry.lookupOrCreateConstructor({
+  kind: kind.STRUCT,
+  name: '',
+  fields: [
     {
-      name: 'top-level only',
-      input: new Int16(5, true),
-      expected: new Int16(5)
-    },
-    {
-      name: 'wrapped big int',
-      input: new Int64(new BigInt(1, new Uint8Array([0x10, 0xff])), true),
-      expected: new Int64(new BigInt(1, new Uint8Array([0x10, 0xff])))
-    },
-    {
-      name: 'wrapped complex',
-      input: new Complex64(new Complex(4, 5), true),
-      expected: new Complex64(new Complex(4, 5))
-    },
-    {
-      name: 'map',
-      input: new Uint32Uint32Map(new Map([[3, 4], [6, 3]]), true),
-      expected: new Uint32Uint32Map(new Map([[3, 4], [6, 3]]))
-    },
-    {
-      name: 'fake typeobject',
-      input: new KindNameStruct({
-        kind: new Uint32(3, true),
-        name: new Str('Boolean', true)
-      }, true),
-      expected: {
-        kind: 3,
-        name: 'Boolean'
-      }
-    },
-    {
-      name: 'union',
-      input: new ABUnion({
-        b: new Str('abc', true),
-      }, true),
-      expected: {
-        b: 'abc'
-      }
-    },
-    {
-      name: 'struct',
-      input: new ABStruct({
-        a: new Uint32(3, true),
-        b: new Str('abc', true)
-      }, true),
-      expected: {
-        a: 3,
-        b: 'abc',
-      }
-    },
-    {
-      name: 'Struct with ANY',
-      input: new AnyStrStruct({
-        any: new Str('wrapped', true),
-        normal: new Str('shallow', true)
-      }, true),
-      expected: {
-        any: new Str('wrapped'),
-        normal: 'shallow'
-      }
-    }
-  ];
-
-  for (var i = 0; i < tests.length; i++) {
-    testDeepWrapToUnwrap(t, tests[i]);
+    name: 'kind',
+    type: types.UINT32
+  },
+  {
+    name: 'Name',
+    type: types.STRING
   }
-  t.end();
+  ]
+});
+var ABUnion = registry.lookupOrCreateConstructor({
+  kind: kind.UNION,
+  name: '',
+  fields: [
+    {
+    name: 'A',
+    type: types.UINT32
+  },
+  {
+    name: 'B',
+    type: types.STRING
+  }
+  ]
+});
+var ABStruct = registry.lookupOrCreateConstructor({
+  kind: kind.STRUCT,
+  name: '',
+  fields: [
+    {
+    name: 'A',
+    type: types.UINT32
+  },
+  {
+    name: 'B',
+    type: types.STRING
+  }
+  ]
+});
+var AnyStrStruct = registry.lookupOrCreateConstructor({
+  kind: kind.STRUCT,
+  name: '',
+  fields: [
+    {
+    name: 'Any',
+    type: types.ANY
+  },
+  {
+    name: 'Normal',
+    type: types.STRING
+  }
+  ]
+});
+[
+  {
+    name: 'top-level only',
+    input: new Int16(5, true),
+    expected: new Int16(5)
+  },
+  {
+    name: 'wrapped big int',
+    input: new Int64(new BigInt(1, new Uint8Array([0x10, 0xff])), true),
+    expected: new Int64(new BigInt(1, new Uint8Array([0x10, 0xff])))
+  },
+  {
+    name: 'wrapped complex',
+    input: new Complex64(new Complex(4, 5), true),
+    expected: new Complex64(new Complex(4, 5))
+  },
+  {
+    name: 'map',
+    input: new Uint32Uint32Map(new Map([[3, 4], [6, 3]]), true),
+    expected: new Uint32Uint32Map(new Map([[3, 4], [6, 3]]))
+  },
+  {
+    name: 'fake typeobject',
+    input: new KindNameStruct({
+      kind: new Uint32(3, true),
+      name: new Str('Boolean', true)
+    }, true),
+    expected: {
+      kind: 3,
+      name: 'Boolean'
+    }
+  },
+  {
+    name: 'union',
+    input: new ABUnion({
+      b: new Str('abc', true),
+    }, true),
+    expected: {
+      b: 'abc'
+    }
+  },
+  {
+    name: 'struct',
+    input: new ABStruct({
+      a: new Uint32(3, true),
+      b: new Str('abc', true)
+    }, true),
+    expected: {
+      a: 3,
+      b: 'abc',
+    }
+  },
+  {
+    name: 'Struct with ANY',
+    input: new AnyStrStruct({
+      any: new Str('wrapped', true),
+      normal: new Str('shallow', true)
+    }, true),
+    expected: {
+      any: new Str('wrapped'),
+      normal: 'shallow'
+    }
+  }
+].forEach(function(testCase) {
+  test('canonicalize deep to shallow - basic functionality ' + testCase.name,
+       function(t) {
+    testDeepWrapToUnwrap(t, testCase);
+    t.end();
+  });
 });
 
 // TODO(alexfandrianto): DeepWrapToUnwrap can be expanded to test more, just
@@ -1197,7 +1179,6 @@
 // canonicalization, since it includes more types than just struct/union.
 // So the TODO is to convert this into a basic canonicalization test.
 function testDeepWrapToUnwrap(t, test) {
-  var name = test.name;
   var input = test.input;
   var expected = test.expected;
 
@@ -1207,13 +1188,13 @@
   // Compare with stringify; the output/expected could be recursive.
   var expectedStr = stringify(expected);
   var outputStr = stringify(output);
-  t.equal(outputStr, expectedStr, name);
+  t.equal(outputStr, expectedStr);
 
   // The types must also match.
   var type = input._type;
   var expectedTypeStr = stringify(type);
   var outputTypeStr = stringify(output._type);
-  t.equal(outputTypeStr, expectedTypeStr, name + ' - top-level type match');
+  t.equal(outputTypeStr, expectedTypeStr, 'top-level type match');
 }
 
 
@@ -1222,288 +1203,276 @@
 // to their base type.
 // This test supplements the cross-language conversion tests cases in
 // test-vom-compatible.js
-test('canonicalize conversion - success', function(t) {
-  var AnyListType = new Type({
-    kind: kind.LIST,
-    elem: types.ANY
-  });
-  var OptStringType = new Type({
-    kind: kind.OPTIONAL,
-    elem: types.STRING
-  });
-  var StringListType = new Type({
-    kind: kind.LIST,
-    elem: types.STRING
-  });
-  var ByteListType = new Type({
-    kind: kind.LIST,
-    elem: types.BYTE
-  });
-  var MyEnumType = new Type({
-    kind: kind.ENUM,
-    labels: ['M', 'A', 'G']
-  });
-  var IntSetType = new Type({
-    kind: kind.SET,
-    key: types.INT16
-  });
-  var FloatBoolMapType = new Type({
-    kind: kind.MAP,
-    key: types.FLOAT32,
-    elem: types.BOOL
-  });
-  var StringSetType = new Type({
-    kind: kind.SET,
-    key: types.STRING
-  });
-  var StringyStructType = new Type({
-    kind: kind.STRUCT,
-    fields: [
-      {
-        name: 'Ma',
-        type: types.STRING
-      },
-      {
-        name: 'Bu',
-        type: ByteListType
-      },
-      {
-        name: 'Fu',
-        type: MyEnumType
-      }
-    ]
-  });
-  var StringStringMapType = new Type({
-    kind: kind.MAP,
-    key: types.STRING,
-    elem: types.STRING
-  });
-  var StringAnyMapType = new Type({
-    kind: kind.MAP,
-    key: types.STRING,
-    elem: types.ANY
-  });
-  var Byte10ArrayType = new Type({
-    kind: kind.ARRAY,
-    elem: types.BYTE,
-    len: 10
-  });
-  var StructABCType = new Type({
-    kind: kind.STRUCT,
-    fields: [
-      {
-        name: 'A',
-        type: types.BOOL
-      },
-      {
-        name: 'B',
-        type: types.STRING
-      },
-      {
-        name: 'C',
-        type: types.UINT32
-      }
-    ]
-  });
-  var StructCDBType = new Type({
-    kind: kind.STRUCT,
-    fields: [
-      {
-        name: 'C',
-        type: types.UINT32
-      },
-      {
-        name: 'D',
-        type: OptStringType
-      },
-      {
-        name: 'B',
-        type: types.STRING
-      }
-    ]
-  });
-
-  var Any = registry.lookupOrCreateConstructor(types.ANY);
-  var AnyList = registry.lookupOrCreateConstructor(AnyListType);
-  var Bool = registry.lookupOrCreateConstructor(types.BOOL);
-  var Str = registry.lookupOrCreateConstructor(types.STRING);
-  var StrList = registry.lookupOrCreateConstructor(StringListType);
-  var OptStr = registry.lookupOrCreateConstructor(OptStringType);
-  var IntSet = registry.lookupOrCreateConstructor(IntSetType);
-  var FloatBoolMap = registry.lookupOrCreateConstructor(FloatBoolMapType);
-  var ByteList = registry.lookupOrCreateConstructor(ByteListType);
-  var Byte10Array = registry.lookupOrCreateConstructor(Byte10ArrayType);
-  var MyEnum = registry.lookupOrCreateConstructor(MyEnumType);
-  var StructABC = registry.lookupOrCreateConstructor(StructABCType);
-  var StructCDB = registry.lookupOrCreateConstructor(StructCDBType);
-  var StringSet = registry.lookupOrCreateConstructor(StringSetType);
-  var StringStringMap = registry.lookupOrCreateConstructor(StringStringMapType);
-  var StringAnyMap = registry.lookupOrCreateConstructor(StringAnyMapType);
-  var StringyStruct = registry.lookupOrCreateConstructor(StringyStructType);
-
-  var tests = [
+//
+var StringListType = new Type({
+  kind: kind.LIST,
+  elem: types.STRING
+});
+var ByteListType = new Type({
+  kind: kind.LIST,
+  elem: types.BYTE
+});
+var MyEnumType = new Type({
+  kind: kind.ENUM,
+  labels: ['M', 'A', 'G']
+});
+var IntSetType = new Type({
+  kind: kind.SET,
+  key: types.INT16
+});
+var FloatBoolMapType = new Type({
+  kind: kind.MAP,
+  key: types.FLOAT32,
+  elem: types.BOOL
+});
+var StringSetType = new Type({
+  kind: kind.SET,
+  key: types.STRING
+});
+var StringyStructType = new Type({
+  kind: kind.STRUCT,
+  fields: [
     {
-      name: 'Any(String) to String',
-      inValue: new Any(new Str('fff')),
-      outValue: new Str('fff'),
-      targetType: types.STRING
+      name: 'Ma',
+      type: types.STRING
     },
     {
-      name: 'String to Any(String)',
-      inValue: new Str('fff'),
-      outValue: new Any(new Str('fff')),
-      targetType: types.ANY
+      name: 'Bu',
+      type: ByteListType
     },
     {
-      name: '[]Any to []String',
-      // Note: 'jsval' is a JSValue that happens to convert to a string.
-      // This cannot always be expected to work, however.
-      inValue: new AnyList([new Str('fff'), 'jsval']),
-      outValue: new StrList(['fff', 'jsval']),
-      targetType: StringListType
-    },
-    {
-      name: '[]string to []any',
-      inValue: new StrList(['fff', 'not jsval']),
-      outValue: new AnyList([new Str('fff'), new Str('not jsval')]),
-      targetType: AnyListType
-    },
-    {
-      name: 'OptString to String',
-      inValue: new OptStr('abc'),
-      outValue: new Str('abc'),
-      targetType: types.STRING
-    },
-    {
-      name: 'String to ByteArray',
-      inValue: '1234567',
-      outValue: new Byte10Array(
-        new Uint8Array([49, 50, 51, 52, 53, 54, 55, 0, 0, 0])
-      ),
-      targetType: Byte10ArrayType
-    },
-    {
-      name: 'Set to Map',
-      inValue: new IntSet(new Set([4, -5, 8])),
-      outValue: new FloatBoolMap(new Map([[4, true], [-5, true], [8, true]])),
-      targetType: FloatBoolMapType
-    },
-    {
-      name: 'Map to Set',
-      inValue: new FloatBoolMap(new Map([[4, false], [-5, true], [8, true]])),
-      outValue: new IntSet(new Set([-5, 8])),
-      targetType: IntSetType
-    },
-    {
-      name: 'StructABC to StructCDB',
-      inValue: new StructABC({
-        a: true,
-        b: 'boom',
-        c: 5
-      }),
-      outValue: new StructCDB({
-        b: 'boom',
-        c: 5,
-        d: undefined
-      }),
-      targetType: StructCDBType
-    },
-    {
-      name: 'StructCDB to StructABC',
-      inValue: new StructCDB({
-        d: null,
-        b: 'doom',
-        c: 6
-      }),
-      outValue: new StructABC({
-        a: undefined,
-        b: 'doom',
-        c: 6
-      }),
-      targetType: StructABCType
-    },
-    {
-      name: 'set[string] to map[string]any',
-      inValue: new StringSet(
-        new Set(['me', 'di', 'a'])
-      ),
-      outValue: new StringAnyMap(
-        new Map([
-          ['me', new Bool(true)],
-          ['di', new Bool(true)],
-          ['a', new Bool(true)]
-        ])
-      ),
-      targetType: StringAnyMapType
-    },
-    {
-      name: 'struct with string-y fields to map[string]any',
-      inValue: new StringyStruct({
-        ma: 'Pool', // string
-        bu: 'imp',  // bytelist 105 109 112
-        fu: 'A'     // autoconverts to enum
-      }),
-      outValue: new StringAnyMap(new Map([
-        ['Ma', new Str('Pool')],
-        ['Bu', new ByteList(new Uint8Array([105, 109, 112]))],
-        ['Fu', new MyEnum('A')]
-      ])),
-      targetType: StringAnyMapType
-    },
-    {
-      name: 'struct with string-y fields to map[string]string',
-      inValue: new StringyStruct({
-        ma: 'Pool',
-        bu: 'imp',
-        fu: 'A'
-      }),
-      outValue: new StringStringMap(new Map([
-        ['Ma', 'Pool'],
-        ['Bu', 'imp'],
-        ['Fu', 'A']
-      ])),
-      targetType: StringStringMapType
-    },
-    {
-      name: 'map[string]any to struct with stringy-fields',
-      inValue: new StringAnyMap(new Map([
-        ['Ma', 'V'], // JSValue happens to be string-compatible.
-        ['Bu', new ByteList(new Uint8Array([79]))],
-        ['Fu', new MyEnum('M')]
-      ])),
-      outValue: new StringyStruct({
-        ma: 'V',
-        bu: 'O', // bytelist 79
-        fu: 'M'  // enum with M
-      }),
-      targetType: StringyStructType
-    },
-    {
-      name: 'map[string]any to set[string]',
-      inValue: new StringAnyMap(new Map([
-        ['Z', true], // JSValue that happens to be bool-compatible
-        ['o', new Bool(true)],
-        ['nga', false], // Will not appear since it's false
-        ['dine', new Bool(true)]
-      ])),
-      outValue: new StringSet(new Set([
-        'Z', 'o', 'dine'
-      ])),
-      targetType: StringSetType
+      name: 'Fu',
+      type: MyEnumType
     }
-  ];
-  for (var i = 0; i < tests.length; i++) {
-    var test = tests[i];
+  ]
+});
+var StringStringMapType = new Type({
+  kind: kind.MAP,
+  key: types.STRING,
+  elem: types.STRING
+});
+var StringAnyMapType = new Type({
+  kind: kind.MAP,
+  key: types.STRING,
+  elem: types.ANY
+});
+var Byte10ArrayType = new Type({
+  kind: kind.ARRAY,
+  elem: types.BYTE,
+  len: 10
+});
+var StructABCType = new Type({
+  kind: kind.STRUCT,
+  fields: [
+    {
+      name: 'A',
+      type: types.BOOL
+    },
+    {
+      name: 'B',
+      type: types.STRING
+    },
+    {
+      name: 'C',
+      type: types.UINT32
+    }
+  ]
+});
+var StructCDBType = new Type({
+  kind: kind.STRUCT,
+  fields: [
+    {
+      name: 'C',
+      type: types.UINT32
+    },
+    {
+      name: 'D',
+      type: OptStringType
+    },
+    {
+      name: 'B',
+      type: types.STRING
+    }
+  ]
+});
 
-    var reduced = canonicalize.reduce(test.inValue, test.targetType);
-    var outValue = test.outValue;
+var Any = registry.lookupOrCreateConstructor(types.ANY);
+var AnyList = registry.lookupOrCreateConstructor(AnyListType);
+var StrList = registry.lookupOrCreateConstructor(StringListType);
+var IntSet = registry.lookupOrCreateConstructor(IntSetType);
+var FloatBoolMap = registry.lookupOrCreateConstructor(FloatBoolMapType);
+var ByteList = registry.lookupOrCreateConstructor(ByteListType);
+var Byte10Array = registry.lookupOrCreateConstructor(Byte10ArrayType);
+var MyEnum = registry.lookupOrCreateConstructor(MyEnumType);
+var StructABC = registry.lookupOrCreateConstructor(StructABCType);
+var StructCDB = registry.lookupOrCreateConstructor(StructCDBType);
+var StringSet = registry.lookupOrCreateConstructor(StringSetType);
+var StringStringMap = registry.lookupOrCreateConstructor(StringStringMapType);
+var StringAnyMap = registry.lookupOrCreateConstructor(StringAnyMapType);
+var StringyStruct = registry.lookupOrCreateConstructor(StringyStructType);
+
+[
+  {
+    name: 'Any(String) to String',
+    inValue: new Any(new Str('fff')),
+    outValue: new Str('fff'),
+    targetType: types.STRING
+  },
+  {
+    name: 'String to Any(String)',
+    inValue: new Str('fff'),
+    outValue: new Any(new Str('fff')),
+    targetType: types.ANY
+  },
+  {
+    name: '[]Any to []String',
+    // Note: 'jsval' is a JSValue that happens to convert to a string.
+    // This cannot always be expected to work, however.
+    inValue: new AnyList([new Str('fff'), 'jsval']),
+    outValue: new StrList(['fff', 'jsval']),
+    targetType: StringListType
+  },
+  {
+    name: '[]string to []any',
+    inValue: new StrList(['fff', 'not jsval']),
+    outValue: new AnyList([new Str('fff'), new Str('not jsval')]),
+    targetType: AnyListType
+  },
+  {
+    name: 'OptString to String',
+    inValue: new OptStr('abc'),
+    outValue: new Str('abc'),
+    targetType: types.STRING
+  },
+  {
+    name: 'String to ByteArray',
+    inValue: '1234567',
+    outValue: new Byte10Array(
+      new Uint8Array([49, 50, 51, 52, 53, 54, 55, 0, 0, 0])
+    ),
+    targetType: Byte10ArrayType
+  },
+  {
+    name: 'Set to Map',
+    inValue: new IntSet(new Set([4, -5, 8])),
+    outValue: new FloatBoolMap(new Map([[4, true], [-5, true], [8, true]])),
+    targetType: FloatBoolMapType
+  },
+  {
+    name: 'Map to Set',
+    inValue: new FloatBoolMap(new Map([[4, false], [-5, true], [8, true]])),
+    outValue: new IntSet(new Set([-5, 8])),
+    targetType: IntSetType
+  },
+  {
+    name: 'StructABC to StructCDB',
+    inValue: new StructABC({
+      a: true,
+      b: 'boom',
+      c: 5
+    }),
+    outValue: new StructCDB({
+      b: 'boom',
+      c: 5,
+      d: undefined
+    }),
+    targetType: StructCDBType
+  },
+  {
+    name: 'StructCDB to StructABC',
+    inValue: new StructCDB({
+      d: null,
+      b: 'doom',
+      c: 6
+    }),
+    outValue: new StructABC({
+      a: undefined,
+      b: 'doom',
+      c: 6
+    }),
+    targetType: StructABCType
+  },
+  {
+    name: 'set[string] to map[string]any',
+    inValue: new StringSet(
+      new Set(['me', 'di', 'a'])
+    ),
+    outValue: new StringAnyMap(
+      new Map([
+        ['me', new Bool(true)],
+        ['di', new Bool(true)],
+        ['a', new Bool(true)]
+      ])
+    ),
+    targetType: StringAnyMapType
+  },
+  {
+    name: 'struct with string-y fields to map[string]any',
+    inValue: new StringyStruct({
+      ma: 'Pool', // string
+      bu: 'imp',  // bytelist 105 109 112
+      fu: 'A'     // autoconverts to enum
+    }),
+    outValue: new StringAnyMap(new Map([
+      ['Ma', new Str('Pool')],
+      ['Bu', new ByteList(new Uint8Array([105, 109, 112]))],
+      ['Fu', new MyEnum('A')]
+    ])),
+    targetType: StringAnyMapType
+  },
+  {
+    name: 'struct with string-y fields to map[string]string',
+    inValue: new StringyStruct({
+      ma: 'Pool',
+      bu: 'imp',
+      fu: 'A'
+    }),
+    outValue: new StringStringMap(new Map([
+      ['Ma', 'Pool'],
+      ['Bu', 'imp'],
+      ['Fu', 'A']
+    ])),
+    targetType: StringStringMapType
+  },
+  {
+    name: 'map[string]any to struct with stringy-fields',
+    inValue: new StringAnyMap(new Map([
+      ['Ma', 'V'], // JSValue happens to be string-compatible.
+      ['Bu', new ByteList(new Uint8Array([79]))],
+      ['Fu', new MyEnum('M')]
+    ])),
+    outValue: new StringyStruct({
+      ma: 'V',
+      bu: 'O', // bytelist 79
+      fu: 'M'  // enum with M
+    }),
+    targetType: StringyStructType
+  },
+  {
+    name: 'map[string]any to set[string]',
+    inValue: new StringAnyMap(new Map([
+      ['Z', true], // JSValue that happens to be bool-compatible
+      ['o', new Bool(true)],
+      ['nga', false], // Will not appear since it's false
+      ['dine', new Bool(true)]
+    ])),
+    outValue: new StringSet(new Set([
+      'Z', 'o', 'dine'
+    ])),
+    targetType: StringSetType
+  }
+].forEach(function(testCase) {
+  var testName = 'canonicalize conversion - success - ' + testCase.name;
+  test(testName, function(t) {
+    var reduced = canonicalize.reduce(testCase.inValue, testCase.targetType);
+    var outValue = testCase.outValue;
     t.deepEqual(
       stringify(reduced),
       stringify(outValue),
       test.name + ' converts correctly'
     );
-  }
-  t.end();
+    t.end();
+  });
 });
 
 test('canonicalize error', function(t) {
@@ -1526,7 +1495,6 @@
   });
 
   var VerrorConstructor = registry.lookupOrCreateConstructor(types.ERROR.elem);
-  var Str = registry.lookupOrCreateConstructor(types.STRING);
   var Int32 = registry.lookupOrCreateConstructor(types.INT32);
 
   var wrappedMessage = new VerrorConstructor({
@@ -1648,260 +1616,244 @@
 });
 
 // Tests the combination of native and vdl values.
-test('canonicalize native and vdl', function(t) {
-  var TimeType = Time.prototype._type;
-  var TimeArray = new Type({
-    kind: kind.ARRAY,
-    elem: TimeType,
-    len: 3
-  });
-  var TimeErrStruct = new Type({
-    kind: kind.STRUCT,
-    fields: [
-      {
-        name: 'Time',
-        type: TimeType
-      },
-      {
-        name: 'Err',
-        type: types.ERROR
-      }
-    ]
-  });
-
-  // The canonical error (input) and its wrapped paramList form.
-  var CanonError = makeError(
-    'cerrID',
-    actions.RETRY_BACKOFF,
-    'Canonical Error',
-    [ types.STRING, types.INT32 ]
-  );
-  var cError = new CanonError(null, 'blue', -1); // no ctx, string, int32
-  var cErrorN = cError.clone(); // The reduced cError params should be wrapped.
-  cErrorN.paramList = cError.paramList.map(function(p) {
-    return { val: p };
-  });
-
-  // Additional constants
-  var MILLI_TO_NANO = 1000*1000;
-  var zeroDateOffset = Date.parse('0001-01-01');
-
-  var tests = [
+var TimeType = Time.prototype._type;
+var TimeArray = new Type({
+  kind: kind.ARRAY,
+  elem: TimeType,
+  len: 3
+});
+var TimeErrStruct = new Type({
+  kind: kind.STRUCT,
+  fields: [
     {
-      name: 'TimeArray',
-      type: TimeArray,
-      inputObject: [
+      name: 'Time',
+      type: TimeType
+    },
+    {
+      name: 'Err',
+      type: types.ERROR
+    }
+  ]
+});
+
+// The canonical error (input) and its wrapped paramList form.
+var CanonError = makeError(
+  'cerrID',
+  actions.RETRY_BACKOFF,
+  'Canonical Error',
+  [ types.STRING, types.INT32 ]
+);
+var cError = new CanonError(null, 'blue', -1); // no ctx, string, int32
+var cErrorN = cError.clone(); // The reduced cError params should be wrapped.
+cErrorN.paramList = cError.paramList.map(function(p) {
+  return { val: p };
+});
+
+// Additional constants
+var MILLI_TO_NANO = 1000*1000;
+var zeroDateOffset = Date.parse('0001-01-01');
+
+[
+  {
+    name: 'TimeArray',
+    type: TimeArray,
+    inputObject: [
+      new Date(zeroDateOffset),
+      new Date(zeroDateOffset+100),
+      new Date(zeroDateOffset-2001)
+    ],
+    outputObject: {
+      val: [
         new Date(zeroDateOffset),
         new Date(zeroDateOffset+100),
         new Date(zeroDateOffset-2001)
-      ],
-      outputObject: {
-        val: [
-          new Date(zeroDateOffset),
-          new Date(zeroDateOffset+100),
-          new Date(zeroDateOffset-2001)
-        ]
-      },
-      outputObjectDeep: {
-        val: [
-          {
-            seconds: { val: BigInt.fromNativeNumber(0) },
-            nanos: { val: 0 }
-          },
-          {
-            seconds: { val: BigInt.fromNativeNumber(0) },
-            nanos: { val: 100*MILLI_TO_NANO }
-          },
-          {
-            seconds: { val: BigInt.fromNativeNumber(-3) },
-            nanos: { val: 999*MILLI_TO_NANO }
-          }
-        ]
-      }
+      ]
     },
-    {
-      name: 'TimeArray (empty)',
-      type: TimeArray,
-      inputObject: undefined,
-      outputObject: {
-        val: [new Date(zeroDateOffset), new Date(zeroDateOffset),
-        new Date(zeroDateOffset)]
-      },
-      outputObjectDeep: {
-        val: [
-          {
-            seconds: { val: BigInt.fromNativeNumber(0) },
-            nanos: { val: 0 }
-          },
-          {
-            seconds: { val: BigInt.fromNativeNumber(0) },
-            nanos: { val: 0 }
-          },
-          {
-            seconds: { val: BigInt.fromNativeNumber(0) },
-            nanos: { val: 0 }
-          },
-        ]
-      }
-    },
-    {
-      name: 'TimeErrStruct (empty)',
-      type: TimeErrStruct,
-      inputObject: undefined,
-      outputObject: {
-        time: new Date(zeroDateOffset),
-        err: null,
-      },
-      outputObjectDeep: {
-        time: {
+    outputObjectDeep: {
+      val: [
+        {
           seconds: { val: BigInt.fromNativeNumber(0) },
           nanos: { val: 0 }
         },
-        err: { val: null }
-      }
-    },
-    {
-      name: 'TimeErrStruct',
-      type: TimeErrStruct,
-      inputObject: {
-        time: new Date(zeroDateOffset+4024),
-        err: cError
-      },
-      outputObject: {
-        time: new Date(zeroDateOffset+4024),
-        err: cErrorN
-      },
-      outputObjectDeep: {
-        time: {
-          seconds: { val: BigInt.fromNativeNumber(4) },
-          nanos: { val: 24 * MILLI_TO_NANO }
+        {
+          seconds: { val: BigInt.fromNativeNumber(0) },
+          nanos: { val: 100*MILLI_TO_NANO }
         },
-        err: { // optional error
-           val: { // error
-            _langId: 'en-US',
-            id: { val: 'cerrID' },
-            retryCode: { val: actions.RETRY_BACKOFF },
-            msg: { val: 'Canonical Error' },
-            paramList: {
-              val: [
-                { // any(string)
-                  val: { val: 'app' }
-                },
-                { // any(string)
-                  val: { val: 'op' }
-                },
-                { // any(string)
-                  val: { val: 'blue' }
-                },
-                { // any(int32)
-                  val: { val: -1 }
-                }
-              ]
-            }
+        {
+          seconds: { val: BigInt.fromNativeNumber(-3) },
+          nanos: { val: 999*MILLI_TO_NANO }
+        }
+      ]
+    }
+  },
+  {
+    name: 'TimeArray (empty)',
+    type: TimeArray,
+    inputObject: undefined,
+    outputObject: {
+      val: [new Date(zeroDateOffset), new Date(zeroDateOffset),
+      new Date(zeroDateOffset)]
+    },
+    outputObjectDeep: {
+      val: [
+        {
+          seconds: { val: BigInt.fromNativeNumber(0) },
+          nanos: { val: 0 }
+        },
+        {
+          seconds: { val: BigInt.fromNativeNumber(0) },
+          nanos: { val: 0 }
+        },
+        {
+          seconds: { val: BigInt.fromNativeNumber(0) },
+          nanos: { val: 0 }
+        },
+      ]
+    }
+  },
+  {
+    name: 'TimeErrStruct (empty)',
+    type: TimeErrStruct,
+    inputObject: undefined,
+    outputObject: {
+      time: new Date(zeroDateOffset),
+      err: null,
+    },
+    outputObjectDeep: {
+      time: {
+        seconds: { val: BigInt.fromNativeNumber(0) },
+        nanos: { val: 0 }
+      },
+      err: { val: null }
+    }
+  },
+  {
+    name: 'TimeErrStruct',
+    type: TimeErrStruct,
+    inputObject: {
+      time: new Date(zeroDateOffset+4024),
+      err: cError
+    },
+    outputObject: {
+      time: new Date(zeroDateOffset+4024),
+      err: cErrorN
+    },
+    outputObjectDeep: {
+      time: {
+        seconds: { val: BigInt.fromNativeNumber(4) },
+        nanos: { val: 24 * MILLI_TO_NANO }
+      },
+      err: { // optional error
+         val: { // error
+          _langId: 'en-US',
+          id: { val: 'cerrID' },
+          retryCode: { val: actions.RETRY_BACKOFF },
+          msg: { val: 'Canonical Error' },
+          paramList: {
+            val: [
+              { // any(string)
+                val: { val: 'app' }
+              },
+              { // any(string)
+                val: { val: 'op' }
+              },
+              { // any(string)
+                val: { val: 'blue' }
+              },
+              { // any(int32)
+                val: { val: -1 }
+              }
+            ]
           }
         }
       }
     }
-  ];
-
-  for (var i = 0; i < tests.length; i++) {
-    runNativeWireTest(tests[i], tests[i].type, t);
   }
-  t.end();
+].forEach(function(testCase) {
+  test('canonicalize native and vdl -' + testCase.name, function(t) {
+    runNativeWireTest(testCase, testCase.type, t);
+    t.end();
+  });
 });
 
+var IntListType = new Type({
+  kind: kind.LIST,
+  elem: types.INT32
+});
+var Int3ArrType = new Type({
+  kind: kind.ARRAY,
+  elem: types.INT32,
+  len: 3
+});
+
+var IntList = registry.lookupOrCreateConstructor(IntListType);
 // This test checks the failure cases of value to type conversion.
 // For example, some maps fail to convert to sets, and null optional values
 // cannot convert to their base type.
 // This test supplements the cross-language conversion tests cases in
 // test-vom-compatible.js
-test('canonicalize conversion - failure', function(t) {
-  var OptStringType = new Type({
-    kind: kind.OPTIONAL,
-    elem: types.STRING
-  });
-  var IntListType = new Type({
-    kind: kind.LIST,
-    elem: types.INT32
-  });
-  var Int3ArrType = new Type({
-    kind: kind.ARRAY,
-    elem: types.INT32,
-    len: 3
-  });
-  var IntSetType = new Type({
-    kind: kind.SET,
-    key: types.INT16
-  });
-
-  var Str = registry.lookupOrCreateConstructor(types.STRING);
-  var OptStr = registry.lookupOrCreateConstructor(OptStringType);
-  var IntList = registry.lookupOrCreateConstructor(IntListType);
-
-  var tests = [
-    {
-      name: 'number larger than MAX_FLOAT32',
-      inValue: 1e40,
-      targetType: types.FLOAT32,
-      expectedErr: 'is too large'
-    },
-    {
-      name: 'imag smaller than MAX_FLOAT32 in Complex64',
-      inValue: { real: 0, imag: -1e40 },
-      targetType: types.COMPLEX64,
-      expectedErr: 'is too small'
-    },
-    {
-      name: 'negative, real Complex to uint',
-      inValue: new Complex(-4, 0),
-      targetType: types.UINT16,
-      expectedErr: 'value cannot be negative'
-    },
-    {
-      name: 'null OptString to String',
-      inValue: new OptStr(null),
-      targetType: types.STRING,
-      expectedErr: 'value is null for non-optional type'
-    },
-    {
-      name: 'String to Bool',
-      inValue: new Str('not a boolean'),
-      targetType: types.BOOL,
-      expectedErr: 'not compatible'
-    },
-    {
-      name: 'String to Bool - native',
-      inValue: 'not a boolean',
-      targetType: types.BOOL,
-      expectedErr: 'value is not a boolean'
-    },
-    {
-      name: 'large list to smaller array',
-      inValue: new IntList([3, 4, 8, 1]),
-      targetType: Int3ArrType,
-      expectedErr: 'exceeds type length 3'
-    },
-    {
-      name: 'large list to smaller array - native',
-      inValue: [3, 4, 8, 1],
-      targetType: Int3ArrType,
-      expectedErr: 'exceeds type length 3'
-    },
-    {
-      name: 'map to set',
-      inValue: new Map([[4, 'not a bool'], [5, true]]),
-      targetType: IntSetType,
-      expectedErr: 'this Map value cannot convert to Set'
-    }
-  ];
-  for (var i = 0; i < tests.length; i++) {
-    var test = tests[i];
-
-    t.throws(
-      canonicalize.reduce.bind(null, test.inValue, test.targetType),
-      new RegExp('.*' + (test.expectedErr || '') + '.*'),
-      test.name + ' fails to convert'
-    );
+[
+  {
+    name: 'number larger than MAX_FLOAT32',
+    inValue: 1e40,
+    targetType: types.FLOAT32,
+    expectedErr: 'is too large'
+  },
+  {
+    name: 'imag smaller than MAX_FLOAT32 in Complex64',
+    inValue: { real: 0, imag: -1e40 },
+    targetType: types.COMPLEX64,
+    expectedErr: 'is too small'
+  },
+  {
+    name: 'negative, real Complex to uint',
+    inValue: new Complex(-4, 0),
+    targetType: types.UINT16,
+    expectedErr: 'value cannot be negative'
+  },
+  {
+    name: 'null OptString to String',
+    inValue: new OptStr(null),
+    targetType: types.STRING,
+    expectedErr: 'value is null for non-optional type'
+  },
+  {
+    name: 'String to Bool',
+    inValue: new Str('not a boolean'),
+    targetType: types.BOOL,
+    expectedErr: 'not compatible'
+  },
+  {
+    name: 'String to Bool - native',
+    inValue: 'not a boolean',
+    targetType: types.BOOL,
+    expectedErr: 'value is not a boolean'
+  },
+  {
+    name: 'large list to smaller array',
+    inValue: new IntList([3, 4, 8, 1]),
+    targetType: Int3ArrType,
+    expectedErr: 'exceeds type length 3'
+  },
+  {
+    name: 'large list to smaller array - native',
+    inValue: [3, 4, 8, 1],
+    targetType: Int3ArrType,
+    expectedErr: 'exceeds type length 3'
+  },
+  {
+    name: 'map to set',
+    inValue: new Map([[4, 'not a bool'], [5, true]]),
+    targetType: IntSetType,
+    expectedErr: 'this Map value cannot convert to Set'
   }
-  t.end();
+].forEach(function(testCase) {
+  test('canonicalize conversion - failure -' + testCase.name, function(t) {
+    t.throws(
+      canonicalize.reduce.bind(null, testCase.inValue, testCase.targetType),
+      new RegExp('.*' + (testCase.expectedErr || '') + '.*'),
+      'fails to convert'
+    );
+    t.end();
+  });
 });