[sledge] Add integration test.

This CL adds an integration test that obtains a Ledger connection via
the ledger_test_instance_provider binary.

TEST=sledge_integration_tests

Change-Id: I6792e044900e41f48623c4dcf1a0570a931eb349
diff --git a/packages/tests/all b/packages/tests/all
index f75874f..517da1f 100644
--- a/packages/tests/all
+++ b/packages/tests/all
@@ -19,6 +19,7 @@
         "topaz/packages/tests/modular_integration_tests",
         "topaz/packages/tests/repl",
         "topaz/packages/tests/scenic",
+        "topaz/packages/tests/sledge",
         "topaz/packages/tests/voila",
         "topaz/packages/tests/web_runner",
         "topaz/packages/tests/web_runner_smoke",
diff --git a/packages/tests/sledge b/packages/tests/sledge
new file mode 100644
index 0000000..fb8f4ff
--- /dev/null
+++ b/packages/tests/sledge
@@ -0,0 +1,8 @@
+{
+    "imports": [
+        "peridot/packages/tests/ledger"
+    ],
+    "packages": [
+        "//topaz/public/dart/sledge:sledge_integration_tests"
+    ]
+}
diff --git a/public/dart/sledge/BUILD.gn b/public/dart/sledge/BUILD.gn
index 3f953e8..c6babfb 100644
--- a/public/dart/sledge/BUILD.gn
+++ b/public/dart/sledge/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/dart/dart_fuchsia_test.gni")
 import("//build/dart/dart_library.gni")
 import("//topaz/runtime/dart/dart_test.gni")
 
@@ -111,9 +112,27 @@
     "values/set_value_test.dart",
   ]
 
+  source_dir = "test"
+
   deps = [
     ":sledge",
     "//third_party/dart-pkg/git/flutter/packages/flutter_test",
     "//third_party/dart-pkg/pub/test",
   ]
 }
+
+dart_fuchsia_test("sledge_integration_tests") {
+  meta = [
+    {
+      path = rebase_path("meta/sledge_integration_tests.cmx")
+      dest = "sledge_integration_tests.cmx"
+    },
+  ]
+
+  source_dir = "test/integration_tests"
+
+  deps = [
+    ":sledge",
+    "//third_party/dart-pkg/pub/test",
+  ]
+}
diff --git a/public/dart/sledge/README.md b/public/dart/sledge/README.md
index fd5fc30..003121c 100644
--- a/public/dart/sledge/README.md
+++ b/public/dart/sledge/README.md
@@ -106,7 +106,7 @@
 fx set x64 $FUCHSIA_DIR/out/release-x64 --args=is_debug=false
 ```
 
-### Device-side tests
+### Device-side tests (deprecated)
 
 These tests run on a fuchsia and exercise real Ledger instances.
 Require the `topaz/public/dart/sledge/sledge_testing_mod/package` package.
@@ -115,4 +115,19 @@
 # The following statement will add a module named "bar" in a newly created
 # story named "foo" and will run the sledge_testing_mod.
 sessionctl --story_name="foo" --mod_name="bar" --mod_url="sledge_testing_mod" add_mod
+```
+
+### Device-side integration tests (work in progress)
+
+To locally run these tests:
+
+```
+fx set x64 --product ermine --monolith topaz/packages/all --monolith topaz/packages/tests/all
+fx build
+fx shell "run fuchsia-pkg://fuchsia.com/sledge_integration_tests#meta/sledge_integration_tests.cmx"
+```
+
+The results of the tests are visible in the logs:
+```
+fx syslog
 ```
\ No newline at end of file
diff --git a/public/dart/sledge/lib/src/sledge.dart b/public/dart/sledge/lib/src/sledge.dart
index 081dad7..661a33c 100644
--- a/public/dart/sledge/lib/src/sledge.dart
+++ b/public/dart/sledge/lib/src/sledge.dart
@@ -110,6 +110,13 @@
     _initializationSucceeded = initializationCompleter.future;
   }
 
