[fidl][go] Regression test for AddEthernetDevice req

... and a fix: we were not properly re-positioning the head after
encoding/decoding a struct.

Change-Id: I6bcf88572373f1e134a50a2fd1a767378a039aaa
diff --git a/src/syscall/zx/fidl/bindingstest/impl.go b/src/syscall/zx/fidl/bindingstest/impl.go
index 6ef9ec9..fe7ba71 100644
--- a/src/syscall/zx/fidl/bindingstest/impl.go
+++ b/src/syscall/zx/fidl/bindingstest/impl.go
@@ -839,6 +839,115 @@
 	return 24
 }
 
+type InterfaceConfig struct {
+	_               struct{} `fidl2:"s,48,8"`
+	Name            string
+	IpAddressConfig IpAddressConfig
+}
+
+var _mInterfaceConfig = _bindings.CreateLazyMarshaler(InterfaceConfig{})
+
+func (msg *InterfaceConfig) Marshaler() _bindings.Marshaler {
+	return _mInterfaceConfig
+}
+
+// Implements Payload.
+func (_ *InterfaceConfig) InlineAlignment() int {
+	return 8
+}
+
+// Implements Payload.
+func (_ *InterfaceConfig) InlineSize() int {
+	return 48
+}
+
+type Subnet struct {
+	_         struct{} `fidl2:"s,24,4"`
+	Addr      IpAddress
+	PrefixLen uint8
+}
+
+var _mSubnet = _bindings.CreateLazyMarshaler(Subnet{})
+
+func (msg *Subnet) Marshaler() _bindings.Marshaler {
+	return _mSubnet
+}
+
+// Implements Payload.
+func (_ *Subnet) InlineAlignment() int {
+	return 4
+}
+
+// Implements Payload.
+func (_ *Subnet) InlineSize() int {
+	return 24
+}
+
+type IPv4Address struct {
+	_    struct{} `fidl2:"s,4,1"`
+	Addr [4]uint8
+}
+
+var _mIPv4Address = _bindings.CreateLazyMarshaler(IPv4Address{})
+
+func (msg *IPv4Address) Marshaler() _bindings.Marshaler {
+	return _mIPv4Address
+}
+
+// Implements Payload.
+func (_ *IPv4Address) InlineAlignment() int {
+	return 1
+}
+
+// Implements Payload.
+func (_ *IPv4Address) InlineSize() int {
+	return 4
+}
+
+type IPv6Address struct {
+	_    struct{} `fidl2:"s,16,1"`
+	Addr [16]uint8
+}
+
+var _mIPv6Address = _bindings.CreateLazyMarshaler(IPv6Address{})
+
+func (msg *IPv6Address) Marshaler() _bindings.Marshaler {
+	return _mIPv6Address
+}
+
+// Implements Payload.
+func (_ *IPv6Address) InlineAlignment() int {
+	return 1
+}
+
+// Implements Payload.
+func (_ *IPv6Address) InlineSize() int {
+	return 16
+}
+
+type TestAddEthernetDeviceRequest struct {
+	_               struct{} `fidl2:"s,72,8"`
+	TopologicalPath string
+	Config          InterfaceConfig
+	Device          EthernetDeviceInterface
+}
+
+var _mTestAddEthernetDeviceRequest = _bindings.CreateLazyMarshaler(TestAddEthernetDeviceRequest{})
+
+func (msg *TestAddEthernetDeviceRequest) Marshaler() _bindings.Marshaler {
+	return _mTestAddEthernetDeviceRequest
+}
+
+// Implements Payload.
+func (_ *TestAddEthernetDeviceRequest) InlineAlignment() int {
+	return 8
+}
+
+// Implements Payload.
+func (_ *TestAddEthernetDeviceRequest) InlineSize() int {
+	return 72
+}
+
 type Union1Tag uint32
 
 const (
@@ -950,6 +1059,82 @@
 	u.Str = str
 }
 
