[fidl][dart] Porting one conformance tests

Starting to port conformance tests. Changing encoder so it can both be
used to encode messages (e.g. structs), and transactional messages
(which have a header prepended).

Test: fx shell runtests /pkgfs/packages/fidl_bindings_test/0/test
Change-Id: I6049504005b20d95d3e42f7684218ddd07383f3b
diff --git a/bin/fidl_bindings_test/fidl/BUILD.gn b/bin/fidl_bindings_test/fidl/BUILD.gn
index c69bc24..bf7364f 100644
--- a/bin/fidl_bindings_test/fidl/BUILD.gn
+++ b/bin/fidl_bindings_test/fidl/BUILD.gn
@@ -9,5 +9,6 @@
 
   sources = [
     "bindings_test.fidl",
+    "conformance.fidl",
   ]
 }
diff --git a/bin/fidl_bindings_test/fidl/conformance.fidl b/bin/fidl_bindings_test/fidl/conformance.fidl
new file mode 100644
index 0000000..36da153
--- /dev/null
+++ b/bin/fidl_bindings_test/fidl/conformance.fidl
@@ -0,0 +1,14 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+library fidl.examples.bindingstest;
+
+struct EmptyStruct {
+};
+
+struct TestEmptyStructSandwich {
+  string before;
+  EmptyStruct es;
+  string after;
+};
diff --git a/bin/fidl_bindings_test/test/test/roundtrip_test.dart b/bin/fidl_bindings_test/test/test/roundtrip_test.dart
new file mode 100644
index 0000000..2bca8e8
--- /dev/null
+++ b/bin/fidl_bindings_test/test/test/roundtrip_test.dart
@@ -0,0 +1,67 @@
+// Copyright 2019 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:typed_data';
+
+import 'package:test/test.dart';
+import 'package:fidl/fidl.dart' as $fidl;
+import 'package:fidl_fidl_examples_bindingstest/fidl_async.dart';
+
+class SuccessCase<T> {
+  SuccessCase(this.input, this.type, this.bytes);
+
+  final T input;
+  final $fidl.FidlType<T> type;
+  final Uint8List bytes;
+
+  static void run<T>(String name, T input, $fidl.FidlType<T> type, Uint8List bytes) {
+    group(name, () {
+      new SuccessCase(input, type, bytes)
+        .._checkEncode()
+        .._checkDecode();
+    });
+  }
+
+  void _checkEncode() {
+    test('encode', () {
+      final $fidl.Encoder $encoder = new $fidl.Encoder()
+        ..alloc(type.encodedSize);
+      type.encode($encoder, input, 0);
+      final message = $encoder.message;
+      expect(new Uint8List.view(message.data.buffer, 0, message.dataLength), equals(bytes));
+    });
+  }
+
+  void _checkDecode() {
+    test('decode', () {
+      final $fidl.Decoder $decoder = new $fidl.Decoder(
+        new $fidl.Message(new ByteData.view(bytes.buffer, 0, bytes.length), [], bytes.length, 0))
+          ..claimMemory(type.encodedSize);
+        final $actual = type.decode($decoder, 0);
+        expect($actual, equals(input));
+    });
+  }
+}
+
+void main() {
+  group('roundtrip', () {
+    SuccessCase.run('empty-struct-sandwich',
+      TestEmptyStructSandwich(
+      before: 'before', es: EmptyStruct(), after: 'after'),
+      kTestEmptyStructSandwich_Type,
+      Uint8List.fromList([
+        6, 0, 0, 0, 0, 0, 0, 0, // length of "before"
+        255, 255, 255, 255, 255, 255, 255, 255, // "before" is present
+        0,                   // empty struct zero field
+        0, 0, 0, 0, 0, 0, 0, // 7 bytes of padding after empty struct, to align to 64 bits
+        5, 0, 0, 0, 0, 0, 0, 0, // length of "after"
+        255, 255, 255, 255, 255, 255, 255, 255, // "after" is present
+        98, 101, 102, 111, 114, 101, // "before"
+        0, 0, // 2 bytes of padding after "before", to align to 64 bits
+        97, 102, 116, 101, 114, // "after" string
+        0, 0, 0, // 3 bytes of padding after "after", to align to 64 bits
+      ])
+    );
+  });
+}
diff --git a/bin/fidlgen_dart/backend/templates/interface.tmpl.go b/bin/fidlgen_dart/backend/templates/interface.tmpl.go
index 69e2ae9..40ff55a 100644
--- a/bin/fidlgen_dart/backend/templates/interface.tmpl.go
+++ b/bin/fidlgen_dart/backend/templates/interface.tmpl.go
@@ -146,7 +146,8 @@
       return;
     }
 
-    final $fidl.Encoder $encoder = new $fidl.Encoder({{ .OrdinalName }});
+    final $fidl.Encoder $encoder = new $fidl.Encoder();
+    $encoder.encodeMessageHeader({{ .OrdinalName }}, 0);
     {{- if .Request }}
     $encoder.alloc({{ .RequestSize }} - $fidl.kMessageHeaderSize);
     final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.request;
