[ledger] Test LedgerRepositoryFactoryImpl
Test: The new test passes.
Change-Id: Ic3c00f69f764bfd02b26db35292414152270c54c
diff --git a/bin/ledger/app/BUILD.gn b/bin/ledger/app/BUILD.gn
index 3f6a790..104d01b 100644
--- a/bin/ledger/app/BUILD.gn
+++ b/bin/ledger/app/BUILD.gn
@@ -158,6 +158,7 @@
"disk_cleanup_manager_unittest.cc",
"fidl/serialization_size_unittest.cc",
"ledger_manager_unittest.cc",
+ "ledger_repository_factory_impl_unittest.cc",
"ledger_repository_impl_unittest.cc",
"merging/common_ancestor_unittest.cc",
"merging/conflict_resolver_client_unittest.cc",
diff --git a/bin/ledger/app/ledger_repository_factory_impl.h b/bin/ledger/app/ledger_repository_factory_impl.h
index 7271bd4..d43e888 100644
--- a/bin/ledger/app/ledger_repository_factory_impl.h
+++ b/bin/ledger/app/ledger_repository_factory_impl.h
@@ -40,10 +40,6 @@
component::ObjectDir inspect_object_dir);
~LedgerRepositoryFactoryImpl() override;
- private:
- class LedgerRepositoryContainer;
- struct RepositoryInformation;
-
// LedgerRepositoryFactoryErrorNotifierDelegate:
void GetRepository(
zx::channel repository_handle,
@@ -53,6 +49,10 @@
repository_request,
fit::function<void(Status)> callback) override;
+ private:
+ class LedgerRepositoryContainer;
+ struct RepositoryInformation;
+
// Binds |repository_request| to the repository stored in the directory opened
// in |root_fd|.
void GetRepositoryByFD(
diff --git a/bin/ledger/app/ledger_repository_factory_impl_unittest.cc b/bin/ledger/app/ledger_repository_factory_impl_unittest.cc
new file mode 100644
index 0000000..5391029
--- /dev/null
+++ b/bin/ledger/app/ledger_repository_factory_impl_unittest.cc
@@ -0,0 +1,326 @@
+// 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.
+
+#include "peridot/bin/ledger/app/ledger_repository_factory_impl.h"
+
+#include <fuchsia/inspect/cpp/fidl.h>
+#include <lib/callback/capture.h>
+#include <lib/callback/set_when_called.h>
+#include <lib/fsl/io/fd.h>
+#include <lib/fxl/files/directory.h>
+#include <lib/fxl/files/unique_fd.h>
+#include <lib/fxl/strings/string_view.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "peridot/bin/ledger/testing/inspect.h"
+#include "peridot/bin/ledger/testing/test_with_environment.h"
+#include "peridot/lib/scoped_tmpfs/scoped_tmpfs.h"
+
+namespace ledger {
+namespace {
+
+using ::testing::ElementsAre;
+using ::testing::IsEmpty;
+using ::testing::Not;
+using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
+
+constexpr fxl::StringView kObjectsName = "test objects";
+constexpr fxl::StringView kRepositoriesName = "repositories";
+constexpr fxl::StringView kUserID = "test user ID";
+
+class LedgerRepositoryFactoryImplTest : public TestWithEnvironment {
+ public:
+ LedgerRepositoryFactoryImplTest() {
+ object_dir_ = component::ObjectDir(
+ fbl::MakeRefCounted<component::Object>(kObjectsName));
+ repository_factory_ = std::make_unique<LedgerRepositoryFactoryImpl>(
+ &environment_, nullptr, object_dir_);
+ }
+
+ ~LedgerRepositoryFactoryImplTest() override {}
+
+ protected:
+ ::testing::AssertionResult CreateDirectory(std::string name);
+ ::testing::AssertionResult CallGetRepository(
+ std::string name,
+ ledger_internal::LedgerRepositoryPtr* ledger_repository_ptr);
+ ::testing::AssertionResult ReadTopLevelData(fuchsia::inspect::Object* object);
+ ::testing::AssertionResult ListTopLevelChildren(
+ fidl::VectorPtr<fidl::StringPtr>* children);
+ ::testing::AssertionResult OpenTopLevelRepositoriesChild(
+ fuchsia::inspect::InspectPtr* repositories_inspect_ptr);
+ ::testing::AssertionResult ReadData(fuchsia::inspect::InspectPtr* inspect_ptr,
+ fuchsia::inspect::Object* object);
+ ::testing::AssertionResult ListChildren(
+ fuchsia::inspect::InspectPtr* inspect_ptr,
+ fidl::VectorPtr<fidl::StringPtr>* children_names);
+ ::testing::AssertionResult OpenChild(
+ fuchsia::inspect::InspectPtr* parent_inspect_ptr,
+ fidl::StringPtr child_name,
+ fuchsia::inspect::InspectPtr* child_inspect_ptr);
+
+ scoped_tmpfs::ScopedTmpFS tmpfs_;
+ component::ObjectDir object_dir_;
+ std::unique_ptr<LedgerRepositoryFactoryImpl> repository_factory_;
+
+ private:
+ FXL_DISALLOW_COPY_AND_ASSIGN(LedgerRepositoryFactoryImplTest);
+};
+
+::testing::AssertionResult LedgerRepositoryFactoryImplTest::CreateDirectory(
+ std::string name) {
+ if (!files::CreateDirectoryAt(tmpfs_.root_fd(), name)) {
+ return ::testing::AssertionFailure()
+ << "Failed to create directory \"" << name << "\"!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult LedgerRepositoryFactoryImplTest::CallGetRepository(
+ std::string name,
+ ledger_internal::LedgerRepositoryPtr* ledger_repository_ptr) {
+ fxl::UniqueFD fd(openat(tmpfs_.root_fd(), name.c_str(), O_PATH));
+ if (!fd.is_valid()) {
+ return ::testing::AssertionFailure()
+ << "Failed to validate directory \"" << name << "\"!";
+ }
+
+ bool callback_called;
+ Status status = Status::UNKNOWN_ERROR;
+
+ repository_factory_->GetRepository(
+ fsl::CloneChannelFromFileDescriptor(fd.get()), nullptr,
+ kUserID.ToString(), ledger_repository_ptr->NewRequest(),
+ callback::Capture(callback::SetWhenCalled(&callback_called), &status));
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure()
+ << "Callback passed to GetRepository not called!";
+ }
+ if (status != Status::OK) {
+ return ::testing::AssertionFailure() << "Status of GetRepository call was "
+ << static_cast<int32_t>(status) << "!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult LedgerRepositoryFactoryImplTest::ReadTopLevelData(
+ fuchsia::inspect::Object* object) {
+ bool callback_called;
+
+ object_dir_.object()->ReadData(
+ callback::Capture(callback::SetWhenCalled(&callback_called), object));
+ RunLoopUntilIdle();
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure()
+ << "Callback passed to object_dir_.object()->ReadData not called!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult
+LedgerRepositoryFactoryImplTest::ListTopLevelChildren(
+ fidl::VectorPtr<fidl::StringPtr>* children) {
+ bool callback_called;
+
+ object_dir_.object()->ListChildren(
+ callback::Capture(callback::SetWhenCalled(&callback_called), children));
+ RunLoopUntilIdle();
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure()
+ << "Callback passed to object_dir_.object()->ListChildren not "
+ "called!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult
+LedgerRepositoryFactoryImplTest::OpenTopLevelRepositoriesChild(
+ fuchsia::inspect::InspectPtr* repositories_inspect_ptr) {
+ bool callback_called;
+ bool success = false;
+
+ object_dir_.object()->OpenChild(
+ kRepositoriesName.ToString(), repositories_inspect_ptr->NewRequest(),
+ callback::Capture(callback::SetWhenCalled(&callback_called), &success));
+ RunLoopUntilIdle();
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure()
+ << "Callback passed to object_dir_.object()->OpenChild not called!";
+ }
+ if (!success) {
+ return ::testing::AssertionFailure()
+ << "object_dir_.object()->OpenChild call unsuccessful!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult LedgerRepositoryFactoryImplTest::ReadData(
+ fuchsia::inspect::InspectPtr* inspect_ptr,
+ fuchsia::inspect::Object* object) {
+ bool callback_called;
+
+ (*inspect_ptr)
+ ->ReadData(
+ callback::Capture(callback::SetWhenCalled(&callback_called), object));
+ RunLoopUntilIdle();
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure() << "ReadData callback not called!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult LedgerRepositoryFactoryImplTest::ListChildren(
+ fuchsia::inspect::InspectPtr* inspect_ptr,
+ fidl::VectorPtr<fidl::StringPtr>* children_names) {
+ bool callback_called;
+
+ (*inspect_ptr)
+ ->ListChildren(callback::Capture(
+ callback::SetWhenCalled(&callback_called), children_names));
+ RunLoopUntilIdle();
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure() << "ListChildren callback not called!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+::testing::AssertionResult LedgerRepositoryFactoryImplTest::OpenChild(
+ fuchsia::inspect::InspectPtr* parent_inspect_ptr,
+ fidl::StringPtr child_name,
+ fuchsia::inspect::InspectPtr* child_inspect_ptr) {
+ bool callback_called;
+ bool success = false;
+
+ (*parent_inspect_ptr)
+ ->OpenChild(child_name, child_inspect_ptr->NewRequest(),
+ callback::Capture(callback::SetWhenCalled(&callback_called),
+ &success));
+ RunLoopUntilIdle();
+
+ if (!callback_called) {
+ return ::testing::AssertionFailure() << "OpenChild callback not called!";
+ }
+ if (!success) {
+ return ::testing::AssertionFailure() << "OpenChild call unsuccessful!";
+ }
+ return ::testing::AssertionSuccess();
+}
+
+TEST_F(LedgerRepositoryFactoryImplTest, InspectAPINoRepositories) {
+ fuchsia::inspect::Object object;
+ fidl::VectorPtr<fidl::StringPtr> children;
+
+ ASSERT_TRUE(ReadTopLevelData(&object));
+ ASSERT_TRUE(ListTopLevelChildren(&children));
+
+ EXPECT_EQ(kObjectsName, *object.name);
+ EXPECT_THAT(*object.properties, IsEmpty());
+ EXPECT_THAT(*object.metrics, IsEmpty());
+ EXPECT_THAT(*children, IsEmpty());
+}
+
+TEST_F(LedgerRepositoryFactoryImplTest,
+ InspectAPITwoRepositoriesOneAccessedTwice) {
+ // The directories in which the two repositories will be created.
+ std::string first_directory = "first directory";
+ std::string second_directory = "second directory";
+
+ // The names of the two repositories, determined by the
+ // LedgerRepositoryFactoryImpl under test.
+ fidl::StringPtr first_repository_name;
+ fidl::StringPtr second_repository_name;
+
+ // Bindings to the two repositories. If these are not maintained, the
+ // LedgerRepositoryFactoryImpl::LedgerRepositoryContainer objects associated
+ // with the repositories will be destroyed and the repositories will no
+ // longer appear represented in the Inspect API.
+ ledger_internal::LedgerRepositoryPtr first_ledger_repository_ptr;
+ ledger_internal::LedgerRepositoryPtr second_ledger_repository_ptr;
+ ledger_internal::LedgerRepositoryPtr first_again_ledger_repository_ptr;
+
+ // Bindings to Inspect API "Inspect" objects. Over the course of the test the
+ // top-level object_dir_ will gain a "repositories" child which itself will
+ // gain two children (one for each created repository, with names chosen by
+ // the LedgerRepositoryFactoryImpl under test).
+ fuchsia::inspect::InspectPtr repositories_inspect_ptr;
+ fuchsia::inspect::InspectPtr first_repository_inspect_ptr;
+ fuchsia::inspect::InspectPtr second_repository_inspect_ptr;
+
+ // Temporary objects populated and cleared throughout the test.
+ fuchsia::inspect::Object object;
+ fidl::VectorPtr<fidl::StringPtr> children_names;
+
+ // Create the directories for the repositories.
+ ASSERT_TRUE(CreateDirectory(first_directory));
+ ASSERT_TRUE(CreateDirectory(second_directory));
+
+ // Request one repository, then query the object_dir_ (and its children) to
+ // verify that that repository is listed (and to learn the name under which
+ // it is listed) and that it was requested once.
+ ASSERT_TRUE(CallGetRepository(first_directory, &first_ledger_repository_ptr));
+ ASSERT_TRUE(ListTopLevelChildren(&children_names));
+ EXPECT_THAT(*children_names, ElementsAre(kRepositoriesName.ToString()));
+ ASSERT_TRUE(OpenTopLevelRepositoriesChild(&repositories_inspect_ptr));
+ ASSERT_TRUE(ListChildren(&repositories_inspect_ptr, &children_names));
+ EXPECT_THAT(*children_names, SizeIs(1));
+ first_repository_name = children_names->at(0);
+ EXPECT_THAT(*first_repository_name, Not(IsEmpty()));
+ ASSERT_TRUE(OpenChild(&repositories_inspect_ptr, first_repository_name,
+ &first_repository_inspect_ptr));
+ ASSERT_TRUE(ReadData(&first_repository_inspect_ptr, &object));
+ EXPECT_EQ(first_repository_name, *object.name);
+ ExpectRequestsMetric(&object, 1UL);
+
+ // Request a second repository, then query the "repositories" Inspect object
+ // to verify that that second repository is listed in addition to the first
+ // (and to learn the name under which it is listed) and that the two
+ // repositories were each requested once.
+ ASSERT_TRUE(
+ CallGetRepository(second_directory, &second_ledger_repository_ptr));
+ ASSERT_TRUE(ListChildren(&repositories_inspect_ptr, &children_names));
+ EXPECT_THAT(*children_names, SizeIs(2));
+ second_repository_name =
+ *find_if_not(children_names->begin(), children_names->end(),
+ [&first_repository_name](const auto& name) {
+ return name == first_repository_name;
+ });
+ EXPECT_THAT(*children_names, UnorderedElementsAre(first_repository_name,
+ second_repository_name));
+ EXPECT_THAT(*second_repository_name, Not(IsEmpty()));
+ ASSERT_TRUE(OpenChild(&repositories_inspect_ptr, second_repository_name,
+ &second_repository_inspect_ptr));
+ ASSERT_TRUE(ReadData(&first_repository_inspect_ptr, &object));
+ EXPECT_EQ(first_repository_name, *object.name);
+ ExpectRequestsMetric(&object, 1UL);
+ ASSERT_TRUE(ReadData(&second_repository_inspect_ptr, &object));
+ EXPECT_EQ(second_repository_name, *object.name);
+ ExpectRequestsMetric(&object, 1UL);
+
+ // Request the first repository a second time, then query the "repositories"
+ // Inspect object to verify that both repositories remain listed (with their
+ // same names) and are described as having been requested twice and once,
+ // respectively.
+ ASSERT_TRUE(
+ CallGetRepository(first_directory, &first_again_ledger_repository_ptr));
+ ASSERT_TRUE(ListChildren(&repositories_inspect_ptr, &children_names));
+ EXPECT_THAT(*children_names, UnorderedElementsAre(first_repository_name,
+ second_repository_name));
+ ASSERT_TRUE(ReadData(&first_repository_inspect_ptr, &object));
+ EXPECT_EQ(first_repository_name, *object.name);
+ ExpectRequestsMetric(&object, 2UL);
+ ASSERT_TRUE(ReadData(&second_repository_inspect_ptr, &object));
+ EXPECT_EQ(second_repository_name, *object.name);
+ ExpectRequestsMetric(&object, 1UL);
+}
+
+} // namespace
+} // namespace ledger
diff --git a/bin/ledger/app/ledger_repository_impl_unittest.cc b/bin/ledger/app/ledger_repository_impl_unittest.cc
index bef8461..c3d7a31 100644
--- a/bin/ledger/app/ledger_repository_impl_unittest.cc
+++ b/bin/ledger/app/ledger_repository_impl_unittest.cc
@@ -21,32 +21,13 @@
#include "peridot/bin/ledger/storage/fake/fake_db_factory.h"
#include "peridot/bin/ledger/storage/public/types.h"
#include "peridot/bin/ledger/testing/fake_disk_cleanup_manager.h"
+#include "peridot/bin/ledger/testing/inspect.h"
#include "peridot/bin/ledger/testing/test_with_environment.h"
#include "peridot/lib/scoped_tmpfs/scoped_tmpfs.h"
namespace ledger {
namespace {
-void ExpectRequestsMetric(fuchsia::inspect::Object* object,
- unsigned long expected_value) {
- bool requests_found = false;
- unsigned long extra_requests_found = 0UL;
- unsigned long requests = 777'777;
- for (auto& index : *object->metrics) {
- if (index.key == "requests") {
- if (!requests_found) {
- requests_found = true;
- requests = index.value.uint_value();
- } else {
- extra_requests_found++;
- }
- }
- }
- EXPECT_TRUE(requests_found);
- EXPECT_EQ(expected_value, requests);
- EXPECT_EQ(0UL, extra_requests_found);
-}
-
class LedgerRepositoryImplTest : public TestWithEnvironment {
public:
LedgerRepositoryImplTest() {
diff --git a/bin/ledger/testing/BUILD.gn b/bin/ledger/testing/BUILD.gn
index ad39e70..6a4508b 100644
--- a/bin/ledger/testing/BUILD.gn
+++ b/bin/ledger/testing/BUILD.gn
@@ -16,6 +16,8 @@
"fake_disk_cleanup_manager.h",
"get_page_ensure_initialized.cc",
"get_page_ensure_initialized.h",
+ "inspect.cc",
+ "inspect.h",
"ledger_matcher.cc",
"ledger_matcher.h",
"page_data_generator.cc",
diff --git a/bin/ledger/testing/inspect.cc b/bin/ledger/testing/inspect.cc
new file mode 100644
index 0000000..dc9be8b
--- /dev/null
+++ b/bin/ledger/testing/inspect.cc
@@ -0,0 +1,34 @@
+// 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.
+
+#include "peridot/bin/ledger/testing/inspect.h"
+
+#include <fuchsia/inspect/cpp/fidl.h>
+
+#include "gtest/gtest.h"
+
+namespace ledger {
+
+// TODO(crjohns, nathaniel): Migrate this to a testing::Matcher.
+void ExpectRequestsMetric(fuchsia::inspect::Object* object,
+ unsigned long expected_value) {
+ bool requests_found = false;
+ unsigned long extra_requests_found = 0UL;
+ unsigned long requests = 0UL;
+ for (auto& index : *object->metrics) {
+ if (index.key == "requests") {
+ if (!requests_found) {
+ requests_found = true;
+ requests = index.value.uint_value();
+ } else {
+ extra_requests_found++;
+ }
+ }
+ }
+ EXPECT_TRUE(requests_found);
+ EXPECT_EQ(expected_value, requests);
+ EXPECT_EQ(0UL, extra_requests_found);
+}
+
+} // namespace ledger
diff --git a/bin/ledger/testing/inspect.h b/bin/ledger/testing/inspect.h
new file mode 100644
index 0000000..7df672c
--- /dev/null
+++ b/bin/ledger/testing/inspect.h
@@ -0,0 +1,21 @@
+// 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.
+
+#ifndef PERIDOT_BIN_LEDGER_TESTING_INSPECT_H_
+#define PERIDOT_BIN_LEDGER_TESTING_INSPECT_H_
+
+#include <fuchsia/inspect/cpp/fidl.h>
+
+namespace ledger {
+
+// TODO(crjohns, nathaniel): Move to an "Inspect API testing helpers" library,
+// parameterizing by the metric name rather than having "requests" hard-coded.
+// EXPECTs that |object| has a metric named "requests" and that the value of
+// the "requests" metric is |expected_value|.
+void ExpectRequestsMetric(fuchsia::inspect::Object* object,
+ unsigned long expected_value);
+
+} // namespace ledger
+
+#endif // PERIDOT_BIN_LEDGER_TESTING_INSPECT_H_