+type IpAddressConfigTag uint32
+
+const (
+	_ IpAddressConfigTag = iota
+	IpAddressConfigStaticIp
+	IpAddressConfigDhcp
+)
+
+type IpAddressConfig struct {
+	IpAddressConfigTag `fidl:"tag" fidl2:"u,28,4"`
+	StaticIp           Subnet
+	Dhcp               bool
+}
+
+// Implements Payload.
+func (_ *IpAddressConfig) InlineAlignment() int {
+	return 4
+}
+
+// Implements Payload.
+func (_ *IpAddressConfig) InlineSize() int {
+	return 28
+}
+
+func (u *IpAddressConfig) Which() IpAddressConfigTag {
+	return u.IpAddressConfigTag
+}
+
+func (u *IpAddressConfig) SetStaticIp(staticIp Subnet) {
+	u.IpAddressConfigTag = IpAddressConfigStaticIp
+	u.StaticIp = staticIp
+}
+
+func (u *IpAddressConfig) SetDhcp(dhcp bool) {
+	u.IpAddressConfigTag = IpAddressConfigDhcp
+	u.Dhcp = dhcp
+}
+
+type IpAddressTag uint32
+
+const (
+	_ IpAddressTag = iota
+	IpAddressIpv4
+	IpAddressIpv6
+)
+
+type IpAddress struct {
+	IpAddressTag `fidl:"tag" fidl2:"u,20,4"`
+	Ipv4         IPv4Address
+	Ipv6         IPv6Address
+}
+
+// Implements Payload.
+func (_ *IpAddress) InlineAlignment() int {
+	return 4
+}
+
+// Implements Payload.
+func (_ *IpAddress) InlineSize() int {
+	return 20
+}
+
+func (u *IpAddress) Which() IpAddressTag {
+	return u.IpAddressTag
+}
+
+func (u *IpAddress) SetIpv4(ipv4 IPv4Address) {
+	u.IpAddressTag = IpAddressIpv4
+	u.Ipv4 = ipv4
+}
+
+func (u *IpAddress) SetIpv6(ipv6 IPv6Address) {
+	u.IpAddressTag = IpAddressIpv6
+	u.Ipv6 = ipv6
+}
+
 type SampleXUnionTag uint32
 
 const (
@@ -1436,3 +1621,46 @@
 	}
 	return ((*_bindings.ChannelProxy)(p)).Send(Test1SurpriseOrdinal, event_)
 }
+
+type EthernetDeviceInterface _bindings.ChannelProxy
+
+type EthernetDevice interface {
+}
+
+type EthernetDeviceTransitionalBase struct{}
+
+type EthernetDeviceInterfaceRequest _bindings.InterfaceRequest
+
+func NewEthernetDeviceInterfaceRequest() (EthernetDeviceInterfaceRequest, *EthernetDeviceInterface, error) {
+	req, cli, err := _bindings.NewInterfaceRequest()
+	return EthernetDeviceInterfaceRequest(req), (*EthernetDeviceInterface)(cli), err
+}
+
+type EthernetDeviceStub struct {
+	Impl EthernetDevice
+}
+
+func (s *EthernetDeviceStub) Dispatch(ord uint32, b_ []byte, h_ []_zx.Handle) (_bindings.Payload, error) {
+	return s.DispatchNew(ord, b_, h_)
+}
+
+func (s *EthernetDeviceStub) DispatchNew(ord uint32, b_ []byte, h_ []_zx.Handle) (_bindings.Message, error) {
+	switch ord {
+	}
+	return nil, _bindings.ErrUnknownOrdinal
+}
+
+type EthernetDeviceService struct {
+	_bindings.BindingSet
+}
+
+func (s *EthernetDeviceService) Add(impl EthernetDevice, c _zx.Channel, onError func(error)) (_bindings.BindingKey, error) {
+	return s.BindingSet.Add(&EthernetDeviceStub{Impl: impl}, c, onError)
+}
+
+func (s *EthernetDeviceService) EventProxyFor(key _bindings.BindingKey) (*EthernetDeviceEventProxy, bool) {
+	pxy, err := s.BindingSet.ProxyFor(key)
+	return (*EthernetDeviceEventProxy)(pxy), err
+}
+
+type EthernetDeviceEventProxy _bindings.ChannelProxy
diff --git a/src/syscall/zx/fidl/bindingstest/test.fidl b/src/syscall/zx/fidl/bindingstest/test.fidl
index 2162cd9..6e4aea7 100644
--- a/src/syscall/zx/fidl/bindingstest/test.fidl
+++ b/src/syscall/zx/fidl/bindingstest/test.fidl
@@ -265,3 +265,21 @@
     vector<uint8>:kMaxBuf data;
     uint64 offset;
 };
+
+protocol EthernetDevice {};
+
+struct InterfaceConfig {
+    string name;
+    IpAddressConfig ip_address_config;
+};
+
+union IpAddressConfig {
+    array<uint32>:6 padding_size_24_align_4;
+    bool dhcp;
+};
+
+struct TestAddEthernetDeviceRequest {
+    string topological_path;
+    InterfaceConfig config;
+    EthernetDevice device;
+};
diff --git a/src/syscall/zx/fidl/encoding_new.go b/src/syscall/zx/fidl/encoding_new.go
index 775a6a8..a40d219 100644
--- a/src/syscall/zx/fidl/encoding_new.go
+++ b/src/syscall/zx/fidl/encoding_new.go
@@ -449,6 +449,7 @@
 			return err
 		}
 	}
