Clean up and tests for mojo <-> vom transcoder

MultiPart: 2/2
Change-Id: Ia146daceda073f495934ba6847e8f92d283f4cc4
diff --git a/Makefile b/Makefile
index 1a94de4..4636b72 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,14 @@
 
 build-examples: $(BUILD_DIR)/echo_client.mojo $(BUILD_DIR)/echo_server.mojo $(BUILD_DIR)/fortune_client.mojo $(BUILD_DIR)/fortune_server.mojo
 
+# Go-based unit tests
+test: gen/go/src/mojom/tests/transcoder_testcases/transcoder_testcases.mojom.go
+	$(call MOGO_TEST,v.io/x/mojo/transcoder/...)
+
+gen/go/src/mojom/tests/transcoder_testcases/transcoder_testcases.mojom.go: mojom/mojom/tests/transcoder_testcases.mojom | mojo-env-check
+	$(call MOJOM_GEN,$<,mojom,gen,go)
+	gofmt -w $@
+
 $(BUILD_DIR)/echo_client.mojo: $(MOJO_SHARED_LIB) gen/go/src/mojom/examples/echo/echo.mojom.go
 	$(call MOGO_BUILD,examples/echo/client,$@)
 
diff --git a/go/src/v.io/x/mojo/proxy/fake_service.go b/go/src/v.io/x/mojo/proxy/fake_service.go
index 294bff3..56e3fa6 100644
--- a/go/src/v.io/x/mojo/proxy/fake_service.go
+++ b/go/src/v.io/x/mojo/proxy/fake_service.go
@@ -273,9 +273,9 @@
 
 	// Decode the *vdl.Value from the mojom bytes and mojom type.
 	outType := transcoder.MojomStructToVDLType(*mm.ResponseParams, desc)
-	outVdlValue, err := transcoder.DecodeValue(outMessage.Payload, outType)
-	if err != nil {
-		return nil, fmt.Errorf("transcoder.DecodeValue failed: %v", err)
+	var outVdlValue *vdl.Value
+	if err := transcoder.MojomToVdl(outMessage.Payload, outType, &outVdlValue); err != nil {
+		return nil, fmt.Errorf("transcoder.MojoToVom failed: %v", err)
 	}
 
 	// Then split the *vdl.Value (struct) into []*vdl.Value
diff --git a/go/src/v.io/x/mojo/proxy/misc.go b/go/src/v.io/x/mojo/proxy/misc.go
index 27d5e78..19b0f70 100644
--- a/go/src/v.io/x/mojo/proxy/misc.go
+++ b/go/src/v.io/x/mojo/proxy/misc.go
@@ -6,8 +6,8 @@
 
 	"mojo/public/go/bindings"
 
-	"v.io/x/mojo/transcoder"
 	"v.io/v23/vdl"
+	"v.io/x/mojo/transcoder"
 )
 
 // TODO(alexfandrianto): Since this function could panic, we should consider
@@ -49,7 +49,7 @@
 		return nil, err
 	} else {
 		// Encode here.
-		moreBytes, err := transcoder.Encode(vdlValue)
+		moreBytes, err := transcoder.VdlToMojom(vdlValue)
 		if err != nil {
 			return nil, fmt.Errorf("mojovdl.Encode failed: %v", err)
 		}
diff --git a/go/src/v.io/x/mojo/proxy/proxy.go b/go/src/v.io/x/mojo/proxy/proxy.go
index 72ae21d..1e64bfd 100644
--- a/go/src/v.io/x/mojo/proxy/proxy.go
+++ b/go/src/v.io/x/mojo/proxy/proxy.go
@@ -161,9 +161,9 @@
 	}
 
 	// Decode the vdl.Value from the mojom bytes and mojom type.
