[Sync] Add SharingMessageBridge implementation.

Add SharingMessageBridge without wiring it. The bridge does not store any
data on persistent storage.

Bug: 1034930
Change-Id: I0b19a136a2cc8a3e9ec11b7c1bb0f0b629996e52
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2002524
Reviewed-by: Alex Chau <[email protected]>
Reviewed-by: Marc Treib <[email protected]>
Reviewed-by: vitaliii <[email protected]>
Commit-Queue: Rushan Suleymanov <[email protected]>
Cr-Commit-Position: refs/heads/master@{#733594}
diff --git a/chrome/browser/sharing/sharing_message_bridge.h b/chrome/browser/sharing/sharing_message_bridge.h
new file mode 100644
index 0000000..c8f6b6030
--- /dev/null
+++ b/chrome/browser/sharing/sharing_message_bridge.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium 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 CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_H_
+#define CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_H_
+
+#include <memory>
+
+#include "components/sync/protocol/sharing_message_specifics.pb.h"
+
+// Class to provide an interface to send sharing messages using Sync.
+class SharingMessageBridge {
+ public:
+  // TODO(crbug.com/1034930): take callbacks once commit error propagation back
+  // to the bridge is implemented.
+  virtual void SendSharingMessage(
+      std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) = 0;
+
+  virtual ~SharingMessageBridge() = default;
+};
+
+#endif  // CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_H_
diff --git a/chrome/browser/sharing/sharing_message_bridge_impl.cc b/chrome/browser/sharing/sharing_message_bridge_impl.cc
new file mode 100644
index 0000000..b9cb0371
--- /dev/null
+++ b/chrome/browser/sharing/sharing_message_bridge_impl.cc
@@ -0,0 +1,101 @@
+// Copyright 2020 The Chromium 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 "chrome/browser/sharing/sharing_message_bridge_impl.h"
+
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/model_impl/in_memory_metadata_change_list.h"
+
+namespace {
+
+std::unique_ptr<syncer::EntityData> MoveToEntityData(
+    std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) {
+  auto entity_data = std::make_unique<syncer::EntityData>();
+  entity_data->specifics.set_allocated_sharing_message(specifics.release());
+  return entity_data;
+}
+
+}  // namespace
+
+SharingMessageBridgeImpl::SharingMessageBridgeImpl(
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
+    : ModelTypeSyncBridge(std::move(change_processor)) {
+  // Current data type doesn't have persistent storage so it's ready to sync
+  // immediately.
+  this->change_processor()->ModelReadyToSync(
+      std::make_unique<syncer::MetadataBatch>());
+}
+
+SharingMessageBridgeImpl::~SharingMessageBridgeImpl() = default;
+
+void SharingMessageBridgeImpl::SendSharingMessage(
+    std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) {
+  std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
+      CreateMetadataChangeList();
+  std::unique_ptr<syncer::EntityData> entity_data =
+      MoveToEntityData(std::move(specifics));
+  const std::string empty_storage_key;
+  change_processor()->Put(empty_storage_key, std::move(entity_data),
+                          metadata_change_list.get());
+}
+
+std::unique_ptr<syncer::MetadataChangeList>
+SharingMessageBridgeImpl::CreateMetadataChangeList() {
+  // The data type intentionally doesn't persist the data on disk, so metadata
+  // is just ignored.
+  // TODO(crbug.com/1034930): this metadata changelist stores data in memory, it
+  // would be better to create DummyMetadataChangeList to ignore any changes at
+  // all.
+  return std::make_unique<syncer::InMemoryMetadataChangeList>();
+}
+
+base::Optional<syncer::ModelError> SharingMessageBridgeImpl::MergeSyncData(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_data) {
+  DCHECK(entity_data.empty());
+  DCHECK(change_processor()->IsTrackingMetadata());
+  return ApplySyncChanges(std::move(metadata_change_list),
+                          std::move(entity_data));
+}
+
+base::Optional<syncer::ModelError> SharingMessageBridgeImpl::ApplySyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_data) {
+  // This data type is commit only and does not store any data in persistent
+  // storage. We can ignore any data coming from the server.
+  return {};
+}
+
+void SharingMessageBridgeImpl::GetData(StorageKeyList storage_keys,
+                                       DataCallback callback) {
+  return GetAllDataForDebugging(std::move(callback));
+}
+
+void SharingMessageBridgeImpl::GetAllDataForDebugging(DataCallback callback) {
+  // This data type does not store any data, we can always run the callback
+  // with empty data.
+  std::move(callback).Run(std::make_unique<syncer::MutableDataBatch>());
+}
+
+std::string SharingMessageBridgeImpl::GetClientTag(
+    const syncer::EntityData& entity_data) {
+  return GetStorageKey(entity_data);
+}
+
+std::string SharingMessageBridgeImpl::GetStorageKey(
+    const syncer::EntityData& entity_data) {
+  NOTREACHED();
+  return "";
+}
+
+// This is commit-only data type without storing any data on persistent storage.
+// We do not need keys here.
+bool SharingMessageBridgeImpl::SupportsGetClientTag() const {
+  return false;
+}
+
+bool SharingMessageBridgeImpl::SupportsGetStorageKey() const {
+  return false;
+}
diff --git a/chrome/browser/sharing/sharing_message_bridge_impl.h b/chrome/browser/sharing/sharing_message_bridge_impl.h
new file mode 100644
index 0000000..2ee91a9
--- /dev/null
+++ b/chrome/browser/sharing/sharing_message_bridge_impl.h
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium 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 CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_IMPL_H_
+#define CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_IMPL_H_
+
+#include <memory>
+
+#include "chrome/browser/sharing/sharing_message_bridge.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+// Class that implements sending sharing messages using Sync. This class
+// implements interaction with sync service. Sharing message data type is not
+// stored in any persistent storage.
+class SharingMessageBridgeImpl : public SharingMessageBridge,
+                                 public syncer::ModelTypeSyncBridge {
+ public:
+  explicit SharingMessageBridgeImpl(
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
+  ~SharingMessageBridgeImpl() override;
+  SharingMessageBridgeImpl(const SharingMessageBridgeImpl&) = delete;
+  SharingMessageBridgeImpl& operator=(const SharingMessageBridgeImpl&) = delete;
+
+  // SharingMessageBridge implementation.
+  void SendSharingMessage(
+      std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) override;
+
+  // ModelTypeSyncBridge implementation.
+  std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+      override;
+  base::Optional<syncer::ModelError> MergeSyncData(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_data) override;
+  base::Optional<syncer::ModelError> ApplySyncChanges(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_changes) override;
+  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetAllDataForDebugging(DataCallback callback) override;
+  std::string GetClientTag(const syncer::EntityData& entity_data) override;
+  std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+  bool SupportsGetClientTag() const override;
+  bool SupportsGetStorageKey() const override;
+};
+
+#endif  // CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_IMPL_H_
diff --git a/chrome/browser/sharing/sharing_message_bridge_impl_unittest.cc b/chrome/browser/sharing/sharing_message_bridge_impl_unittest.cc
new file mode 100644
index 0000000..d1bacc2
--- /dev/null
+++ b/chrome/browser/sharing/sharing_message_bridge_impl_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 The Chromium 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 "chrome/browser/sharing/sharing_message_bridge_impl.h"
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using testing::_;
+using testing::InvokeWithoutArgs;
+using testing::NotNull;
+using testing::Return;
+using testing::SaveArg;
+
+// Action SaveArgPointeeMove<k>(pointer) saves the value pointed to by the k-th
+// (0-based) argument of the mock function by moving it to *pointer.
+ACTION_TEMPLATE(SaveArgPointeeMove,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_1_VALUE_PARAMS(pointer)) {
+  *pointer = std::move(*testing::get<k>(args));
+}
+
+class SharingMessageBridgeTest : public testing::Test {
+ protected:
+  SharingMessageBridgeTest() {
+    EXPECT_CALL(*processor(), ModelReadyToSync(NotNull()));
+    bridge_ = std::make_unique<SharingMessageBridgeImpl>(
+        mock_processor_.CreateForwardingProcessor());
+    ON_CALL(*processor(), IsTrackingMetadata()).WillByDefault(Return(true));
+  }
+
+  SharingMessageBridgeImpl* bridge() { return bridge_.get(); }
+  syncer::MockModelTypeChangeProcessor* processor() { return &mock_processor_; }
+
+  std::unique_ptr<sync_pb::SharingMessageSpecifics> CreateSpecifics(
+      const std::string& payload) const {
+    auto specifics = std::make_unique<sync_pb::SharingMessageSpecifics>();
+    specifics->set_payload(payload);
+    return specifics;
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  testing::NiceMock<syncer::MockModelTypeChangeProcessor> mock_processor_;
+  std::unique_ptr<SharingMessageBridgeImpl> bridge_;
+};
+
+TEST_F(SharingMessageBridgeTest, ShouldWriteMessagesToProcessor) {
+  syncer::EntityData entity_data;
+  EXPECT_CALL(*processor(), Put(_, _, _))
+      .WillRepeatedly(SaveArgPointeeMove<1>(&entity_data));
+  bridge()->SendSharingMessage(CreateSpecifics("test_payload"));
+
+  EXPECT_TRUE(entity_data.specifics.has_sharing_message());
+  EXPECT_EQ(entity_data.specifics.sharing_message().payload(), "test_payload");
+
+  entity_data.specifics.Clear();
+  bridge()->SendSharingMessage(CreateSpecifics("another_payload"));
+
+  EXPECT_TRUE(entity_data.specifics.has_sharing_message());
+  EXPECT_EQ(entity_data.specifics.sharing_message().payload(),
+            "another_payload");
+}
+
+TEST_F(SharingMessageBridgeTest, ShouldNotGenerateStorageKey) {
+  std::string storage_key;
+  EXPECT_CALL(*processor(), Put(_, _, _)).WillOnce(SaveArg<0>(&storage_key));
+  bridge()->SendSharingMessage(
+      std::make_unique<sync_pb::SharingMessageSpecifics>());
+
+  EXPECT_TRUE(storage_key.empty());
+}
+
+}  // namespace