js/core: Move test-encode-decode.js to have one testcase per test.

This makes STOPONFAIL work better, since it that waits until the
test is complete before stopping.  For many of the vom and vdl tests
this might mean the actual failure is well off the screen by the time
it stops.
https://github.com/vanadium/issues/issues/490

Change-Id: Ifa19db58e536e2eea7863dc9770335d282b60e20
diff --git a/test/vom/test-encode-decode.js b/test/vom/test-encode-decode.js
index f155aff..1f1b0bb 100644
--- a/test/vom/test-encode-decode.js
+++ b/test/vom/test-encode-decode.js
@@ -25,1255 +25,1250 @@
 var Encoder = require('./../../src/vom/encoder.js');
 var Decoder = require('./../../src/vom/decoder.js');
 
-test('encode and decode', function(t) {
-  var linkedListNodetype = {
-    kind: kind.STRUCT,
-    name: 'LinkedListNode',
-    fields: [
-      {
-        name: 'Value',
-        type: types.ANY
-      },
-      {
-        name: 'Next'
-      }
-    ]
-  };
-  linkedListNodetype.fields[1].type = {
-    kind: kind.OPTIONAL,
-    elem: linkedListNodetype
-  };
-
-  var treeNodetype = new Type({
-    kind: kind.STRUCT,
-    name: 'TreeNodetype',
-    fields: [
-      {
-        name: 'Value',
-        type: types.ANY
-      },
-      {
-        name: 'Left'
-      },
-      {
-        name: 'Right'
-      }
-    ]
-  });
-  var nextTreeNodetype = new Type({
-    kind: kind.OPTIONAL,
-    elem: treeNodetype
-  });
-  treeNodetype.fields[1].type = nextTreeNodetype;
-  treeNodetype.fields[2].type = nextTreeNodetype;
-
-  // Define a type with _type on the prototype to test type look up in one of.
-  function NamedUintConstructor(val) {
-    this.val = val;
+var linkedListNodeType = {
+  kind: kind.STRUCT,
+  name: 'LinkedListNode',
+  fields: [
+    {
+    name: 'Value',
+    type: types.ANY
+  },
+  {
+    name: 'Next'
   }
-  NamedUintConstructor.prototype._type = {
-    kind: kind.UINT32,
-    name: 'namedUint32'
-  };
-  NamedUintConstructor.prototype._wrappedType = true;
+  ]
+};
+linkedListNodeType.fields[1].type = {
+  kind: kind.OPTIONAL,
+  elem: linkedListNodeType
+};
 
-  var tests = [
+var treeNodeType = new Type({
+  kind: kind.STRUCT,
+  name: 'TreeNodeType',
+  fields: [
     {
-      n: 'Decode(Encode(Byte))',
-      v:  1,
-      expectedOutput: {
-        val:  1
-      },
-      t: types.BYTE
+    name: 'Value',
+    type: types.ANY
+  },
+  {
+    name: 'Left'
+  },
+  {
+    name: 'Right'
+  }
+  ]
+});
+var nextTreeNodeType = new Type({
+  kind: kind.OPTIONAL,
+  elem: treeNodeType
+});
+treeNodeType.fields[1].type = nextTreeNodeType;
+treeNodeType.fields[2].type = nextTreeNodeType;
+
+// Define a type with _type on the prototype to test type look up in one of.
+function NamedUintConstructor(val) {
+  this.val = val;
+}
+NamedUintConstructor.prototype._type = {
+  kind: kind.UINT32,
+  name: 'namedUint32'
+};
+NamedUintConstructor.prototype._wrappedType = true;
+
+[
+  {
+    n: 'Decode(Encode(Byte))',
+    v:  1,
+    expectedOutput: {
+      val:  1
     },
-    {
-      n: 'Decode(Encode(Uint16))',
-      v:  1000,
-      expectedOutput: {
-        val:  1000
-      },
-      t: types.UINT16
+    t: types.BYTE
+  },
+  {
+    n: 'Decode(Encode(Uint16))',
+    v:  1000,
+    expectedOutput: {
+      val:  1000
     },
-    {
-      n: 'Decode(Encode(Float32))',
-      v:  0.3,
-      expectedOutput: {
-        val:  0.3
-      },
-      t: types.FLOAT32
+    t: types.UINT16
+  },
+  {
+    n: 'Decode(Encode(Float32))',
+    v:  0.3,
+    expectedOutput: {
+      val:  0.3
     },
-    {
-      n: 'Decode(Encode(Int32))',
-      v:  -1,
-      expectedOutput: {
-        val:  -1
-      },
-      t: types.INT32
+    t: types.FLOAT32
+  },
+  {
+    n: 'Decode(Encode(Int32))',
+    v:  -1,
+    expectedOutput: {
+      val:  -1
     },
-    {
-      n: 'Decode(Encode(Complex64))',
-      v: {
+    t: types.INT32
+  },
+  {
+    n: 'Decode(Encode(Complex64))',
+    v: {
+      real: 1.9,
+      imag: -0.4
+    },
+    expectedOutput: {
+      val: {
         real: 1.9,
         imag: -0.4
-      },
-      expectedOutput: {
-        val: {
-          real: 1.9,
-          imag: -0.4
-        }
-      },
-      t: types.COMPLEX64
-    },
-    {
-      n: 'Decode(Encode(String))',
-      v:  'a string',
-      expectedOutput: {
-        val:  'a string'
-      },
-      t: types.STRING
-    },
-    {
-      n: 'Decode(Encode(Bool))',
-      v:  true,
-      expectedOutput: {
-        val:  true
-      },
-      t: types.BOOL
-    },
-    {
-      n: 'Decode(Encode(typeObject))',
-      v: {
-        kind: kind.LIST,
-        name: 'A list',
-        elem: types.STRING
-      },
-      expectedOutput: {
-        kind: kind.LIST,
-        name: 'A list',
-        elem: types.STRING
-      },
-      t: types.TYPEOBJECT
-    },
-    {
-      n: 'Decode(Encode(Struct{X: typeObject(nil)}))',
-      v: {
-        x: undefined
-      },
-      expectedOutput: {
-        x: types.ANY
-      },
-      t: {
-        kind: kind.STRUCT,
-        fields: [
-          {
-            name: 'X',
-            type: types.TYPEOBJECT
-          }
-        ]
       }
     },
-    {
-      n: 'Decode(Encode(Struct w/ Private field))',
-      v: {
-        x: 'val',
-        _privateField: 99
-      },
-      expectedOutput: {
-        x: 'val'
-      },
-      t: {
-        kind: kind.STRUCT,
-        fields: [
-          {
-            name: 'X',
-            type: types.STRING
-          }
-        ]
+    t: types.COMPLEX64
+  },
+  {
+    n: 'Decode(Encode(String))',
+    v:  'a string',
+    expectedOutput: {
+      val:  'a string'
+    },
+    t: types.STRING
+  },
+  {
+    n: 'Decode(Encode(Bool))',
+    v:  true,
+    expectedOutput: {
+      val:  true
+    },
+    t: types.BOOL
+  },
+  {
+    n: 'Decode(Encode(typeObject))',
+    v: {
+      kind: kind.LIST,
+      name: 'A list',
+      elem: types.STRING
+    },
+    expectedOutput: {
+      kind: kind.LIST,
+      name: 'A list',
+      elem: types.STRING
+    },
+    t: types.TYPEOBJECT
+  },
+  {
+    n: 'Decode(Encode(Struct{X: typeObject(nil)}))',
+    v: {
+      x: undefined
+    },
+    expectedOutput: {
+      x: types.ANY
+    },
+    t: {
+      kind: kind.STRUCT,
+      fields: [
+        {
+        name: 'X',
+        type: types.TYPEOBJECT
       }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Struct w/ Private field))',
+    v: {
+      x: 'val',
+      _privateField: 99
     },
-    {
-      n: 'Decode(Encode(Object w/ Private field [no type]))',
-      v: {
-        x: 'val',
-        _privateField: 99
-      },
-      expectedOutput: {
-        x: 'val'
+    expectedOutput: {
+      x: 'val'
+    },
+    t: {
+      kind: kind.STRUCT,
+      fields: [
+        {
+        name: 'X',
+        type: types.STRING
       }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Object w/ Private field [no type]))',
+    v: {
+      x: 'val',
+      _privateField: 99
     },
-    {
-      n: 'Decode(Encode(List<Uint32>))',
-      v:  [2, 3, 4],
-      expectedOutput: {
-        val:  [2, 3, 4]
-      },
-      t: {
-        kind: kind.LIST,
-        elem: types.UINT32
-      }
+    expectedOutput: {
+      x: 'val'
+    }
+  },
+  {
+    n: 'Decode(Encode(List<Uint32>))',
+    v:  [2, 3, 4],
+    expectedOutput: {
+      val:  [2, 3, 4]
     },
-    {
-      n: 'Decode(Encode(Array<Int32>))',
-      v:  [2, 3, 4],
-      expectedOutput: {
-        val:  [2, 3, 4]
-      },
-      t: {
-        kind: kind.ARRAY,
-        elem: types.INT32,
-        len: 3
-      }
+    t: {
+      kind: kind.LIST,
+      elem: types.UINT32
+    }
+  },
+  {
+    n: 'Decode(Encode(Array<Int32>))',
+    v:  [2, 3, 4],
+    expectedOutput: {
+      val:  [2, 3, 4]
     },
-    {
-      n: 'Decode(Encode(List<Byte>))',
-      v:  new Uint8Array([0x80, 0x90]),
-      expectedOutput: {
-        val:  new Uint8Array([0x80, 0x90])
-      },
-      t: {
-        kind: kind.LIST,
-        elem: types.BYTE
-      }
+    t: {
+      kind: kind.ARRAY,
+      elem: types.INT32,
+      len: 3
+    }
+  },
+  {
+    n: 'Decode(Encode(List<Byte>))',
+    v:  new Uint8Array([0x80, 0x90]),
+    expectedOutput: {
+      val:  new Uint8Array([0x80, 0x90])
     },
-    {
-      n: 'Decode(Encode(Array<Byte>))',
-      v:  new Uint8Array([0x80, 0x90]),
-      expectedOutput: {
-        val:  new Uint8Array([0x80, 0x90])
-      },
-      t: {
-        kind: kind.ARRAY,
-        elem: types.BYTE,
-        len: 2
-      }
+    t: {
+      kind: kind.LIST,
+      elem: types.BYTE
+    }
+  },
+  {
+    n: 'Decode(Encode(Array<Byte>))',
+    v:  new Uint8Array([0x80, 0x90]),
+    expectedOutput: {
+      val:  new Uint8Array([0x80, 0x90])
     },
-    {
-      n: 'Decode(Encode(Set<String> as Object))',
-      v:  {
-        'b': true,
-        'a': true
-      },
-      expectedOutput: {
-        val:  new Set(['B', 'A'])
-      },
-      t: {
-        kind: kind.SET,
-        key: types.STRING
-      },
+    t: {
+      kind: kind.ARRAY,
+      elem: types.BYTE,
+      len: 2
+    }
+  },
+  {
+    n: 'Decode(Encode(Set<String> as Object))',
+    v:  {
+      'b': true,
+      'a': true
     },
-    {
-      n: 'Decode(Encode(Set<Uint32> as Set))',
-      v:  new Set([3, 5]),
-      expectedOutput: {
-        val:  new Set([3, 5])
-      },
-      t: {
-        kind: kind.SET,
-        key: types.UINT32
-      }
+    expectedOutput: {
+      val:  new Set(['B', 'A'])
     },
-    {
-      n: 'Decode(Encode(Map[String]String as Object))',
-      v: {
-        'key1': 'value1',
-        'key2': 'value2'
-      },
-      expectedOutput: {
-        val: new Map([
-          ['Key1', 'value1'],
-          ['Key2', 'value2']
-        ]),
-      },
-      t: {
-        kind: kind.MAP,
-        key: types.STRING,
-        elem: types.STRING
-      },
+    t: {
+      kind: kind.SET,
+      key: types.STRING
     },
-    {
-      n: 'Decode(Encode(Map[Uint16]Float32 as Map))',
-      v: new Map([
+  },
+  {
+    n: 'Decode(Encode(Set<Uint32> as Set))',
+    v:  new Set([3, 5]),
+    expectedOutput: {
+      val:  new Set([3, 5])
+    },
+    t: {
+      kind: kind.SET,
+      key: types.UINT32
+    }
+  },
+  {
+    n: 'Decode(Encode(Map[String]String as Object))',
+    v: {
+      'key1': 'value1',
+      'key2': 'value2'
+    },
+    expectedOutput: {
+      val: new Map([
+        ['Key1', 'value1'],
+        ['Key2', 'value2']
+      ]),
+    },
+    t: {
+      kind: kind.MAP,
+      key: types.STRING,
+      elem: types.STRING
+    },
+  },
+  {
+    n: 'Decode(Encode(Map[Uint16]Float32 as Map))',
+    v: new Map([
+      [3, 1.3],
+      [2, -5.6]
+    ]),
+    expectedOutput: {
+      val: new Map([
         [3, 1.3],
         [2, -5.6]
-      ]),
-      expectedOutput: {
-        val: new Map([
-          [3, 1.3],
-          [2, -5.6]
-        ])
-      },
-      t: {
-        kind: kind.MAP,
-        key: types.UINT16,
-        elem: types.FLOAT32
-      }
-    },
-    {
-      n: 'Decode(Encode(Struct as Object))',
-      v: {
-        field1: 8,
-        field2: 'str',
-        field3: [4, 5]
-      },
-      t: {
-        kind: kind.STRUCT,
-        name: 'testStruct',
-        fields: [
-          {
-            name: 'Field1',
-            type: types.UINT16
-          },
-          {
-            name: 'Field2',
-            type: types.STRING
-          },
-          {
-            name: 'Field3',
-            type: {
-              kind: kind.LIST,
-              elem: types.FLOAT64
-            }
-          }
-        ]
-      }
-    },
-    {
-      n: 'Decode(Encode(Enum))',
-      v: 'alabel',
-      expectedOutput: {
-        val: 'alabel'
-      },
-      t: {
-        kind: kind.ENUM,
-        name: 'enumtype',
-        labels: ['alabel', 'blabel']
-      }
-    },
-    {
-      n: 'Decode(Encode(Optional String w/ value))',
-      v: 'optionalString',
-      expectedOutput: {
-        val: 'optionalString'
-      },
-      t: {
-        kind: kind.OPTIONAL,
-        elem: types.STRING
-      }
-    },
-    {
-      n: 'Decode(Encode(Optional String w/ null))',
-      v: null,
-      expectedOutput: {
-        val: null
-      },
-      t: {
-        kind: kind.OPTIONAL,
-        elem: types.STRING
-      }
-    },
-    {
-      n: 'Decode(Encode(Array<Byte>) w/o type)',
-      v:  new Uint8Array([0x80, 0x90]),
-      expectedOutput: new Uint8Array([0x80, 0x90])
-    },
-    {
-      n: 'Decode(Encode(Bool w/o type))',
-      v: true,
-      expectedOutput: true
-    },
-    {
-      n: 'Decode(Encode(Map w/o type))',
-      v: new Map([
-        ['x', [4, 5, 3]],
-        ['y', 'test']
-      ]),
-      expectedOutput: new Map([
-        ['x', [4, 5, 3]],
-        ['y', 'test']
       ])
     },
-    {
-      n: 'Decode(Encode(Set w/o type))',
-      v: new Set([3, 4, 0]),
-      expectedOutput: new Set([3, 4, 0])
+    t: {
+      kind: kind.MAP,
+      key: types.UINT16,
+      elem: types.FLOAT32
+    }
+  },
+  {
+    n: 'Decode(Encode(Struct as Object))',
+    v: {
+      field1: 8,
+      field2: 'str',
+      field3: [4, 5]
     },
-    {
-      n: 'Decode(Encode(Map w/o type))',
-      v: new Map([
-        ['string', false],
-        [2, ['mixed', 3, 'list']]
-      ]),
-      expectedOutput: new Map([
-        ['string', false],
-        [2, ['mixed', 3, 'list']]
-      ])
-    },
-    {
-      n: 'Decode(Encode(Object w/o type))',
-      v: {
-        a: 'a',
-        b: 3,
-        c: true
-      },
-      expectedOutput: {
-        a: 'a',
-        b: 3,
-        c: true
-      }
-    },
-    {
-      n: 'Decode(Encode(List))',
-      v: [
+    t: {
+      kind: kind.STRUCT,
+      name: 'testStruct',
+      fields: [
         {
-          aa: 2,
-          bb: 3
-        },
-        'something else'
-      ],
-      expectedOutput: {
-        val: [
-          {
-            aa: 2,
-            bb: 3
-          },
-          'something else'
-        ]
+        name: 'Field1',
+        type: types.UINT16
       },
-      t: {
-        kind: kind.LIST,
-        elem: types.ANY
+      {
+        name: 'Field2',
+        type: types.STRING
+      },
+      {
+        name: 'Field3',
+        type: {
+          kind: kind.LIST,
+          elem: types.FLOAT64
+        }
       }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Enum))',
+    v: 'alabel',
+    expectedOutput: {
+      val: 'alabel'
     },
-    {
-      n: 'Decode(Encode(Union<String, Uint16> w/ Uint16))',
-      v: {
-        'uInt': 5
+    t: {
+      kind: kind.ENUM,
+      name: 'enumtype',
+      labels: ['alabel', 'blabel']
+    }
+  },
+  {
+    n: 'Decode(Encode(Optional String w/ value))',
+    v: 'optionalString',
+    expectedOutput: {
+      val: 'optionalString'
+    },
+    t: {
+      kind: kind.OPTIONAL,
+      elem: types.STRING
+    }
+  },
+  {
+    n: 'Decode(Encode(Optional String w/ null))',
+    v: null,
+    expectedOutput: {
+      val: null
+    },
+    t: {
+      kind: kind.OPTIONAL,
+      elem: types.STRING
+    }
+  },
+  {
+    n: 'Decode(Encode(Array<Byte>) w/o type)',
+    v:  new Uint8Array([0x80, 0x90]),
+    expectedOutput: new Uint8Array([0x80, 0x90])
+  },
+  {
+    n: 'Decode(Encode(Bool w/o type))',
+    v: true,
+    expectedOutput: true
+  },
+  {
+    n: 'Decode(Encode(Map w/o type))',
+    v: new Map([
+      ['x', [4, 5, 3]],
+      ['y', 'test']
+    ]),
+    expectedOutput: new Map([
+      ['x', [4, 5, 3]],
+      ['y', 'test']
+    ])
+  },
+  {
+    n: 'Decode(Encode(Set w/o type))',
+    v: new Set([3, 4, 0]),
+    expectedOutput: new Set([3, 4, 0])
+  },
+  {
+    n: 'Decode(Encode(Map w/o type))',
+    v: new Map([
+      ['string', false],
+      [2, ['mixed', 3, 'list']]
+    ]),
+    expectedOutput: new Map([
+      ['string', false],
+      [2, ['mixed', 3, 'list']]
+    ])
+  },
+  {
+    n: 'Decode(Encode(Object w/o type))',
+    v: {
+      a: 'a',
+      b: 3,
+      c: true
+    },
+    expectedOutput: {
+      a: 'a',
+      b: 3,
+      c: true
+    }
+  },
+  {
+    n: 'Decode(Encode(List))',
+    v: [
+      {
+      aa: 2,
+      bb: 3
+    },
+    'something else'
+    ],
+    expectedOutput: {
+      val: [
+        {
+        aa: 2,
+        bb: 3
       },
-      t: {
-        kind: kind.UNION,
-        name: 'unionName',
-        fields: [
-          {
-            name: 'StringInt',
-            type: types.STRING
-          },
-          {
-            name: 'UInt',
-            type: types.UINT16
-          }
-        ]
+      'something else'
+      ]
+    },
+    t: {
+      kind: kind.LIST,
+      elem: types.ANY
+    }
+  },
+  {
+    n: 'Decode(Encode(Union<String, Uint16> w/ Uint16))',
+    v: {
+      'uInt': 5
+    },
+    t: {
+      kind: kind.UNION,
+      name: 'unionName',
+      fields: [
+        {
+        name: 'StringInt',
+        type: types.STRING
+      },
+      {
+        name: 'UInt',
+        type: types.UINT16
       }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Union<String, Bool> w/ String))',
+    v: {
+      'stringBool': 'str'
     },
-    {
-      n: 'Decode(Encode(Union<String, Bool> w/ String))',
-      v: {
-        'stringBool': 'str'
+    t: {
+      kind: kind.UNION,
+      name: 'unionName',
+      fields: [
+        {
+        name: 'StringBool',
+        type: types.STRING
       },
-      t: {
-        kind: kind.UNION,
-        name: 'unionName',
-        fields: [
-          {
-            name: 'StringBool',
-            type: types.STRING
-          },
-          {
-            name: 'Boolean',
-            type: types.BOOL
-          }
-        ]
+      {
+        name: 'Boolean',
+        type: types.BOOL
       }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Union<String, Bool> w/ Bool))',
+    v: {
+      'boolean': true
     },
-    {
-      n: 'Decode(Encode(Union<String, Bool> w/ Bool))',
-      v: {
-        'boolean': true
+    t: {
+      kind: kind.UNION,
+      name: 'UnionName',
+      fields: [
+        {
+        name: 'StringBool',
+        type: types.STRING
       },
-      t: {
-        kind: kind.UNION,
-        name: 'UnionName',
-        fields: [
-          {
-            name: 'StringBool',
-            type: types.STRING
-          },
-          {
-            name: 'Boolean',
-            type: types.BOOL
-          }
-        ]
+      {
+        name: 'Boolean',
+        type: types.BOOL
       }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Union<Map[Uint16]Uint32, List<Float64>> w/ List))',
+    v: {
+      'list': [4,3,5]
     },
-    {
-      n: 'Decode(Encode(Union<Map[Uint16]Uint32, List<Float64>> w/ List))',
-      v: {
-        'list': [4,3,5]
-      },
-      t: {
-        kind: kind.UNION,
-        name: 'UnionName',
-        fields: [
-          {
-            name: 'Map',
-            type: {
-              kind: kind.MAP,
-              key: types.STRING,
-              elem: types.UINT32
-            }
-          },
-          {
-            name: 'List',
-            type: {
-              kind: kind.LIST,
-              elem: types.FLOAT64
-            }
-          }
-        ]
-      }
-    },
-    {
-      n: 'Decode(Encode(Union<Map[Uint16]Uint32, List<Float64>> w/ Map))',
-      v: {
-        'map': {                   // This is a native object; the fields
-          'a': 9,                  // are capitalized upon conversion to Map.
-          'b': 10,
-          _type: {
-            kind: kind.MAP,
-            key: types.STRING,
-            elem: types.UINT32
-          }
-        }
-      },
-      t: {
-        kind: kind.UNION,
-        name: 'UnionName',
-        fields: [
-          {
-            name: 'Map',
-            type: {
-              kind: kind.MAP,
-              key: types.STRING,
-              elem: types.UINT32
-            }
-          },
-          {
-            name: 'List',
-            type: {
-              kind: kind.LIST,
-              elem: types.FLOAT64
-            }
-          }
-        ]
-      },
-      expectedOutput: {
-        'map': new Map([
-          ['A', 9],
-          ['B', 10]
-        ])
-      }
-    },
-    {
-      n: 'Decode(Encode(Linked List Nodes))',
-      v: {
-        value: 9,
-        next: {
-          value: 10,
-          next: {
-            value: 11,
-            next: null
-          }
-        }
-      },
-      expectedOutput: {
-        value: 9,
-        next: {
-          value: 10,
-          next: {
-            value: 11,
-            next: null
-          }
-        }
-      },
-      t: linkedListNodetype
-    },
-    {
-      n: 'Decode(Encode(Tree Nodes))',
-      v: {
-        value: 4,
-        left: {
-          value: 5,
-          left: null,
-          right: null
-        },
-        right: {
-          value: false,
-          left: {
-            value: true,
-            left: null,
-            right: null
-          },
-          right: null
-        }
-      },
-      expectedOutput: {
-        value: 4,
-        left: {
-          value: 5,
-          left: null,
-          right: null
-        },
-        right: {
-          value: false,
-          left: {
-            value: true,
-            left: null,
-            right: null
-          },
-          right: null
-        }
-      },
-      t: treeNodetype
-    },
-    {
-      n: 'Decode(Encode(Any))',
-      v: 5,
-      expectedOutput: { // any with JSValue(number)
-        val: 5
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any)) - wrapLike',
-      v: {
-        val: 5
-      },
-      expectedOutput: { // any with JSValue(object with field val: 5)
-        val: {
-          val: 5
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any)) - INT32 wrap',
-      v: {
-        val: 5,
-        _type: types.INT32, // pretend this is on the prototype
-        _wrappedType: true  // pretend this is on the prototype
-      },
-      expectedOutput: {
-        val: {
-          val: 5
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any)) - Optional wrap',
-      v: {
-        val: 'not null',
-        _type: {              // pretend this is on the prototype
-          kind: kind.OPTIONAL,
-          elem: types.STRING
-        },
-        _wrappedType: true    // pretend this is on the prototype
-      },
-      expectedOutput: {
-        val: {
-          val: 'not null'
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any)) - Struct wrap',
-      v: {
-        a: 'abc',
-        _type: {              // pretend this is on the prototype
-          kind: kind.STRUCT,
-          fields: [
-            {
-              name: 'A',
-              type: types.STRING
-            }
-          ]
-        }
-      },
-      expectedOutput: {
-        val: {
-          a: 'abc'
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any)) - Struct w/ Any wrap',
-      v: {
-        a: 'abc',
-        _type: {              // pretend this is on the prototype
-          kind: kind.STRUCT,
-          fields: [
-            {
-              name: 'A',
-              type: types.ANY
-            }
-          ]
-        }
-      },
-      expectedOutput: {
-        val: {
-          a: 'abc'
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(JSValue w/ null))',
-      expectedOutput: null,
-      v: null,
-      t: types.JSVALUE
-    },
-    {
-      n: 'Decode(Encode(Any w/ null))',
-      expectedOutput: {
-        val: null
-      },
-      v: null,            // guesses to be a null of type ANY
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any w/ null)) - wrapLike))',
-      expectedOutput: {
-        val: {
-          val: null
-        }
-      },
-      v: {
-        val: null         // is not a null of type ANY; it's a struct
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any w/ null)) - ANY wrap',
-      expectedOutput: {
-        val: null
-      },
-      v: {
-        val: null,
-        _type: types.ANY,  // pretend this is on the prototype
-        _wrappedType: true // pretend this is on the prototype
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any w/ null)) - OPTIONAL wrap',
-      expectedOutput: {
-        val: {
-          val: null
-        }
-      },
-      v: {
-        val: null,
-        _type: {              // pretend this is on the prototype
-          kind: kind.OPTIONAL,
-          elem: types.STRING
-        },
-        _wrappedType: true    // pretend this is on the prototype
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any w/ null)) - in a struct',
-      v: {
-        a: null,
-        _type: {              // pretend this is on the prototype
-          kind: kind.STRUCT,
-          fields: [
-            {
-              name: 'A',
-              type: types.ANY
-            }
-          ]
-        }
-      },
-      expectedOutput: {
-        val: {
-          a: null
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Any w/ null)) - wrapped null in a struct',
-      v: {
-        a: {
-          val: null,
-          _type: types.ANY,   // pretend this is on the prototype
-          _wrappedType: true  // pretend this is on the prototype
-        },
-        _type: {              // pretend this is on the prototype
-          kind: kind.STRUCT,
-          fields: [
-            {
-              name: 'A',
-              type: types.ANY
-            }
-          ]
-        }
-      },
-      expectedOutput: {
-        val: {
-          a: null
-        }
-      },
-      t: types.ANY
-    },
-    {
-      n: 'Decode(Encode(Map in Map))',
-      expectedOutput: {
-        val: new Map([
-          [
-            'testMethod',
-            new Map([
-              ['numInArgs', 3],
-              ['numOutArgs', 3],
-              ['isStreaming', false]
-            ])
-          ],
-          [
-            'testMethod2',
-            new Map([
-              ['numInArgs', 2],
-              ['numOutArgs', 1],
-              ['isStreaming', true]
-            ])
-          ]
-        ])
-      },
-      v: new Map([
-        [
-          'testMethod',
-          new Map([
-            ['numInArgs', 3],
-            ['numOutArgs', 3],
-            ['isStreaming', false]
-          ])
-        ],
-        [
-          'testMethod2',
-          new Map([
-            ['numInArgs', 2],
-            ['numOutArgs', 1],
-            ['isStreaming', true]
-          ])
-        ]
-      ]),
-      t: {
-        kind: kind.MAP,
-        key: types.STRING,
-        elem: {
+    t: {
+      kind: kind.UNION,
+      name: 'UnionName',
+      fields: [
+        {
+        name: 'Map',
+        type: {
           kind: kind.MAP,
           key: types.STRING,
-          elem: types.ANY,
-        },
-      },
-    },
-    {
-      n: 'Struct zero-values are filled post-decode',
-      v: {
-        c: true,
-        _type: {
-          kind: kind.STRUCT,
-          fields: [
-            {
-              name: 'A',
-              type: types.UINT32
-            },
-            {
-              name: 'B',
-              type: types.STRING
-            },
-            {
-              name: 'C',
-              type: types.BOOL
-            }
-          ]
+          elem: types.UINT32
         }
       },
-      expectedOutput: {
+      {
+        name: 'List',
+        type: {
+          kind: kind.LIST,
+          elem: types.FLOAT64
+        }
+      }
+      ]
+    }
+  },
+  {
+    n: 'Decode(Encode(Union<Map[Uint16]Uint32, List<Float64>> w/ Map))',
+    v: {
+      'map': {                   // This is a native object; the fields
+        'a': 9,                  // are capitalized upon conversion to Map.
+        'b': 10,
+        _type: {
+          kind: kind.MAP,
+          key: types.STRING,
+          elem: types.UINT32
+        }
+      }
+    },
+    t: {
+      kind: kind.UNION,
+      name: 'UnionName',
+      fields: [
+        {
+        name: 'Map',
+        type: {
+          kind: kind.MAP,
+          key: types.STRING,
+          elem: types.UINT32
+        }
+      },
+      {
+        name: 'List',
+        type: {
+          kind: kind.LIST,
+          elem: types.FLOAT64
+        }
+      }
+      ]
+    },
+    expectedOutput: {
+      'map': new Map([
+        ['A', 9],
+        ['B', 10]
+      ])
+    }
+  },
+  {
+    n: 'Decode(Encode(Linked List Nodes))',
+    v: {
+      value: 9,
+      next: {
+        value: 10,
+        next: {
+          value: 11,
+          next: null
+        }
+      }
+    },
+    expectedOutput: {
+      value: 9,
+      next: {
+        value: 10,
+        next: {
+          value: 11,
+          next: null
+        }
+      }
+    },
+    t: linkedListNodeType
+  },
+  {
+    n: 'Decode(Encode(Tree Nodes))',
+    v: {
+      value: 4,
+      left: {
+        value: 5,
+        left: null,
+        right: null
+      },
+      right: {
+        value: false,
+        left: {
+          value: true,
+          left: null,
+          right: null
+        },
+        right: null
+      }
+    },
+    expectedOutput: {
+      value: 4,
+      left: {
+        value: 5,
+        left: null,
+        right: null
+      },
+      right: {
+        value: false,
+        left: {
+          value: true,
+          left: null,
+          right: null
+        },
+        right: null
+      }
+    },
+    t: treeNodeType
+  },
+  {
+    n: 'Decode(Encode(Any))',
+    v: 5,
+    expectedOutput: { // any with JSValue(number)
+      val: 5
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any)) - wrapLike',
+    v: {
+      val: 5
+    },
+    expectedOutput: { // any with JSValue(object with field val: 5)
+      val: {
+        val: 5
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any)) - INT32 wrap',
+    v: {
+      val: 5,
+      _type: types.INT32, // pretend this is on the prototype
+      _wrappedType: true  // pretend this is on the prototype
+    },
+    expectedOutput: {
+      val: {
+        val: 5
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any)) - Optional wrap',
+    v: {
+      val: 'not null',
+      _type: {              // pretend this is on the prototype
+        kind: kind.OPTIONAL,
+        elem: types.STRING
+      },
+      _wrappedType: true    // pretend this is on the prototype
+    },
+    expectedOutput: {
+      val: {
+        val: 'not null'
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any)) - Struct wrap',
+    v: {
+      a: 'abc',
+      _type: {              // pretend this is on the prototype
+        kind: kind.STRUCT,
+        fields: [
+          {
+          name: 'A',
+          type: types.STRING
+        }
+        ]
+      }
+    },
+    expectedOutput: {
+      val: {
+        a: 'abc'
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any)) - Struct w/ Any wrap',
+    v: {
+      a: 'abc',
+      _type: {              // pretend this is on the prototype
+        kind: kind.STRUCT,
+        fields: [
+          {
+          name: 'A',
+          type: types.ANY
+        }
+        ]
+      }
+    },
+    expectedOutput: {
+      val: {
+        a: 'abc'
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(JSValue w/ null))',
+    expectedOutput: null,
+    v: null,
+    t: types.JSVALUE
+  },
+  {
+    n: 'Decode(Encode(Any w/ null))',
+    expectedOutput: {
+      val: null
+    },
+    v: null,            // guesses to be a null of type ANY
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any w/ null)) - wrapLike))',
+    expectedOutput: {
+      val: {
+        val: null
+      }
+    },
+    v: {
+      val: null         // is not a null of type ANY; it's a struct
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any w/ null)) - ANY wrap',
+    expectedOutput: {
+      val: null
+    },
+    v: {
+      val: null,
+      _type: types.ANY,  // pretend this is on the prototype
+      _wrappedType: true // pretend this is on the prototype
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any w/ null)) - OPTIONAL wrap',
+    expectedOutput: {
+      val: {
+        val: null
+      }
+    },
+    v: {
+      val: null,
+      _type: {              // pretend this is on the prototype
+        kind: kind.OPTIONAL,
+        elem: types.STRING
+      },
+      _wrappedType: true    // pretend this is on the prototype
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any w/ null)) - in a struct',
+    v: {
+      a: null,
+      _type: {              // pretend this is on the prototype
+        kind: kind.STRUCT,
+        fields: [
+          {
+          name: 'A',
+          type: types.ANY
+        }
+        ]
+      }
+    },
+    expectedOutput: {
+      val: {
+        a: null
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Any w/ null)) - wrapped null in a struct',
+    v: {
+      a: {
+        val: null,
+        _type: types.ANY,   // pretend this is on the prototype
+        _wrappedType: true  // pretend this is on the prototype
+      },
+      _type: {              // pretend this is on the prototype
+        kind: kind.STRUCT,
+        fields: [
+          {
+          name: 'A',
+          type: types.ANY
+        }
+        ]
+      }
+    },
+    expectedOutput: {
+      val: {
+        a: null
+      }
+    },
+    t: types.ANY
+  },
+  {
+    n: 'Decode(Encode(Map in Map))',
+    expectedOutput: {
+      val: new Map([
+        [
+        'testMethod',
+        new Map([
+          ['numInArgs', 3],
+          ['numOutArgs', 3],
+          ['isStreaming', false]
+        ])
+      ],
+      [
+        'testMethod2',
+        new Map([
+          ['numInArgs', 2],
+          ['numOutArgs', 1],
+          ['isStreaming', true]
+        ])
+      ]
+      ])
+    },
+    v: new Map([
+      [
+      'testMethod',
+      new Map([
+        ['numInArgs', 3],
+        ['numOutArgs', 3],
+        ['isStreaming', false]
+      ])
+    ],
+    [
+      'testMethod2',
+      new Map([
+        ['numInArgs', 2],
+        ['numOutArgs', 1],
+        ['isStreaming', true]
+      ])
+    ]
+    ]),
+    t: {
+      kind: kind.MAP,
+      key: types.STRING,
+      elem: {
+        kind: kind.MAP,
+        key: types.STRING,
+        elem: types.ANY,
+      },
+    },
+  },
+  {
+    n: 'Struct zero-values are filled post-decode',
+    v: {
+      c: true,
+      _type: {
+        kind: kind.STRUCT,
+        fields: [
+          {
+          name: 'A',
+          type: types.UINT32
+        },
+        {
+          name: 'B',
+          type: types.STRING
+        },
+        {
+          name: 'C',
+          type: types.BOOL
+        }
+        ]
+      }
+    },
+    expectedOutput: {
+      a: 0,
+      b: '',
+      c: true
+    }
+  },
+  {
+    n: 'Internal struct zero-values are filled post-decode',
+    v: [
+      {
+      c: true
+    },
+    {
+      a: 3
+    },
+    {
+      b: 'hello'
+    },
+    {
+      a: 42,
+      b: 'all here',
+      c: false
+    }
+    ],
+    expectedOutput: {
+      val: [
+        {
         a: 0,
         b: '',
         c: true
-      }
-    },
-    {
-      n: 'Internal struct zero-values are filled post-decode',
-      v: [
-        {
-          c: true
-        },
-        {
-          a: 3
-        },
-        {
-          b: 'hello'
-        },
-        {
-          a: 42,
-          b: 'all here',
-          c: false
-        }
-      ],
-      expectedOutput: {
-        val: [
-          {
-            a: 0,
-            b: '',
-            c: true
-          },
-          {
-            a: 3,
-            b: '',
-            c: false
-          },
-          {
-            a: 0,
-            b: 'hello',
-            c: false
-          },
-          {
-            a: 42,
-            b: 'all here',
-            c: false
-          }
-        ]
       },
-      t: {
-        kind: kind.LIST,
-        elem: {
-          kind: kind.STRUCT,
-          fields: [
-            {
-              name: 'A',
-              type: types.UINT32
-            },
-            {
-              name: 'B',
-              type: types.STRING
-            },
-            {
-              name: 'C',
-              type: types.BOOL
-            }
-          ]
-        }
-      }
-    },
-    {
-      n: 'native string',
-      v: 'hi',
-      expectedOutput: 'hi'
-    },
-    {
-      n: 'native number',
-      v: 4,
-      expectedOutput: 4
-    },
-    {
-      n: 'native bool',
-      v: true,
-      expectedOutput: true
-    },
-    {
-      n: 'native list',
-      v: [{}, 'f', true],
-      expectedOutput: [{}, 'f', true]
-    },
-    {
-      n: 'native object',
-      v: {
-        a: 'three',
-        A: 'THREE',
-        b: 3
-      },
-      expectedOutput: {
-        a: 'three',
-        A: 'THREE',
-        b: 3
-      }
-    },
-    {
-      n: 'native map',
-      v: new Map([
-        [null, 3],
-        ['asdf', 'jkle']
-      ]),
-      expectedOutput: new Map([
-        [null, 3],
-        ['asdf', 'jkle']
-      ])
-    },
-    {
-      n: 'native set',
-      v: new Set([null, 3, true, ['asdf', 'jkle']]),
-      expectedOutput: new Set([null, 3, true, ['asdf', 'jkle']])
-    },
-    {
-      n: 'typed string',
-      v: new (registry.lookupOrCreateConstructor(types.STRING))(''),
-      expectedOutput: new (registry.lookupOrCreateConstructor(types.STRING))('')
-    },
-    {
-      n: 'typed number',
-      v: new (registry.lookupOrCreateConstructor(types.INT16))(4),
-      expectedOutput: new (registry.lookupOrCreateConstructor(types.INT16))(4)
-    },
-    {
-      n: 'typed boolean',
-      v: new (registry.lookupOrCreateConstructor(types.BOOL))(true),
-      expectedOutput: new (registry.lookupOrCreateConstructor(types.BOOL))(true)
-    },
-  ];
-  var promises = [];
-  function runTestCase(test, useCb) {
-    var messageWriter = new ByteArrayMessageWriter();
-    var encoder = new Encoder(messageWriter);
-    encoder.encode(test.v, test.t); // encode to messageWriter
-
-    var messageReader = new ByteArrayMessageReader(messageWriter.getBytes());
-
-    var decoder = new Decoder(messageReader);
-    if (useCb) {
-      var def = new Deferred();
-      decoder.decode(function(err, result) {
-        def.resolve();
-        if (err) {
-          return t.fail(test.n + ' (cb) failed with ' + err.stack);
-        }
-        handleResult(result);
-      });
-      promises.push(def.promise);
-    } else {
-      promises.push(decoder.decode().then(handleResult, function(err) {
-        t.fail(test.n + ' (promise) failed with ' + err.stack);
-      }));
-    }
-
-
-    function handleResult(result) {
-      var asyncType = useCb ? ' (cb)' : ' (promise)';
-      var resultStr = stringify(result);
-      var expected = test.expectedOutput || test.v;
-      var expectedStr = stringify(expected);
-      t.equals(resultStr, expectedStr, test.n + asyncType +
-               ' - decode value match');
-
-      // Then validate that we were given a canonicalized value.
-      // Note that some results are native post-decode; if so, use
-      // types.JSVALUE.
-      var resultType = types.JSVALUE;
-      if (typeUtil.isTyped(result)) {
-        resultType = result._type;
-      }
-      t.deepEqual(
-        canonicalize.reduce(result, resultType),
-        expected,
-        test.n + asyncType + ' - decode value validation'
-      );
-
-      // If given a type, check that the decoded object's type matches it.
-      // TODO(bprosnitz) Even if test.t isn't defined, we should still know
-      // what the expected type ought to be.
-      if (test.t) {
-        var resultTypeStr = stringify(resultType);
-        var expectedTypeStr = stringify(canonicalize.type(test.t));
-        t.equals(resultTypeStr, expectedTypeStr, test.n + asyncType +
-                 ' - decode type match');
-      }
-    }
-  }
-  for (var i = 0; i < tests.length; i++) {
-    runTestCase(tests[i], false);
-    runTestCase(tests[i], true);
-  }
-  Promise.all(promises).then(function() {
-    t.end();
-  }, t.end);
-});
-
-test('encode error cases', function(t) {
-  var Str = registry.lookupOrCreateConstructor(types.STRING);
-  var IntList = registry.lookupOrCreateConstructor(new Type({
-    kind: kind.LIST,
-    elem: types.INT16
-  }));
-
-  var tests = [
-    {
-      n: 'converting null to non-optional type',
-      v: null,
-      t: types.UINT64
-    },
-    {
-      n: 'encoding float as int',
-      v: 3.5,
-      t: types.INT32
-    },
-    {
-      n: 'converting string to complex type',
-      v: 'a string cannot convert to Complex',
-      t: types.COMPLEX64
-    },
-    {
-      n: 'converting wrapped string to complex type',
-      v: new Str('a string cannot convert to Complex'),
-      t: types.COMPLEX64,
-      e: 'are not compatible'
-    },
-    {
-      n: 'using value as typeobject',
-      v: [3, 4, 90],
-      t: types.TYPEOBJECT
-    },
-    {
-      n: 'using wrapped value as typeobject',
-      v: new IntList([3, 4, 90]),
-      t: types.TYPEOBJECT,
-      e: 'are not compatible'
-    },
-    {
-      n: 'using label not in enum',
-      v: 'Thursday',
-      t: {
-        kind: kind.ENUM,
-        labels: ['Sunday', 'Monday', 'Tuesday']
-      }
-    },
-    {
-      n: 'array size mismatch',
-      v: [2, -4, 9, 34],
-      t: {
-        kind: kind.ARRAY,
-        elem: types.INT16,
-        len: 3
-      }
-    },
-    {
-      n: 'map does not convert to set',
-      v: new Map([
-        ['a', true],
-        ['b', true],
-        ['c', true]
-      ]),
-      t: {
-        kind: kind.SET,
-        key: types.STRING
-      }
-    },
-    {
-      n: 'object cannot be converted to non-string keyed sets',
-      v: {
-        a: true,
-        b: true,
-        c: true
-      },
-      t: {
-        kind: kind.SET,
-        key: types.FLOAT64
-      }
-    },
-    {
-      n: 'object cannot be converted to non-string keyed maps',
-      v: {
+      {
         a: 3,
-        b: true,
-        c: 'asf'
+        b: '',
+        c: false
       },
-      t: {
-        kind: kind.MAP,
-        key: types.UINT32,
-        elem: types.ANY
+      {
+        a: 0,
+        b: 'hello',
+        c: false
+      },
+      {
+        a: 42,
+        b: 'all here',
+        c: false
       }
+      ]
     },
-    {
-      n: 'extra struct entry', // TODO(alexfandrianto): Should we drop the field
-      v: {                     // instead of throwing an error?
-        a: 3,
-        b: 0,
-        c: 'asf',
-        d: 'KABOOM! This cannot be here!'
-      },
-      t: {
+    t: {
+      kind: kind.LIST,
+      elem: {
         kind: kind.STRUCT,
         fields: [
           {
-            name: 'A',
-            type: types.INT16
-          },
-          {
-            name: 'B',
-            type: types.BYTE
-          },
-          {
-            name: 'C',
-            type: types.STRING
-          }
-        ]
-      }
-    },
-    {
-      n: 'Union is not TwoOrMoreOf',
-      v: {
-        a: 3,
-        c: 'asf'
-      },
-      t: {
-        kind: kind.UNION,
-        fields: [
-          {
-            name: 'A',
-            type: types.INT16
-          },
-          {
-            name: 'B',
-            type: types.BYTE
-          },
-          {
-            name: 'C',
-            type: types.STRING
-          }
-        ]
-      }
-    },
-    {
-      n: 'Union is not NoneOf',
-      v: {},
-      t: {
-        kind: kind.UNION,
-        fields: [
-          {
-            name: 'A',
-            type: types.INT16
-          },
-          {
-            name: 'B',
-            type: types.BYTE
-          },
-          {
-            name: 'C',
-            type: types.STRING
-          }
+          name: 'A',
+          type: types.UINT32
+        },
+        {
+          name: 'B',
+          type: types.STRING
+        },
+        {
+          name: 'C',
+          type: types.BOOL
+        }
         ]
       }
     }
-  ];
+  },
+  {
+    n: 'native string',
+    v: 'hi',
+    expectedOutput: 'hi'
+  },
+  {
+    n: 'native number',
+    v: 4,
+    expectedOutput: 4
+  },
+  {
+    n: 'native bool',
+    v: true,
+    expectedOutput: true
+  },
+  {
+    n: 'native list',
+    v: [{}, 'f', true],
+    expectedOutput: [{}, 'f', true]
+  },
+  {
+    n: 'native object',
+    v: {
+      a: 'three',
+      A: 'THREE',
+      b: 3
+    },
+    expectedOutput: {
+      a: 'three',
+      A: 'THREE',
+      b: 3
+    }
+  },
+  {
+    n: 'native map',
+    v: new Map([
+      [null, 3],
+      ['asdf', 'jkle']
+    ]),
+    expectedOutput: new Map([
+      [null, 3],
+      ['asdf', 'jkle']
+    ])
+  },
+  {
+    n: 'native set',
+    v: new Set([null, 3, true, ['asdf', 'jkle']]),
+    expectedOutput: new Set([null, 3, true, ['asdf', 'jkle']])
+  },
+  {
+    n: 'typed string',
+    v: new (registry.lookupOrCreateConstructor(types.STRING))(''),
+    expectedOutput: new (registry.lookupOrCreateConstructor(types.STRING))('')
+  },
+  {
+    n: 'typed number',
+    v: new (registry.lookupOrCreateConstructor(types.INT16))(4),
+    expectedOutput: new (registry.lookupOrCreateConstructor(types.INT16))(4)
+  },
+  {
+    n: 'typed boolean',
+    v: new (registry.lookupOrCreateConstructor(types.BOOL))(true),
+    expectedOutput: new (registry.lookupOrCreateConstructor(types.BOOL))(true)
+  },
+].forEach(function(testCase) {
+  test('encode and decode - ' + testCase.n, function(t) {
+    var promises = [];
+    function runTestCase(test, useCb) {
+      var messageWriter = new ByteArrayMessageWriter();
+      var encoder = new Encoder(messageWriter);
+      encoder.encode(test.v, test.t); // encode to messageWriter
 
-  for (var i = 0; i < tests.length; i++) {
-    var test = tests[i];
+      var messageReader = new ByteArrayMessageReader(messageWriter.getBytes());
+
+      var decoder = new Decoder(messageReader);
+      if (useCb) {
+        var def = new Deferred();
+        decoder.decode(function(err, result) {
+          def.resolve();
+          if (err) {
+            return t.fail('(cb) failed with ' + err.stack);
+          }
+          handleResult(result);
+        });
+        promises.push(def.promise);
+      } else {
+        promises.push(decoder.decode().then(handleResult, function(err) {
+          t.fail(' (promise) failed with ' + err.stack);
+        }));
+      }
+
+
+      function handleResult(result) {
+        var asyncType = useCb ? ' (cb)' : ' (promise)';
+        var resultStr = stringify(result);
+        var expected = test.expectedOutput || test.v;
+        var expectedStr = stringify(expected);
+        t.equals(resultStr, expectedStr, asyncType +
+                 ' - decode value match');
+
+        // Then validate that we were given a canonicalized value.
+        // Note that some results are native post-decode; if so, use
+        // types.JSVALUE.
+        var resultType = types.JSVALUE;
+        if (typeUtil.isTyped(result)) {
+          resultType = result._type;
+        }
+        t.deepEqual(
+          canonicalize.reduce(result, resultType),
+          expected,
+          asyncType + ' - decode value validation'
+        );
+
+        // If given a type, check that the decoded object's type matches it.
+        // TODO(bprosnitz) Even if test.t isn't defined, we should still know
+        // what the expected type ought to be.
+        if (test.t) {
+          var resultTypeStr = stringify(resultType);
+          var expectedTypeStr = stringify(canonicalize.type(test.t));
+          t.equals(resultTypeStr, expectedTypeStr, asyncType +
+                   ' - decode type match');
+        }
+      }
+    }
+    runTestCase(testCase, true);
+    runTestCase(testCase, false);
+    Promise.all(promises).then(function() {
+      t.end();
+    }, t.end);
+  });
+});
+var Str = registry.lookupOrCreateConstructor(types.STRING);
+var IntList = registry.lookupOrCreateConstructor(new Type({
+  kind: kind.LIST,
+  elem: types.INT16
+}));
+
+[
+  {
+    n: 'converting null to non-optional type',
+    v: null,
+    t: types.UINT64
+  },
+  {
+    n: 'encoding float as int',
+    v: 3.5,
+    t: types.INT32
+  },
+  {
+    n: 'converting string to complex type',
+    v: 'a string cannot convert to Complex',
+    t: types.COMPLEX64
+  },
+  {
+    n: 'converting wrapped string to complex type',
+    v: new Str('a string cannot convert to Complex'),
+    t: types.COMPLEX64,
+    e: 'are not compatible'
+  },
+  {
+    n: 'using value as typeobject',
+    v: [3, 4, 90],
+    t: types.TYPEOBJECT
+  },
+  {
+    n: 'using wrapped value as typeobject',
+    v: new IntList([3, 4, 90]),
+    t: types.TYPEOBJECT,
+    e: 'are not compatible'
+  },
+  {
+    n: 'using label not in enum',
+    v: 'Thursday',
+    t: {
+      kind: kind.ENUM,
+      labels: ['Sunday', 'Monday', 'Tuesday']
+    }
+  },
+  {
+    n: 'array size mismatch',
+    v: [2, -4, 9, 34],
+    t: {
+      kind: kind.ARRAY,
+      elem: types.INT16,
+      len: 3
+    }
+  },
+  {
+    n: 'map does not convert to set',
+    v: new Map([
+      ['a', true],
+      ['b', true],
+      ['c', true]
+    ]),
+    t: {
+      kind: kind.SET,
+      key: types.STRING
+    }
+  },
+  {
+    n: 'object cannot be converted to non-string keyed sets',
+    v: {
+      a: true,
+      b: true,
+      c: true
+    },
+    t: {
+      kind: kind.SET,
+      key: types.FLOAT64
+    }
+  },
+  {
+    n: 'object cannot be converted to non-string keyed maps',
+    v: {
+      a: 3,
+      b: true,
+      c: 'asf'
+    },
+    t: {
+      kind: kind.MAP,
+      key: types.UINT32,
+      elem: types.ANY
+    }
+  },
+  {
+    n: 'extra struct entry', // TODO(alexfandrianto): Should we drop the field
+    v: {                     // instead of throwing an error?
+      a: 3,
+      b: 0,
+      c: 'asf',
+      d: 'KABOOM! This cannot be here!'
+    },
+    t: {
+      kind: kind.STRUCT,
+      fields: [
+        {
+          name: 'A',
+          type: types.INT16
+        },
+        {
+          name: 'B',
+          type: types.BYTE
+        },
+        {
+          name: 'C',
+          type: types.STRING
+        }
+      ]
+    }
+  },
+  {
+    n: 'Union is not TwoOrMoreOf',
+    v: {
+      a: 3,
+      c: 'asf'
+    },
+    t: {
+      kind: kind.UNION,
+      fields: [
+        {
+          name: 'A',
+          type: types.INT16
+        },
+        {
+          name: 'B',
+          type: types.BYTE
+        },
+        {
+          name: 'C',
+          type: types.STRING
+        }
+      ]
+    }
+  },
+  {
+    n: 'Union is not NoneOf',
+    v: {},
+    t: {
+      kind: kind.UNION,
+      fields: [
+        {
+          name: 'A',
+          type: types.INT16
+        },
+        {
+          name: 'B',
+          type: types.BYTE
+        },
+        {
+          name: 'C',
+          type: types.STRING
+        }
+      ]
+    }
+  }
+].forEach(function(testCase) {
+  test('encode error cases - ' + test.n, function(t) {
     var messageWriter = new ByteArrayMessageWriter();
     var encoder = new Encoder(messageWriter);
-    t.throws(encoder.encode.bind(encoder, test.v, test.t),
-      new RegExp('.*' + (test.e || '') + '.*'), test.n);
-  }
-  t.end();
+    t.throws(encoder.encode.bind(encoder, testCase.v, testCase.t),
+             new RegExp('.*' + (testCase.e || '') + '.*'));
+    t.end();
+  });
 });