-	inVdlValue, err := transcoder.DecodeValue(value, inVType)
-	if err != nil {
-		return nil, fmt.Errorf("transcoder.DecodeValue failed: %v", err)
+	var inVdlValue *vdl.Value
+	if err := transcoder.MojomToVdl(value, inVType, &inVdlValue); err != nil {
+		return nil, fmt.Errorf("transcoder.MojoToVom failed: %v", err)
 	}
 
 	// inVdlValue is a struct, but we need to send []interface.
@@ -191,7 +191,7 @@
 	outVdlValue := combineVdlValueByMojomType(outargs, outVType)
 
 	// Finally, encode this *vdl.Value (struct) into mojom bytes and send the response.
-	result, err := transcoder.Encode(outVdlValue)
+	result, err := transcoder.VdlToMojom(outVdlValue)
 	if err != nil {
 		return nil, fmt.Errorf("transcoder.Encode failed: %v", err)
 	}
diff --git a/go/src/v.io/x/mojo/transcoder/encode.go b/go/src/v.io/x/mojo/transcoder/encode.go
deleted file mode 100644
index 2625ffb..0000000
--- a/go/src/v.io/x/mojo/transcoder/encode.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package transcoder
-
-import (
-	"reflect"
-
-	"v.io/v23/vdl"
-)
-
-func Encode(value interface{}) ([]byte, error) {
-	enc := &encoder{
-		allocator: &allocator{},
-	}
-	err := vdl.FromReflect(enc, reflect.ValueOf(value))
-	return enc.Bytes(), err
-}
-
-type encoder struct {
-	allocator *allocator
-}
-
-func (e *encoder) Bytes() []byte {
-	return e.allocator.AllocatedBytes()
-}
-
-func (e *encoder) FromBool(src bool, tt *vdl.Type) error {
-	panic("cannot encode top level bool")
-}
-func (e *encoder) FromUint(src uint64, tt *vdl.Type) error {
-	panic("cannot encode top level uint")
-}
-func (e *encoder) FromInt(src int64, tt *vdl.Type) error {
-	panic("cannot encode top level int")
-}
-func (e *encoder) FromFloat(src float64, tt *vdl.Type) error {
-	panic("cannot encode top level float")
-}
-func (e *encoder) FromComplex(src complex128, tt *vdl.Type) error {
-	panic("cannot encode top level complex")
-}
-func (e *encoder) FromBytes(src []byte, tt *vdl.Type) error {
-	panic("cannot encode top level bytes")
-}
-func (e *encoder) FromString(src string, tt *vdl.Type) error {
-	panic("cannot encode top level string")
-}
-func (e *encoder) FromEnumLabel(src string, tt *vdl.Type) error {
-	panic("cannot encode top level enum")
-}
-func (e *encoder) FromTypeObject(src *vdl.Type) error {
-	panic("cannot encode top level type object")
-}
-func (e *encoder) FromNil(tt *vdl.Type) error {
-	panic("cannot encode top level nil")
-}
-
-func (e *encoder) StartList(tt *vdl.Type, len int) (vdl.ListTarget, error) {
-	panic("UNIMPLEMENTED")
-	return nil, nil
-}
-func (e *encoder) FinishList(x vdl.ListTarget) error {
-	return nil
-}
-func (e *encoder) StartSet(tt *vdl.Type, len int) (vdl.SetTarget, error) {
-	panic("UNIMPLEMENTED")
-}
-func (e *encoder) FinishSet(x vdl.SetTarget) error {
-	panic("UNIMPLEMENTED")
-
-}
-func (e *encoder) StartMap(tt *vdl.Type, len int) (vdl.MapTarget, error) {
-	panic("UNIMPLEMENTED")
-
-}
-func (e *encoder) FinishMap(x vdl.MapTarget) error {
-	panic("UNIMPLEMENTED")
-
-}
-func (e *encoder) StartFields(tt *vdl.Type) (vdl.FieldsTarget, error) {
-	if tt.Kind() == vdl.Union {
-		panic("not yet supported")
-	}
-	if tt.Kind() == vdl.Optional {
-		tt = tt.Elem()
-	}
-	block := e.allocator.Allocate(neededStructAllocationSize(tt), 0)
-	return fieldsTarget{
-			vdlType: tt,
-			block:   block,
-		},
-		nil
-}
-func (e *encoder) FinishFields(x vdl.FieldsTarget) error {
-	return nil
-}
diff --git a/go/src/v.io/x/mojo/transcoder/decode.go b/go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go
similarity index 77%
rename from go/src/v.io/x/mojo/transcoder/decode.go
rename to go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go
index 432aa14..fe87aab 100644
--- a/go/src/v.io/x/mojo/transcoder/decode.go
+++ b/go/src/v.io/x/mojo/transcoder/mojom_to_vdl.go
@@ -13,99 +13,90 @@
 // the desired value.  The datatype describes the type of the encoded data.
 // Returns an error if the data cannot be decoded into valptr, based on the VDL
 // value conversion rules.
-func Decode(data []byte, datatype *vdl.Type, valptr interface{}) error {
+func MojomToVdl(data []byte, datatype *vdl.Type, valptr interface{}) error {
 	target, err := vdl.ReflectTarget(reflect.ValueOf(valptr))
 	if err != nil {
 		return err
 	}
-	d := &decoder{dec: bindings.NewDecoder(data, nil)}
-	return d.decodeValue(datatype, target, true, false)
+	mtv := &mojomToVdlTranscoder{modec: bindings.NewDecoder(data, nil)}
+	return mtv.transcodeValue(datatype, target, true, false)
 }
 
-// DecodeValue is like Decode, but decodes mojom-encoded data into a vdl.Value.
-func DecodeValue(data []byte, datatype *vdl.Type) (*vdl.Value, error) {
-	v := new(vdl.Value)
-	if err := Decode(data, datatype, &v); err != nil {
-		return nil, err
-	}
-	return v, nil
-}
-
-type decoder struct {
-	dec       *bindings.Decoder
+type mojomToVdlTranscoder struct {
+	modec *bindings.Decoder
 	typeStack []*vdl.Type
 }
 
-func (d *decoder) decodeValue(vt *vdl.Type, target vdl.Target, isTopType, isNullable bool) error {
+func (mtv *mojomToVdlTranscoder) transcodeValue(vt *vdl.Type, target vdl.Target, isTopType, isNullable bool) error {
 	switch vt.Kind() {
 	case vdl.Bool:
-		value, err := d.dec.ReadBool()
+		value, err := mtv.modec.ReadBool()
 		if err != nil {
 			return err
 		}
 		return target.FromBool(value, vt)
 	case vdl.Int16:
-		value, err := d.dec.ReadInt16()
+		value, err := mtv.modec.ReadInt16()
 		if err != nil {
 			return err
 		}
 		return target.FromInt(int64(value), vt)
 	case vdl.Int32:
-		value, err := d.dec.ReadInt32()
+		value, err := mtv.modec.ReadInt32()
 		if err != nil {
 			return err
 		}
 		return target.FromInt(int64(value), vt)
 	case vdl.Int64:
-		value, err := d.dec.ReadInt64()
+		value, err := mtv.modec.ReadInt64()
 		if err != nil {
 			return err
 		}
 		return target.FromInt(value, vt)
 	case vdl.Byte:
-		value, err := d.dec.ReadUint8()
+		value, err := mtv.modec.ReadUint8()
 		if err != nil {
 			return err
 		}
 		return target.FromUint(uint64(value), vt)
 	case vdl.Uint16:
-		value, err := d.dec.ReadUint16()
+		value, err := mtv.modec.ReadUint16()
 		if err != nil {
 			return err
 		}
 		return target.FromUint(uint64(value), vt)
 	case vdl.Uint32:
-		value, err := d.dec.ReadUint32()
+		value, err := mtv.modec.ReadUint32()
 		if err != nil {
 			return err
 		}
 		return target.FromUint(uint64(value), vt)
 	case vdl.Uint64:
-		value, err := d.dec.ReadUint64()
+		value, err := mtv.modec.ReadUint64()
 		if err != nil {
 			return err
 		}
 		return target.FromUint(value, vt)
 	case vdl.Float32:
-		value, err := d.dec.ReadFloat32()
+		value, err := mtv.modec.ReadFloat32()
 		if err != nil {
 			return err
 		}
 		return target.FromFloat(float64(value), vt)
 	case vdl.Float64:
-		value, err := d.dec.ReadFloat64()
+		value, err := mtv.modec.ReadFloat64()
 		if err != nil {
 			return err
 		}
 		return target.FromFloat(value, vt)
 	case vdl.String:
-		switch ptr, err := d.dec.ReadPointer(); {
+		switch ptr, err := mtv.modec.ReadPointer(); {
 		case err != nil:
 			return err
 		case ptr == 0:
 			return fmt.Errorf("invalid null string pointer")
 		default:
-			value, err := d.dec.ReadString()
+			value, err := mtv.modec.ReadString()
 			if err != nil {
 				return err
 			}
@@ -113,7 +104,7 @@
 		}
 		return nil
 	case vdl.Enum:
-		index, err := d.dec.ReadInt32()
+		index, err := mtv.modec.ReadInt32()
 		if err != nil {
 			return err
 		}
@@ -127,7 +118,7 @@
 	case vdl.Complex128:
 		panic("unimplemented")
 	case vdl.Array, vdl.List:
-		switch ptr, err := d.dec.ReadPointer(); {
+		switch ptr, err := mtv.modec.ReadPointer(); {
 		case err != nil:
 			return err
 		case ptr == 0 && isNullable:
@@ -137,14 +128,14 @@
 		}
 
 		if vt.IsBytes() {
-			str, err := d.dec.ReadString()
+			str, err := mtv.modec.ReadString()
 			if err != nil {
 				return err
 			}
 			return target.FromBytes([]byte(str), vt)
 		} else {
 			elemBitSize := baseTypeSizeBits(vt.Elem())
-			numElems, err := d.dec.StartArray(elemBitSize)
+			numElems, err := mtv.modec.StartArray(elemBitSize)
 			if err != nil {
 				return err
 			}
@@ -157,7 +148,7 @@
 				if err != nil {
 					return err
 				}
-				if err := d.decodeValue(vt.Elem(), elemTarget, false, false); err != nil {
+				if err := mtv.transcodeValue(vt.Elem(), elemTarget, false, false); err != nil {
 					return err
 				}
 				if err := listTarget.FinishElem(elemTarget); err != nil {
@@ -168,7 +159,7 @@
 				return err
 			}
 		}
-		return d.dec.Finish()
+		return mtv.modec.Finish()
 	case vdl.Set:
 		panic("unimplemented")
 		/*switch ptr, err := d.dec.ReadPointer(); {
@@ -205,7 +196,7 @@
 		}
 		return d.dec.Finish()*/
 	case vdl.Map:
-		switch ptr, err := d.dec.ReadPointer(); {
+		switch ptr, err := mtv.modec.ReadPointer(); {
 		case err != nil:
 			return err
 		case ptr == 0 && isNullable:
@@ -213,7 +204,7 @@
 		case ptr == 0 && !isNullable:
 			return fmt.Errorf("invalid null struct pointer")
 		}
-		if err := d.dec.StartMap(); err != nil {
+		if err := mtv.modec.StartMap(); err != nil {
 			return err
 		}
 		var keys, values []*vdl.Value
@@ -222,7 +213,7 @@
 			return err
 		}
 		keysListType := vdl.ListType(vt.Key())
-		if err := d.decodeValue(keysListType, keysTarget, false, false); err != nil {
+		if err := mtv.transcodeValue(keysListType, keysTarget, false, false); err != nil {
 			return err
 		}
 		valuesTarget, err := vdl.ReflectTarget(reflect.ValueOf(&values))
@@ -230,7 +221,7 @@
 			return err
 		}
 		valuesListType := vdl.ListType(vt.Elem())
-		if err := d.decodeValue(valuesListType, valuesTarget, false, false); err != nil {
+		if err := mtv.transcodeValue(valuesListType, valuesTarget, false, false); err != nil {
 			return err
 		}
 
@@ -266,12 +257,12 @@
 			return err
 		}
 
-		return d.dec.Finish()
+		return mtv.modec.Finish()
 	case vdl.Struct:
 		// TODO(toddw): See the comment in encoder.mojomStructSize; we rely on the
 		// fields to be presented in the canonical mojom field ordering.
 		if !isTopType {
-			switch ptr, err := d.dec.ReadPointer(); {
+			switch ptr, err := mtv.modec.ReadPointer(); {
 			case err != nil:
 				return err
 			case ptr == 0 && isNullable:
@@ -280,7 +271,7 @@
 				return fmt.Errorf("invalid null struct pointer")
 			}
 		}
-		_, err := d.dec.StartStruct()
+		_, err := mtv.modec.StartStruct()
 		if err != nil {
 			return err
 		}
@@ -295,7 +286,7 @@
 			case err != nil:
 				return err
 			default:
-				if err := d.decodeValue(mfield.Type, vfield, false, false); err != nil {
+				if err := mtv.transcodeValue(mfield.Type, vfield, false, false); err != nil {
 					return err
 				}
 				if err := targetFields.FinishField(vkey, vfield); err != nil {
@@ -307,14 +298,14 @@
 		if err := target.FinishFields(targetFields); err != nil {
 			return err
 		}
-		return d.dec.Finish()
+		return mtv.modec.Finish()
 	case vdl.Union:
-		size, tag, err := d.dec.ReadUnionHeader()
+		size, tag, err := mtv.modec.ReadUnionHeader()
 		if err != nil {
 			return err
 		}
 		if size == 0 {
-			d.dec.SkipUnionValue()
+			mtv.modec.SkipUnionValue()
 			return target.FromNil(vdl.OptionalType(vt))
 		}
 		if int(tag) >= vt.NumField() {
@@ -330,7 +321,7 @@
 			return err
 		}
 		if fld.Type.Kind() == vdl.Union {
-			switch ptr, err := d.dec.ReadPointer(); {
+			switch ptr, err := mtv.modec.ReadPointer(); {
 			case err != nil:
 				return err
 			case ptr == 0 && isNullable:
@@ -338,15 +329,15 @@
 			case ptr == 0 && !isNullable:
 				return fmt.Errorf("invalid null union pointer")
 			}
-			if err := d.dec.StartNestedUnion(); err != nil {
+			if err := mtv.modec.StartNestedUnion(); err != nil {
 				return err
 			}
 		}
-		if err := d.decodeValue(fld.Type, vField, false, false); err != nil {
+		if err := mtv.transcodeValue(fld.Type, vField, false, false); err != nil {
 			return err
 		}
 		if fld.Type.Kind() == vdl.Union {
-			if err := d.dec.Finish(); err != nil {
+			if err := mtv.modec.Finish(); err != nil {
 				return err
 			}
 		}
@@ -356,10 +347,10 @@
 		if err := target.FinishFields(targetFields); err != nil {
 			return err
 		}
-		d.dec.FinishReadingUnionValue()
+		mtv.modec.FinishReadingUnionValue()
 		return nil
 	case vdl.Optional:
-		return d.decodeValue(vt.Elem(), target, false, true)
+		return mtv.transcodeValue(vt.Elem(), target, false, true)
 	case vdl.Any:
 		panic("unimplemented")
 	//case vdl.TypeObject:
diff --git a/go/src/v.io/x/mojo/transcoder/testdata_test.go b/go/src/v.io/x/mojo/transcoder/testdata_test.go
new file mode 100644
index 0000000..fbc4173
--- /dev/null
+++ b/go/src/v.io/x/mojo/transcoder/testdata_test.go
@@ -0,0 +1,190 @@
+package transcoder_test
+
+import (
+	"mojo/public/interfaces/bindings/tests/rect"
+	"mojo/public/interfaces/bindings/tests/test_structs"
+)
+
+type transcodeTestCase struct {
+	Name      string
+	MojoValue interface{}
+	VdlValue  interface{}
+}
+
+// Test cases for the mojom <-> vdl transcoder tests. See transcoder_test.go
+var testCases = []transcodeTestCase{
+	// from Mojo's rect
+	{
+		Name: "Rect",
+		MojoValue: &rect.Rect{
+			X:      11,
+			Y:      12,
+			Height: 13,
+			Width:  14,
+		},
+		VdlValue: rect.Rect{
+			X:      11,
+			Y:      12,
+			Height: 13,
+			Width:  14,
+		},
+	},
+	// from Mojo's test_structs
+	{
+		Name: "NamedRegion",
+		MojoValue: &test_structs.NamedRegion{
+			Name: stringPtr("A"),
+			Rects: &[]rect.Rect{
+				rect.Rect{},
+			},
+		},
+		VdlValue: test_structs.NamedRegion{
+			Name: stringPtr("A"),
+			Rects: &[]rect.Rect{
+				rect.Rect{},
+			},
+		},
+	},
+	{
+		Name: "RectPair",
+		MojoValue: &test_structs.RectPair{
+			First:  &rect.Rect{X: 0, Y: 1, Height: 2, Width: 3},
+			Second: &rect.Rect{X: 11, Y: 12, Height: 13, Width: 14},
+		},
+		VdlValue: test_structs.RectPair{
+			First:  &rect.Rect{X: 0, Y: 1, Height: 2, Width: 3},
+			Second: &rect.Rect{X: 11, Y: 12, Height: 13, Width: 14},
+		},
+	},
+	{
+		Name:      "EmptyStruct",
+		MojoValue: &test_structs.EmptyStruct{},
+		VdlValue:  test_structs.EmptyStruct{},
+	},
+	// TODO(bprosnitz) HandleStruct?
+	// TODO(bprosnitz) NullableHandleStruct?
+	// TODO(bprosnitz) NoDefaultFieldValues?
+	// TODO(bprosnitz) DefaultFieldValues?
+	{
+		Name: "ScopedConstants",
+		MojoValue: &test_structs.ScopedConstants{
+			test_structs.ScopedConstants_EType_E0,
+			test_structs.ScopedConstants_EType_E1,
+			test_structs.ScopedConstants_EType_E2,
+			test_structs.ScopedConstants_EType_E3,
+			test_structs.ScopedConstants_EType_E4,
+			10,
+			10,
+		},
+		VdlValue: test_structs.ScopedConstants{
+			test_structs.ScopedConstants_EType_E0,
+			test_structs.ScopedConstants_EType_E1,
+			test_structs.ScopedConstants_EType_E2,
+			test_structs.ScopedConstants_EType_E3,
+			test_structs.ScopedConstants_EType_E4,
+			10,
+			10,
+		},
+	},
+	// TODO(bprosnitz) MapKeyTypes?
+	// TODO(bprosnitz) MapValueTypes?
+	// TODO(bprosnitz) ArrayValueTypes?
+	{
+		Name: "UnsignedArrayValueTypes",
+		MojoValue: &test_structs.UnsignedArrayValueTypes{
+			[]uint8{1}, []uint16{2}, []uint32{3}, []uint64{4}, []float32{5}, []float64{6},
+		},
+		VdlValue: test_structs.UnsignedArrayValueTypes{
+			[]uint8{1}, []uint16{2}, []uint32{3}, []uint64{4}, []float32{5}, []float64{6},
+		},
+	},
+	{
+		Name: "UnsignedFixedArrayValueTypes",
+		MojoValue: &test_structs.UnsignedFixedArrayValueTypes{
+			[3]uint8{1}, [2]uint16{2}, [2]uint32{3}, [2]uint64{4}, [2]float32{5}, [2]float64{6},
+		},
+		VdlValue: test_structs.UnsignedFixedArrayValueTypes{
+			[3]uint8{1}, [2]uint16{2}, [2]uint32{3}, [2]uint64{4}, [2]float32{5}, [2]float64{6},
+		},
+	},
+	{
+		Name: "BoolArrayValueTypes",
+		MojoValue: &test_structs.BoolArrayValueTypes{
+			[]bool{false, true, true, false},
+		},
+		VdlValue: test_structs.BoolArrayValueTypes{
+			[]bool{false, true, true, false},
+		},
+	},
+	{
+		Name: "FloatNumberValues",
+		MojoValue: &test_structs.FloatNumberValues{
+			0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9,
+		},
+		VdlValue: test_structs.FloatNumberValues{
+			0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9,
+		},
+	},
+	// TODO(bprosnitz) IntegerNumberValues?
+	{
+		Name: "UnsignedNumberValues",
+		MojoValue: &test_structs.UnsignedNumberValues{
+			0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+		},
+		VdlValue: test_structs.UnsignedNumberValues{
+			0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+		},
+	},
+	{
+		Name: "BitArrayValues",
+		MojoValue: &test_structs.BitArrayValues{
+			[1]bool{true}, [7]bool{true, false, true}, [9]bool{true, false, true}, []bool{true, false, true},
+			[][]bool{[]bool{true, false, true}}, []*[]bool{&[]bool{true, false, true}}, []*[2]bool{&[2]bool{true, false}},
+		},
+		VdlValue: test_structs.BitArrayValues{
+			[1]bool{true}, [7]bool{true, false, true}, [9]bool{true, false, true}, []bool{true, false, true},
+			[][]bool{[]bool{true, false, true}}, []*[]bool{&[]bool{true, false, true}}, []*[2]bool{&[2]bool{true, false}},
+		},
+	},
+	// TODO(bprosnitz) MultiVersionStruct? + other versions
+	// from Mojo's test_unions
+	// TODO(bprosnitz) PodUnion?
+	// TODO(bprosnitz) ObjectUnion?
+	// TODO(bprosnitz) HandleUnion?
+	// TODO(bprosnitz) WrapperStruct?
+	// TODO(bprosnitz) DummyStruct?
+	// TODO(bprosnitz) SmallStruct?
+	// TODO(bprosnitz) SmallStructNonNullableUnion?
+	// TODO(bprosnitz) SmallObjStruct?
+	// TODO(bprosnitz) TryNonNullStruct?
+	// TODO(bprosnitz) OldUnion?
+	// TODO(bprosnitz) NewUnion?
+	// TODO(bprosnitz) IncludingStruct?
+	// test cases not from Mojo:
+	/*
+		// TODO(bprosnitz) These fail:
+		{
+			Name:      "UnnamedPrimitiveTestStruct",
+			MojoValue: &transcoder_testcases.UnnamedPrimitiveTestStruct{1, "A", true, 2},
+			VdlValue:  transcoder_testcases.UnnamedPrimitiveTestStruct{1, "A", true, 2},
+		},
+		{
+			Name:      "Transcode to Named Primitives",
+			MojoValue: &transcoder_testcases.UnnamedPrimitiveTestStruct{1, "A", true, 2},
+			VdlValue:  NamedPrimitiveTestStruct{1, "A", true, 2},
+		},*/
+	// TODO(bprosnitz) More tests of errors, named type conversions, unsupported types, etc
+}
+
+func stringPtr(in string) *string { return &in }
+
+type NUint32 uint32
+type NString string
+type NBool bool
+type NFloat32 float32
+type NamedPrimitiveTestStruct struct {
+	A NUint32
+	B NString
+	C NBool
+	D NFloat32
+}
diff --git a/go/src/v.io/x/mojo/transcoder/transcoder_test.go b/go/src/v.io/x/mojo/transcoder/transcoder_test.go
new file mode 100644
index 0000000..1e5d7bb
--- /dev/null
+++ b/go/src/v.io/x/mojo/transcoder/transcoder_test.go
@@ -0,0 +1,77 @@
+package transcoder_test
+
+import (
+	"reflect"
+	"testing"
+
+	"mojo/public/go/bindings"
+
+	"fmt"
+
+	"bytes"
+
+	"v.io/v23/vdl"
+	"v.io/x/mojo/transcoder"
+)
+
+func TestMojoToVom(t *testing.T) {
+	for _, test := range testCases {
+		testName := test.Name + " mojo->vom"
+
+		data, err := computeExpectedMojomBytes(test.MojoValue)
+		if err != nil {
+			t.Errorf("%s: %v", testName, err)
+			continue
+		}
+
+		var out interface{}
+		if err := transcoder.MojomToVdl(data, vdl.TypeOf(test.VdlValue), &out); err != nil {
+			t.Errorf("%s: error in MojoToVom: %v (was transcoding from %x)", testName, err, data)
+			continue
+		}
+
+		if got, want := out, test.VdlValue; !reflect.DeepEqual(got, want) {
+			t.Errorf("%s: result doesn't match expectation. got %#v, but want %#v", testName, got, want)
+		}
+	}
+}
+
+func TestVomToMojo(t *testing.T) {
+	for _, test := range testCases {
+		testName := test.Name + " vom->mojo"
+
+		data, err := transcoder.VdlToMojom(test.VdlValue)
+		if err != nil {
+			t.Errorf("%s: error in VomToMojo: %v", testName, err)
+			continue
+		}
+
+		expectedData, err := computeExpectedMojomBytes(test.MojoValue)
+		if err != nil {
+			t.Errorf("%s: %v", testName, err)
+			continue
+		}
+
+		if got, want := data, expectedData; !bytes.Equal(got, want) {
+			t.Errorf("%s: got %x, but want %x", testName, got, want)
+		}
+	}
+}
+
+func computeExpectedMojomBytes(mojoValue interface{}) ([]byte, error) {
+	payload, ok := mojoValue.(bindings.Payload)
+	if !ok {
+		return nil, fmt.Errorf("type %T lacks an Encode() method", mojoValue)
+	}
+
+	enc := bindings.NewEncoder()
+	err := payload.Encode(enc)
+	if err != nil {
+		return nil, fmt.Errorf("error in Encode: %v", err)
+	}
+	data, _, err := enc.Data()
+	if err != nil {
+		return nil, fmt.Errorf("error in Data()", err)
+	}
+	return data, nil
+}
diff --git a/go/src/v.io/x/mojo/transcoder/vdl_to_mojom.go b/go/src/v.io/x/mojo/transcoder/vdl_to_mojom.go
new file mode 100644
index 0000000..00945d8
--- /dev/null
+++ b/go/src/v.io/x/mojo/transcoder/vdl_to_mojom.go
@@ -0,0 +1,94 @@
+package transcoder
+
+import (
+	"reflect"
+
+	"v.io/v23/vdl"
+)
+
+func VdlToMojom(value interface{}) ([]byte, error) {
+	vtm := &vdlToMojomTranscoder{
+		allocator: &allocator{},
+	}
+	err := vdl.FromReflect(vtm, reflect.ValueOf(value))
+	return vtm.Bytes(), err
+}
+
+type vdlToMojomTranscoder struct {
+	allocator *allocator
+}
+
+func (vtm *vdlToMojomTranscoder) Bytes() []byte {
+	return vtm.allocator.AllocatedBytes()
+}
+
+func (vtm *vdlToMojomTranscoder) FromBool(src bool, tt *vdl.Type) error {
+	panic("cannot encode top level bool")
+}
+func (vtm *vdlToMojomTranscoder) FromUint(src uint64, tt *vdl.Type) error {
+	panic("cannot encode top level uint")
+}
+func (vtm *vdlToMojomTranscoder) FromInt(src int64, tt *vdl.Type) error {
+	panic("cannot encode top level int")
+}
+func (vtm *vdlToMojomTranscoder) FromFloat(src float64, tt *vdl.Type) error {
+	panic("cannot encode top level float")
+}
+func (vtm *vdlToMojomTranscoder) FromComplex(src complex128, tt *vdl.Type) error {
+	panic("cannot encode top level complex")
+}
+func (vtm *vdlToMojomTranscoder) FromBytes(src []byte, tt *vdl.Type) error {
+	panic("cannot encode top level bytes")
+}
+func (vtm *vdlToMojomTranscoder) FromString(src string, tt *vdl.Type) error {
+	panic("cannot encode top level string")
+}
+func (vtm *vdlToMojomTranscoder) FromEnumLabel(src string, tt *vdl.Type) error {
+	panic("cannot encode top level enum")
+}
+func (vtm *vdlToMojomTranscoder) FromTypeObject(src *vdl.Type) error {
+	panic("cannot encode top level type object")
+}
+func (vtm *vdlToMojomTranscoder) FromNil(tt *vdl.Type) error {
+	panic("cannot encode top level nil")
+}
+
+func (vtm *vdlToMojomTranscoder) StartList(tt *vdl.Type, len int) (vdl.ListTarget, error) {
+	panic("UNIMPLEMENTED")
+	return nil, nil
+}
+func (vtm *vdlToMojomTranscoder) FinishList(x vdl.ListTarget) error {
+	return nil
+}
+func (vtm *vdlToMojomTranscoder) StartSet(tt *vdl.Type, len int) (vdl.SetTarget, error) {
+	panic("UNIMPLEMENTED")
+}
+func (vtm *vdlToMojomTranscoder) FinishSet(x vdl.SetTarget) error {
+	panic("UNIMPLEMENTED")
+
+}
+func (vtm *vdlToMojomTranscoder) StartMap(tt *vdl.Type, len int) (vdl.MapTarget, error) {
+	panic("UNIMPLEMENTED")
+
+}
+func (vtm *vdlToMojomTranscoder) FinishMap(x vdl.MapTarget) error {
+	panic("UNIMPLEMENTED")
+
+}
+func (vtm *vdlToMojomTranscoder) StartFields(tt *vdl.Type) (vdl.FieldsTarget, error) {
+	if tt.Kind() == vdl.Union {
+		panic("not yet supported")
+	}
+	if tt.Kind() == vdl.Optional {
+		tt = tt.Elem()
+	}
+	block := vtm.allocator.Allocate(neededStructAllocationSize(tt), 0)
+	return fieldsTarget{
+			vdlType: tt,
+			block:   block,
+		},
+		nil
+}
+func (vtm *vdlToMojomTranscoder) FinishFields(x vdl.FieldsTarget) error {
+	return nil
+}
diff --git a/mojom/mojom/tests/transcoder_testcases.mojom b/mojom/mojom/tests/transcoder_testcases.mojom
new file mode 100644
index 0000000..61ae4f8
--- /dev/null
+++ b/mojom/mojom/tests/transcoder_testcases.mojom
@@ -0,0 +1,12 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+module tests;
+
+struct UnnamedPrimitiveTestStruct {
+    uint32 A;
+    string B;
+    bool C;
+    float D;
+};
\ No newline at end of file