@@ -195,7 +196,8 @@
   {{- if not .HasRequest }}
     {{- if .HasResponse }}
   void {{ template "ResponseMethodSignature" . }} {
-    final $fidl.Encoder $encoder = new $fidl.Encoder({{ .OrdinalName }});
+    final $fidl.Encoder $encoder = new $fidl.Encoder();
+    $encoder.encodeMessageHeader({{ .OrdinalName }}, 0);
       {{- if .Response }}
     $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
     final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
@@ -226,7 +228,8 @@
     {{- if .HasResponse }}
   Function _{{ .Name }}Responder($fidl.MessageSink $respond, int $txid) {
     return ({{ template "Params" .Response }}) {
-      final $fidl.Encoder $encoder = new $fidl.Encoder({{ .OrdinalName }});
+      final $fidl.Encoder $encoder = new $fidl.Encoder();
+      $encoder.encodeMessageHeader({{ .OrdinalName }}, $txid);
       {{- if .Response }}
       $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
       final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
@@ -234,9 +237,7 @@
       {{- range $index, $response := .Response }}
       $types[{{ $index }}].encode($encoder, {{ .Name }}, 0);
       {{- end }}
-      $fidl.Message $message = $encoder.message;
-      $message.txid = $txid;
-      $respond($message);
+      $respond($encoder.message);
     };
   }
     {{- end }}
@@ -546,7 +547,8 @@
         return new Future.error(new $fidl.FidlStateException('The proxy is closed.'));
       }
 
-      final $fidl.Encoder $encoder = new $fidl.Encoder({{ .OrdinalName }});
+      final $fidl.Encoder $encoder = new $fidl.Encoder();
+      $encoder.encodeMessageHeader({{ .OrdinalName }}, 0);
       {{- if .Request }}
         $encoder.alloc({{ .RequestSize }} - $fidl.kMessageHeaderSize);
         final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.request;
@@ -591,7 +593,8 @@
         {{- if not .HasRequest }}
           if (impl.{{ .Name }} != null) {
             $subscriptions.add(impl.{{ .Name }}.listen(($response) {
-              final $fidl.Encoder $encoder = new $fidl.Encoder({{ .OrdinalName }});
+              final $fidl.Encoder $encoder = new $fidl.Encoder();
+              $encoder.encodeMessageHeader({{ .OrdinalName }}, 0);
               $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
               final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
               {{ template "EncodeResponse" . }}
@@ -624,15 +627,14 @@
 
               {{- if .HasResponse }}
                 $future.then(($response) {
-                  final $fidl.Encoder $encoder = new $fidl.Encoder({{ .OrdinalName }});
+                  final $fidl.Encoder $encoder = new $fidl.Encoder();
+                  $encoder.encodeMessageHeader({{ .OrdinalName }}, $message.txid);
                   {{- if .Response }}
                     $encoder.alloc({{ .ResponseSize }} - $fidl.kMessageHeaderSize);
                     final List<$fidl.MemberType> $types = {{ .TypeSymbol }}.response;
                     {{ template "EncodeResponse" . -}}
                   {{- end }}
-                  $fidl.Message $responseMessage = $encoder.message;
-                  $responseMessage.txid = $message.txid;
-                  $respond($responseMessage);
+                  $respond($encoder.message);
                 }, onError: (_e) {
                   close();
                   final String _name = {{ .TypeSymbol }}.name;
diff --git a/public/dart/fidl/lib/src/codec.dart b/public/dart/fidl/lib/src/codec.dart
index 096de5d..ce0d94d 100644
--- a/public/dart/fidl/lib/src/codec.dart
+++ b/public/dart/fidl/lib/src/codec.dart
@@ -29,10 +29,6 @@
 const int _kInitialBufferSize = 1024;
 
 class Encoder {
-  Encoder(int ordinal) {
-    _encodeMessageHeader(ordinal);
-  }
-
   Message get message {
     final ByteData trimmed = new ByteData.view(data.buffer, 0, _extent);
     return new Message(trimmed, _handles, _extent, _handles.length);
@@ -75,9 +71,10 @@
     _handles.add(value);
   }
 
-  void _encodeMessageHeader(int ordinal) {
+  void encodeMessageHeader(int ordinal, int txid) {
     alloc(kMessageHeaderSize);
     encodeUint32(ordinal, kMessageOrdinalOffset);
+    encodeUint32(txid, kMessageTxidOffset);
   }
 
   void encodeBool(bool value, int offset) {
diff --git a/public/dart/fidl/lib/src/message.dart b/public/dart/fidl/lib/src/message.dart
index 74526d9..744c836 100644
--- a/public/dart/fidl/lib/src/message.dart
+++ b/public/dart/fidl/lib/src/message.dart
@@ -31,8 +31,6 @@
       data.setUint32(kMessageTxidOffset, value, Endian.little);
 
   int get ordinal => data.getUint32(kMessageOrdinalOffset, Endian.little);
-  set ordinal(int value) =>
-      data.setUint32(kMessageOrdinalOffset, value, Endian.little);
 
   void hexDump() {
     const int width = 16;