+  /// Convenience constructor for integration tests.
+  factory Sledge.fromLedgerHandle(
+      fidl.InterfaceHandle<ledger.Ledger> ledgerHandle,
+      [SledgePageId pageId]) {
+    return new Sledge._(ledgerHandle, pageId);
+  }
+
   /// Closes connection to ledger.
   void close() {
     _pageProxy.ctrl.close();
diff --git a/public/dart/sledge/meta/sledge_integration_tests.cmx b/public/dart/sledge/meta/sledge_integration_tests.cmx
new file mode 100644
index 0000000..ea584bb
--- /dev/null
+++ b/public/dart/sledge/meta/sledge_integration_tests.cmx
@@ -0,0 +1,11 @@
+{
+    "program": {
+        "data": "data/sledge_integration_tests"
+    },
+    "sandbox": {
+        "services": [
+            "fuchsia.crash.Analyzer",
+            "fuchsia.sys.Environment"
+        ]
+    }
+}
diff --git a/public/dart/sledge/test/integration_tests/sledge_integration_test.dart b/public/dart/sledge/test/integration_tests/sledge_integration_test.dart
new file mode 100644
index 0000000..87e97b0
--- /dev/null
+++ b/public/dart/sledge/test/integration_tests/sledge_integration_test.dart
@@ -0,0 +1,75 @@
+// Copyright 2018 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:async';
+
+import 'package:fidl/fidl.dart';
+import 'package:fidl_fuchsia_ledger/fidl.dart' as ledger;
+import 'package:fidl_fuchsia_sys/fidl_async.dart' show LaunchInfo, ComponentControllerProxy;
+import 'package:lib.app.dart/app_async.dart' show Services, StartupContext;
+import 'package:sledge/sledge.dart';
+import 'package:test/test.dart';
+
+class _LedgerTestInstance {
+  _LedgerTestInstance(this._componentControllerProxy);
+  InterfaceHandle<ledger.Ledger> ledgerHandle;
+  // ignore: unused_field
+  ComponentControllerProxy _componentControllerProxy;
+}
+
+/// Returns a new in-memory Ledger handle.
+Future<_LedgerTestInstance> getInMemoryLedgerTestInstance() async {
+  String server =
+      'fuchsia-pkg://fuchsia.com/ledger_test_instance_provider#meta/ledger_test_instance_provider.cmx';
+  final Services services = new Services();
+  final LaunchInfo launchInfo =
+      new LaunchInfo(url: server, directoryRequest: services.request());
+  final context = new StartupContext.fromStartupInfo();
+  final ComponentControllerProxy controller = new ComponentControllerProxy();
+  await context.launcher.createComponent(launchInfo, controller.ctrl.request());
+
+  final ledgerTestInstance = new _LedgerTestInstance(controller)
+  ..ledgerHandle = await services.connectToServiceByName<ledger.Ledger>(ledger.Ledger.$serviceName);
+  return ledgerTestInstance;
+}
+
+Completer<_LedgerTestInstance> _ledgerTestInstance;
+
+/// Creates a new test Sledge instance backed by an in-memory Ledger.
+Future<Sledge> getTestSledge() async {
+  if (_ledgerTestInstance == null) {
+    // If the completer has never been created, create it and start the process
+    // of obtaining a LedgerTestInstance.
+    _ledgerTestInstance = new Completer();
+    final futureLedgerTestInstance = getInMemoryLedgerTestInstance();
+    _ledgerTestInstance.complete(await futureLedgerTestInstance);
+  }
+  final ledgerTestInstance = await _ledgerTestInstance.future;
+  final sledge = new Sledge.fromLedgerHandle(ledgerTestInstance.ledgerHandle);
+  return sledge;
+}
+
+void main() {
+  test('Save a document', () async {
+    final sledge = await getTestSledge();
+    Map<String, BaseType> schemaDescription = <String, BaseType>{
+      'someInteger': new Integer()
+    };
+    Schema schema = new Schema(schemaDescription);
+    DocumentId id = new DocumentId(schema);
+
+    // Store a document in Sledge.
+    await sledge.runInTransaction(() async {
+      final List<Document> documents =
+          await sledge.getDocuments(new Query(schema));
+      assert(documents.isEmpty);
+      assert(await sledge.documentExists(id) == false);
+
+      Document doc = await sledge.getDocument(id);
+      assert(doc['someInteger'].value == 0);
+      doc['someInteger'].value = 42;
+      assert(doc['someInteger'].value == 42);
+    });
+  });
+}