TBR: ether: Implement table.Scan.

MultiPart: 2/2
Change-Id: I6a4d3e54810407d8c21d590116da1f545360c559
diff --git a/Makefile b/Makefile
index ce984f4..af463ae 100644
--- a/Makefile
+++ b/Makefile
@@ -58,7 +58,7 @@
 
 	THIRD_PARTY_LIBS := $(V23_ROOT)/third_party/cout/linux_amd64
 
-	V23_MOJO_FLAGS += --root-dir=/tmp/syncbase_data
+	V23_MOJO_FLAGS += --root-dir=/tmp/syncbase_data --alsologtostderr=false
 endif
 
 GOPATH := $(V23_GOPATH):$(MOJO_DIR):$(MOJO_DIR)/third_party/go:$(MOJO_BUILD_DIR)/gen/go:$(PWD)/go:$(PWD)/gen/go
@@ -166,8 +166,8 @@
 dartanalyzer: dart/packages gen-mojom
 	# TODO(nlacasse): Fix dart mojom binding generator so it does not produce
 	# files that violate dartanalyzer.  For now, we use "grep -v" to hide all
-	# warnings from *.mojom.dart files.
-	cd dart && dartanalyzer bin/*.dart lib/*.dart test/*.dart | grep -v '\.mojom\.dart'
+	# hints from *.mojom.dart files.
+	cd dart && dartanalyzer bin/*.dart lib/*.dart test/*.dart | grep -v '\[hint\].*\.mojom\.dart'
 
 # Installs dart dependencies.
 dart/packages: dart/pubspec.yaml
@@ -185,9 +185,6 @@
 run-sky-echo: $(ETHER_BUILD_DIR)/echo_server.mojo dart/packages dart/lib/gen/dart-pkg/mojom/lib/mojo/echo.mojom.dart | env-check
 	$(MOJO_DIR)/src/mojo/devtools/common/mojo_run --config-file $(PWD)/sky_echo/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) 'mojo:window_manager https://mojo.v.io/sky_echo/lib/main.dart'
 
-# TODO(nlacasse): The tests are currently flaky due to
-# https://github.com/domokit/mojo/issues/387.  If you see errors about
-# "!consumer_in_two_phase_read()", ignore and re-run the tests.
 .PHONY: test
 test: dart/packages $(ETHER_BUILD_DIR)/echo_server.mojo $(ETHER_BUILD_DIR)/syncbase_server.mojo gen-mojom | env-check
 	$(MOJO_DIR)/src/mojo/devtools/common/mojo_test --config-file $(PWD)/mojoconfig $(MOJO_SHELL_FLAGS) $(MOJO_ANDROID_FLAGS) --shell-path $(MOJO_SHELL_PATH) tests
@@ -222,9 +219,9 @@
 .PHONY: clean
 clean:
 	rm -rf gen/mojo gen/go
+	rm -rf dart/lib/gen
 
 .PHONY: veryclean
 veryclean: clean
 	rm -rf gen
-	rm -rf dart/lib/gen
 	rm -rf dart/{.packages,pubspec.lock,packages}
diff --git a/dart/lib/src/nosql/database.dart b/dart/lib/src/nosql/database.dart
index adf2bc8..3acee57 100644
--- a/dart/lib/src/nosql/database.dart
+++ b/dart/lib/src/nosql/database.dart
@@ -101,7 +101,7 @@
     sc.add(result);
   }
 
-  onDone(Error err) {
+  onDone(mojom.Error err) {
     if (isError(err)) {
       sc.addError(err);
     }
diff --git a/dart/lib/src/nosql/table.dart b/dart/lib/src/nosql/table.dart
index 5bdbd0f..19604f7 100644
--- a/dart/lib/src/nosql/table.dart
+++ b/dart/lib/src/nosql/table.dart
@@ -34,10 +34,12 @@
 
   Stream<mojom.KeyValue> scan(List<int> start, List<int> limit) {
     StreamController<mojom.KeyValue> sc = new StreamController();
-    mojom.ScanStream scanStream = new ScanStreamImpl._fromStreamController(sc);
+
+    mojom.ScanStreamStub stub = new mojom.ScanStreamStub.unbound();
+    stub.impl = new ScanStreamImpl._fromStreamController(sc);
 
     // Call tableScan asynchronously.
-    _proxy.ptr.tableScan(fullName, start, limit, scanStream).then((v) {
+    _proxy.ptr.tableScan(fullName, start, limit, stub).then((v) {
       // TODO(nlacasse): Is throwing the correct behavior here?  Consider
       // returning a tuple (Stream<mojom.KeyValue>, Future) and resolve the
       // Future at the end of the RPC (with an error if applicable).  Then
@@ -78,7 +80,7 @@
     sc.add(keyValue);
   }
 
-  onDone(Error err) {
+  onDone(mojom.Error err) {
     if (isError(err)) {
       sc.addError(err);
     }
diff --git a/dart/test/syncbase_table_test.dart b/dart/test/syncbase_table_test.dart
index e0bfd46..7aea3fe 100644
--- a/dart/test/syncbase_table_test.dart
+++ b/dart/test/syncbase_table_test.dart
@@ -1,5 +1,7 @@
 library syncbase_table_test;
 
+import 'dart:convert' show UTF8;
+
 import 'package:test/test.dart';
 
 import 'package:ether/syncbase_client.dart' show SyncbaseClient;
@@ -30,4 +32,32 @@
     await table.delete();
     expect(await table.exists(), equals(false));
   });
+
+  test('scanning rows', () async {
+    var app = c.app(utils.uniqueName('app'));
+    await app.create(utils.emptyPerms());
+    var db = app.noSqlDatabase(utils.uniqueName('db'));
+    await db.create(utils.emptyPerms());
+    var table = db.table(utils.uniqueName('table'));
+    await table.create(utils.emptyPerms());
+
+    // Put some rows.
+    var rowNames = [utils.uniqueName('rowA'), utils.uniqueName('rowB')];
+
+    for (var rowName in rowNames) {
+      var row = table.row(rowName);
+      await row.put(UTF8.encode(utils.uniqueName('value')));
+    }
+
+    // Scan!
+    var stream = table.scan(UTF8.encode(''), UTF8.encode('z'));
+
+    var gotRows = await stream.toList();
+    expect(gotRows, hasLength(rowNames.length));
+    expect(gotRows[0].key, equals(rowNames[0]));
+    expect(gotRows[1].key, equals(rowNames[1]));
+
+    // TODO(nlacasse): Write tests that check that 'start' and 'limit' are
+    // working properly.
+  });
 }
diff --git a/mojom/syncbase.mojom b/mojom/syncbase.mojom
index 7af3c49..00a22c5 100644
--- a/mojom/syncbase.mojom
+++ b/mojom/syncbase.mojom
@@ -85,7 +85,7 @@
   DbCreate(string name, Perms perms) => (Error err);
   DbDelete(string name) => (Error err);
   DbExists(string name) => (Error err, bool exists);
-  DbExec(string name, string query, ExecStream& stream) => (Error err);
+  DbExec(string name, string query, ExecStream stream) => (Error err);
   DbBeginBatch(string name, BatchOptions? bo) => (Error err, string batch_dn);
   DbCommit(string name) => (Error err);
   DbAbort(string name) => (Error err);
@@ -126,7 +126,7 @@
   TableDeleteRowRange(string name, array<uint8> start, array<uint8> limit)
       => (Error err);
   TableScan(
-      string name, array<uint8> start, array<uint8> limit, ScanStream& stream)
+      string name, array<uint8> start, array<uint8> limit, ScanStream stream)
       => (Error err);
   TableGetPermissions(string name, string key)
       => (Error err, array<PrefixPerms> perms_arr);