+	out.head = align(out.head, m.alignment)
 	return nil
 }
 
@@ -458,6 +459,7 @@
 			return err
 		}
 	}
+	in.head = align(in.head, m.alignment)
 	return nil
 }
 
diff --git a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
index adb6fe2..1f3c0e1 100644
--- a/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
+++ b/src/syscall/zx/fidl/fidl_test/encoding_new_test.go
@@ -7,6 +7,7 @@
 package fidl_test
 
 import (
+	"bytes"
 	"fmt"
 	"math/rand"
 	"reflect"
@@ -312,6 +313,15 @@
 
 func (c checker) check(t *testing.T, input Message, expectSize int) {
 	t.Helper()
+	defer func() {
+		if r := recover(); r != nil {
+			// When running tests on device, this bubbles up the error
+			// on the console launching the tests, rather than having
+			// to look at the device's kernel logs.
+			t.Fatalf("panic: %s", r)
+			panic(r)
+		}
+	}()
 	var respb [zx.ChannelMaxMessageBytes]byte
 	var resph [zx.ChannelMaxMessageHandles]zx.Handle
 	nb, nh, err := c.marshalFunc.fn(input, respb[:], resph[:])
@@ -359,9 +369,10 @@
 // successCase represents a golden test for a success case, where encoding
 // and decoding should succceed.
 type successCase struct {
-	name  string
-	input Message
-	bytes []byte
+	name    string
+	input   Message
+	bytes   []byte
+	handles []zx.Handle
 }
 
 func (ex successCase) check(t *testing.T) {
@@ -370,6 +381,9 @@
 			t.Run(ex.name+"_"+mFn.name+"_"+uFn.name, func(t *testing.T) {
 				defer func() {
 					if r := recover(); r != nil {
+						// When running tests on device, this bubbles up the error
+						// on the console launching the tests, rather than having
+						// to look at the device's kernel logs.
 						t.Fatalf("panic: %s", r)
 						panic(r)
 					}
@@ -380,11 +394,17 @@
 				if err != nil {
 					t.Fatal(err)
 				}
-				if nh != 0 {
-					t.Fatalf("no handles expected, got %d", nh)
+				if !bytes.Equal(ex.bytes, respb[:nb]) {
+					t.Fatalf("expected %x, got %x", ex.bytes, respb[:nb])
 				}
-				if !reflect.DeepEqual(ex.bytes, respb[:nb]) {
-					t.Fatalf("expected %v, got %v", ex.bytes, respb[:nb])
+				if len(ex.handles) == 0 {
+					if nh != 0 {
+						t.Fatalf("no handles expected, got %d", nh)
+					}
+				} else {
+					if !reflect.DeepEqual(ex.handles, resph[:nh]) {
+						t.Fatalf("expected %v, got %v", ex.handles, resph[:nh])
+					}
 				}
 				output := makeDefault(ex.input)
 				if err := uFn.fn(respb[:nb], resph[:nh], output); err != nil {
@@ -797,6 +817,40 @@
 			'a', 'f', 't', 'e', 'r', 0x00, 0x00, 0x00, // "after"
 		},
 	}.check(t)
+
+	var ipAddressConfig IpAddressConfig
+	ipAddressConfig.SetDhcp(true)
+	successCase{
+		name: "add-ethernet-device-request",
+		input: &TestAddEthernetDeviceRequest{
+			TopologicalPath: "@/dev/sys/pci/00:03.0/e1000/ethernet",
+			Config: InterfaceConfig{
+				Name:            "ethp0003",
+				IpAddressConfig: ipAddressConfig,
+			},
+			Device: EthernetDeviceInterface{zx.Channel(0xdeadbeef)},
+		},
+		bytes: []byte{
+			0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // topological_path
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // topological_path
+			0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // name
+			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // name
+			0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // subnet (dhcp variant)
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
+			0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // device (handle present)
+			0x40, 0x2f, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x79, // @/dev/sy
+			0x73, 0x2f, 0x70, 0x63, 0x69, 0x2f, 0x30, 0x30, // s/pci/00
+			0x3a, 0x30, 0x33, 0x2e, 0x30, 0x2f, 0x65, 0x31, // :03.0/e1
+			0x30, 0x30, 0x30, 0x2f, 0x65, 0x74, 0x68, 0x65, // 000/ethe
+			0x72, 0x6e, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, // rnet
+			0x65, 0x74, 0x68, 0x70, 0x30, 0x30, 0x30, 0x33, // ethp0003
+		},
+		handles: []zx.Handle{
+			zx.Handle(0xdeadbeef),
+		},
+	}.check(t)
 }
 
 func TestTableCompatibilityWithXY(t *testing.T) {