blob: 4c1b4b90d69b98badb4fbdd81a7d831599c41085 [file] [log] [blame]
// Copyright 2016 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 "src/ledger/bin/cloud_sync/impl/batch_upload.h"
#include <lib/async/dispatcher.h>
#include <lib/callback/capture.h>
#include <lib/fit/function.h>
#include <lib/fsl/vmo/strings.h>
#include <lib/gtest/test_loop_fixture.h>
#include <functional>
#include <map>
#include <utility>
#include "src/ledger/bin/cloud_sync/impl/testing/test_page_cloud.h"
#include "src/ledger/bin/encryption/fake/fake_encryption_service.h"
#include "src/ledger/bin/storage/fake/fake_object.h"
#include "src/ledger/bin/storage/public/commit.h"
#include "src/ledger/bin/storage/public/page_storage.h"
#include "src/ledger/bin/storage/testing/commit_empty_impl.h"
#include "src/ledger/bin/storage/testing/page_storage_empty_impl.h"
#include "src/lib/fxl/macros.h"
#include "src/lib/fxl/strings/string_view.h"
namespace cloud_sync {
namespace {
using ::storage::fake::FakeObject;
using ::storage::fake::FakePiece;
// Fake implementation of storage::Commit.
// TODO(kerneis): migrate to storage::fake::FakeCommit.
class TestCommit : public storage::CommitEmptyImpl {
public:
TestCommit(std::string id, std::string storage_bytes)
: id(std::move(id)), storage_bytes(std::move(storage_bytes)) {}
~TestCommit() override = default;
const storage::CommitId& GetId() const override { return id; }
fxl::StringView GetStorageBytes() const override { return storage_bytes; }
std::unique_ptr<const storage::Commit> Clone() const override {
return std::make_unique<TestCommit>(id, storage_bytes);
}
storage::CommitId id;
std::string storage_bytes;
};
// Fake implementation of storage::PageStorage. Injects the data that
// BatchUpload asks about: page id and unsynced objects to be uploaded.
// Registers the reported results of the upload: commits and objects marked as
// synced.
// TODO(kerneis): migrate to storage::fake::FakePageStorage.
class TestPageStorage : public storage::PageStorageEmptyImpl {
public:
TestPageStorage() = default;
~TestPageStorage() override = default;
void GetUnsyncedCommits(
fit::function<void(storage::Status,
std::vector<std::unique_ptr<const storage::Commit>>)>
callback) override {
std::vector<std::unique_ptr<const storage::Commit>> results;
std::transform(unsynced_commits.begin(), unsynced_commits.end(),
std::inserter(results, results.begin()),
[](const std::unique_ptr<const storage::Commit>& commit) {
return commit->Clone();
});
callback(storage::Status::OK, std::move(results));
}
void GetUnsyncedPieces(
fit::function<void(storage::Status,
std::vector<storage::ObjectIdentifier>)>
callback) override {
std::vector<storage::ObjectIdentifier> object_identifiers;
for (auto& digest_object_pair : unsynced_objects_to_return) {
object_identifiers.push_back(digest_object_pair.first);
}
callback(storage::Status::OK, std::move(object_identifiers));
}
void GetObject(storage::ObjectIdentifier object_identifier,
Location /*location*/,
fit::function<void(storage::Status,
std::unique_ptr<const storage::Object>)>
callback) override {
callback(storage::Status::OK,
std::make_unique<FakeObject>(std::move(
unsynced_objects_to_return[std::move(object_identifier)])));
}
void GetPiece(storage::ObjectIdentifier object_identifier,
fit::function<void(storage::Status,
std::unique_ptr<const storage::Piece>)>
callback) override {
callback(
storage::Status::OK,
std::move(unsynced_objects_to_return[std::move(object_identifier)]));
}
void MarkPieceSynced(storage::ObjectIdentifier object_identifier,
fit::function<void(storage::Status)> callback) override {
objects_marked_as_synced.insert(object_identifier);
callback(storage::Status::OK);
}
void MarkCommitSynced(
const storage::CommitId& commit_id,
fit::function<void(storage::Status)> callback) override {
commits_marked_as_synced.insert(commit_id);
unsynced_commits.erase(
std::remove_if(
unsynced_commits.begin(), unsynced_commits.end(),
[&commit_id](const std::unique_ptr<const storage::Commit>& commit) {
return commit->GetId() == commit_id;
}),
unsynced_commits.end());
callback(storage::Status::OK);
}
std::unique_ptr<TestCommit> NewCommit(std::string id, std::string content) {
auto commit =
std::make_unique<TestCommit>(std::move(id), std::move(content));
unsynced_commits.push_back(commit->Clone());
return commit;
}
std::map<storage::ObjectIdentifier, std::unique_ptr<const FakePiece>>
unsynced_objects_to_return;
std::set<storage::ObjectIdentifier> objects_marked_as_synced;
std::set<storage::CommitId> commits_marked_as_synced;
std::vector<std::unique_ptr<const storage::Commit>> unsynced_commits;
};
// Fake implementation of storage::PageStorage. Fails when trying to mark
// objects as synced and can be used to verify behavior on object upload storage
// errors.
class TestPageStorageFailingToMarkPieces : public TestPageStorage {
public:
void MarkPieceSynced(storage::ObjectIdentifier /*object_identifier*/,
fit::function<void(storage::Status)> callback) override {
callback(storage::Status::NOT_IMPLEMENTED);
}
};
template <typename E>
class BaseBatchUploadTest : public gtest::TestLoopFixture {
public:
BaseBatchUploadTest()
: encryption_service_(dispatcher()),
page_cloud_(page_cloud_ptr_.NewRequest()) {}
~BaseBatchUploadTest() override {}
public:
TestPageStorage storage_;
E encryption_service_;
cloud_provider::PageCloudPtr page_cloud_ptr_;
TestPageCloud page_cloud_;
unsigned int done_calls_ = 0u;
unsigned int error_calls_ = 0u;
BatchUpload::ErrorType last_error_type_ = BatchUpload::ErrorType::PERMANENT;
std::unique_ptr<BatchUpload> MakeBatchUpload(
std::vector<std::unique_ptr<const storage::Commit>> commits,
unsigned int max_concurrent_uploads = 10) {
return MakeBatchUploadWithStorage(&storage_, std::move(commits),
max_concurrent_uploads);
}
std::unique_ptr<BatchUpload> MakeBatchUploadWithStorage(
storage::PageStorage* storage,
std::vector<std::unique_ptr<const storage::Commit>> commits,
unsigned int max_concurrent_uploads = 10) {
return std::make_unique<BatchUpload>(
storage, &encryption_service_, &page_cloud_ptr_, std::move(commits),
[this] {
done_calls_++;
QuitLoop();
},
[this](BatchUpload::ErrorType error_type) {
error_calls_++;
last_error_type_ = error_type;
QuitLoop();
},
max_concurrent_uploads);
}
// Returns an object identifier for the provided fake |object_digest|.
// |object_digest| need not be valid (wrt. internal storage constraints) as it
// is only used as an opaque identifier for cloud_sync.
storage::ObjectIdentifier MakeObjectIdentifier(std::string object_digest) {
return encryption_service_.MakeObjectIdentifier(
storage::ObjectDigest(std::move(object_digest)));
}
private:
FXL_DISALLOW_COPY_AND_ASSIGN(BaseBatchUploadTest);
};
using BatchUploadTest = BaseBatchUploadTest<encryption::FakeEncryptionService>;
// Test an upload of a single commit with no unsynced objects.
TEST_F(BatchUploadTest, SingleCommit) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
auto batch_upload = MakeBatchUpload(std::move(commits));
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(0u, error_calls_);
// Verify the artifacts uploaded to cloud provider.
EXPECT_EQ(1u, page_cloud_.received_commits.size());
EXPECT_EQ("id", page_cloud_.received_commits.front().id);
EXPECT_EQ("content", encryption_service_.DecryptCommitSynchronous(
page_cloud_.received_commits.front().data));
EXPECT_TRUE(page_cloud_.received_objects.empty());
// Verify the sync status in storage.
EXPECT_EQ(1u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(1u, storage_.commits_marked_as_synced.count("id"));
EXPECT_EQ(0u, storage_.objects_marked_as_synced.size());
}
// Test an upload of multiple commits with no unsynced objects.
TEST_F(BatchUploadTest, MultipleCommits) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id0", "content0"));
commits.push_back(storage_.NewCommit("id1", "content1"));
auto batch_upload = MakeBatchUpload(std::move(commits));
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(0u, error_calls_);
// Verify that the commits were uploaded correctly and in a single call.
EXPECT_EQ(1u, page_cloud_.add_commits_calls);
ASSERT_EQ(2u, page_cloud_.received_commits.size());
EXPECT_EQ("id0", page_cloud_.received_commits[0].id);
EXPECT_EQ("content0", encryption_service_.DecryptCommitSynchronous(
page_cloud_.received_commits[0].data));
EXPECT_EQ("id1", page_cloud_.received_commits[1].id);
EXPECT_EQ("content1", encryption_service_.DecryptCommitSynchronous(
page_cloud_.received_commits[1].data));
EXPECT_TRUE(page_cloud_.received_objects.empty());
// Verify the sync status in storage.
EXPECT_EQ(2u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(1u, storage_.commits_marked_as_synced.count("id0"));
EXPECT_EQ(1u, storage_.commits_marked_as_synced.count("id1"));
}
// Test an upload of a commit with a few unsynced objects.
TEST_F(BatchUploadTest, SingleCommitWithObjects) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
auto id1 = MakeObjectIdentifier("obj_digest1");
auto id2 = MakeObjectIdentifier("obj_digest2");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload = MakeBatchUpload(std::move(commits));
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(0u, error_calls_);
// Verify the artifacts uploaded to cloud provider.
EXPECT_EQ(1u, page_cloud_.received_commits.size());
EXPECT_EQ("id", page_cloud_.received_commits.front().id);
EXPECT_EQ("content", encryption_service_.DecryptCommitSynchronous(
page_cloud_.received_commits.front().data));
EXPECT_EQ(2u, page_cloud_.received_objects.size());
EXPECT_EQ(
"obj_data1",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id1)]));
EXPECT_EQ(
"obj_data2",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id2)]));
// Verify the sync status in storage.
EXPECT_EQ(1u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(1u, storage_.commits_marked_as_synced.count("id"));
EXPECT_EQ(2u, storage_.objects_marked_as_synced.size());
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id1));
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id2));
}
// Verifies that the number of concurrent object uploads is limited to
// |max_concurrent_uploads|.
TEST_F(BatchUploadTest, ThrottleConcurrentUploads) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
storage::ObjectIdentifier id0 = MakeObjectIdentifier("obj_digest0");
storage::ObjectIdentifier id1 = MakeObjectIdentifier("obj_digest1");
storage::ObjectIdentifier id2 = MakeObjectIdentifier("obj_digest2");
storage_.unsynced_objects_to_return[id0] =
std::make_unique<FakePiece>(id0, "obj_data0");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
// Create the commit upload with |max_concurrent_uploads| = 2.
auto batch_upload = MakeBatchUpload(std::move(commits), 2);
page_cloud_.delay_add_object_callbacks = true;
batch_upload->Start();
RunLoopUntilIdle();
// Verify that only two object uploads are in progress.
EXPECT_EQ(2u, page_cloud_.add_object_calls);
page_cloud_.delay_add_object_callbacks = false;
page_cloud_.RunPendingCallbacks();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(0u, error_calls_);
EXPECT_EQ(3u, page_cloud_.add_object_calls);
EXPECT_EQ(3u, page_cloud_.received_objects.size());
EXPECT_EQ(
"obj_data0",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id0)]));
EXPECT_EQ(
"obj_data1",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id1)]));
EXPECT_EQ(
"obj_data2",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id2)]));
// Verify the sync status in storage.
EXPECT_EQ(3u, storage_.objects_marked_as_synced.size());
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id0));
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id1));
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id2));
}
// Test an upload that fails on uploading objects.
TEST_F(BatchUploadTest, FailedObjectUpload) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
storage::ObjectIdentifier id1 = MakeObjectIdentifier("obj_digest1");
storage::ObjectIdentifier id2 = MakeObjectIdentifier("obj_digest2");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload = MakeBatchUpload(std::move(commits));
page_cloud_.object_status_to_return = cloud_provider::Status::NETWORK_ERROR;
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
EXPECT_EQ(BatchUpload::ErrorType::TEMPORARY, last_error_type_);
// Verify that no commits were uploaded.
EXPECT_EQ(0u, page_cloud_.received_commits.size());
// Verify that neither the objects nor the commit were marked as synced.
EXPECT_TRUE(storage_.commits_marked_as_synced.empty());
EXPECT_TRUE(storage_.objects_marked_as_synced.empty());
}
// Test an upload that fails on uploading the commit.
TEST_F(BatchUploadTest, FailedCommitUpload) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
storage::ObjectIdentifier id1 = MakeObjectIdentifier("obj_digest1");
storage::ObjectIdentifier id2 = MakeObjectIdentifier("obj_digest2");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload = MakeBatchUpload(std::move(commits));
page_cloud_.commit_status_to_return = cloud_provider::Status::NETWORK_ERROR;
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
EXPECT_EQ(BatchUpload::ErrorType::TEMPORARY, last_error_type_);
// Verify that the objects were uploaded to cloud provider and marked as
// synced.
EXPECT_EQ(2u, page_cloud_.received_objects.size());
EXPECT_EQ(
"obj_data1",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id1)]));
EXPECT_EQ(
"obj_data2",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id2)]));
EXPECT_EQ(2u, storage_.objects_marked_as_synced.size());
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id1));
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id2));
// Verify that neither the commit wasn't marked as synced.
EXPECT_TRUE(storage_.commits_marked_as_synced.empty());
}
// Test an upload that fails and a subsequent retry that succeeds.
TEST_F(BatchUploadTest, ErrorAndRetry) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
storage::ObjectIdentifier id1 = MakeObjectIdentifier("obj_digest1");
storage::ObjectIdentifier id2 = MakeObjectIdentifier("obj_digest2");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload = MakeBatchUpload(std::move(commits));
page_cloud_.object_status_to_return = cloud_provider::Status::NETWORK_ERROR;
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
EXPECT_EQ(BatchUpload::ErrorType::TEMPORARY, last_error_type_);
EXPECT_EQ(0u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(0u, storage_.objects_marked_as_synced.size());
// TestStorage moved the objects to be returned out, need to add them again
// before retry.
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
page_cloud_.object_status_to_return = cloud_provider::Status::OK;
batch_upload->Retry();
RunLoopUntilIdle();
// Verify the artifacts uploaded to cloud provider.
EXPECT_EQ(1u, page_cloud_.received_commits.size());
EXPECT_EQ("id", page_cloud_.received_commits.front().id);
EXPECT_EQ("content", encryption_service_.DecryptCommitSynchronous(
page_cloud_.received_commits.front().data));
EXPECT_EQ(2u, page_cloud_.received_objects.size());
EXPECT_EQ(
"obj_data1",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id1)]));
EXPECT_EQ(
"obj_data2",
encryption_service_.DecryptObjectSynchronous(
page_cloud_.received_objects[encryption_service_
.GetObjectNameSynchronous(id2)]));
// Verify the sync status in storage.
EXPECT_EQ(1u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(1u, storage_.commits_marked_as_synced.count("id"));
EXPECT_EQ(2u, storage_.objects_marked_as_synced.size());
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id1));
EXPECT_EQ(1u, storage_.objects_marked_as_synced.count(id2));
}
// Test a commit upload that gets an error from storage.
TEST_F(BatchUploadTest, FailedCommitUploadWitStorageError) {
storage::PageStorageEmptyImpl test_storage;
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
auto batch_upload =
MakeBatchUploadWithStorage(&test_storage, std::move(commits));
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
EXPECT_EQ(BatchUpload::ErrorType::PERMANENT, last_error_type_);
// Verify that no commits were uploaded.
EXPECT_EQ(0u, page_cloud_.received_commits.size());
}
// Test objects upload that get an error from storage.
TEST_F(BatchUploadTest, FailedObjectUploadWitStorageError) {
TestPageStorageFailingToMarkPieces test_storage;
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
storage::ObjectIdentifier id1 = MakeObjectIdentifier("obj_digest1");
storage::ObjectIdentifier id2 = MakeObjectIdentifier("obj_digest2");
test_storage.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
test_storage.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload =
MakeBatchUploadWithStorage(&test_storage, std::move(commits));
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
EXPECT_EQ(BatchUpload::ErrorType::PERMANENT, last_error_type_);
// Verify that no commit or objects were uploaded.
EXPECT_EQ(0u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(0u, storage_.objects_marked_as_synced.size());
}
// Verifies that if only one of many uploads fails, we still stop and notify the
// client.
TEST_F(BatchUploadTest, ErrorOneOfMultipleObject) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
storage::ObjectIdentifier id0 = MakeObjectIdentifier("obj_digest0");
storage::ObjectIdentifier id1 = MakeObjectIdentifier("obj_digest1");
storage::ObjectIdentifier id2 = MakeObjectIdentifier("obj_digest2");
storage_.unsynced_objects_to_return[id0] =
std::make_unique<FakePiece>(id0, "obj_data0");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload = MakeBatchUpload(std::move(commits));
page_cloud_.object_status_to_return = cloud_provider::Status::NETWORK_ERROR;
page_cloud_.reset_object_status_after_call = true;
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
// Verify that two storage objects were correctly marked as synced.
EXPECT_EQ(0u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(2u, storage_.objects_marked_as_synced.size());
EXPECT_EQ(0u, page_cloud_.received_commits.size());
// TestStorage moved the objects to be returned out, need to add them again
// before retry.
storage_.unsynced_objects_to_return[id0] =
std::make_unique<FakePiece>(id0, "obj_data0");
storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
// Try upload again.
batch_upload->Retry();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(1u, error_calls_);
// Verify the sync status in storage.
EXPECT_EQ(1u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(3u, storage_.objects_marked_as_synced.size());
}
// Verifies that we do not upload synced commits.
TEST_F(BatchUploadTest, DoNotUploadSyncedCommits) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(std::make_unique<TestCommit>("id", "content"));
auto batch_upload = MakeBatchUpload(std::move(commits));
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(0u, error_calls_);
// Verify that the commit was not synced.
EXPECT_EQ(0u, storage_.commits_marked_as_synced.size());
EXPECT_EQ(0u, page_cloud_.received_commits.size());
}
// Verifies that we do not upload synced commits on retries.
TEST_F(BatchUploadTest, DoNotUploadSyncedCommitsOnRetry) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(storage_.NewCommit("id", "content"));
auto batch_upload = MakeBatchUpload(std::move(commits));
page_cloud_.commit_status_to_return = cloud_provider::Status::NETWORK_ERROR;
batch_upload->Start();
RunLoopUntilIdle();
EXPECT_EQ(0u, done_calls_);
EXPECT_EQ(1u, error_calls_);
// Verify that the commit was not synced.
EXPECT_EQ(0u, storage_.commits_marked_as_synced.size());
// Mark commit as synced.
storage::Status status;
storage_.MarkCommitSynced("id",
callback::Capture(QuitLoopClosure(), &status));
RunLoopUntilIdle();
EXPECT_EQ(storage::Status::OK, status);
EXPECT_EQ(0u, storage_.unsynced_commits.size());
// Retry.
page_cloud_.add_commits_calls = 0;
page_cloud_.commit_status_to_return = cloud_provider::Status::OK;
batch_upload->Retry();
RunLoopUntilIdle();
EXPECT_EQ(1u, done_calls_);
EXPECT_EQ(1u, error_calls_);
// Verify that no calls were made to attempt to upload the commit.
EXPECT_EQ(0u, page_cloud_.add_commits_calls);
}
class FailingEncryptCommitEncryptionService
: public encryption::FakeEncryptionService {
public:
explicit FailingEncryptCommitEncryptionService(async_dispatcher_t* dispatcher)
: encryption::FakeEncryptionService(dispatcher) {}
void EncryptCommit(
std::string /*commit_storage*/,
fit::function<void(encryption::Status, std::string)> callback) override {
callback(encryption::Status::INVALID_ARGUMENT, "");
}
};
class FailingGetNameEncryptionService
: public encryption::FakeEncryptionService {
public:
explicit FailingGetNameEncryptionService(async_dispatcher_t* dispatcher)
: encryption::FakeEncryptionService(dispatcher) {}
void GetObjectName(
storage::ObjectIdentifier /*object_identifier*/,
fit::function<void(encryption::Status, std::string)> callback) override {
callback(encryption::Status::INVALID_ARGUMENT, "");
}
};
class FailingEncryptObjectEncryptionService
: public encryption::FakeEncryptionService {
public:
explicit FailingEncryptObjectEncryptionService(async_dispatcher_t* dispatcher)
: encryption::FakeEncryptionService(dispatcher) {}
void EncryptObject(
storage::ObjectIdentifier /*object_identifier*/,
fxl::StringView /*content*/,
fit::function<void(encryption::Status, std::string)> callback) override {
callback(encryption::Status::INVALID_ARGUMENT, "");
}
};
template <typename E>
using FailingBatchUploadTest = BaseBatchUploadTest<E>;
using FailingEncryptionServices =
::testing::Types<FailingEncryptCommitEncryptionService,
FailingGetNameEncryptionService,
FailingEncryptObjectEncryptionService>;
TYPED_TEST_SUITE(FailingBatchUploadTest, FailingEncryptionServices);
TYPED_TEST(FailingBatchUploadTest, Fail) {
std::vector<std::unique_ptr<const storage::Commit>> commits;
commits.push_back(this->storage_.NewCommit("id", "content"));
auto id1 = this->MakeObjectIdentifier("obj_digest1");
auto id2 = this->MakeObjectIdentifier("obj_digest2");
this->storage_.unsynced_objects_to_return[id1] =
std::make_unique<FakePiece>(id1, "obj_data1");
this->storage_.unsynced_objects_to_return[id2] =
std::make_unique<FakePiece>(id2, "obj_data2");
auto batch_upload = this->MakeBatchUpload(std::move(commits));
batch_upload->Start();
this->RunLoopUntilIdle();
EXPECT_EQ(0u, this->done_calls_);
EXPECT_GE(this->error_calls_, 1u);
// Verify the artifacts uploaded to cloud provider.
EXPECT_EQ(0u, this->page_cloud_.received_commits.size());
}
} // namespace
} // namespace cloud_sync