Commit 4730620a authored by Leonid Baraz's avatar Leonid Baraz Committed by Commit Bot

Add tests for last record digest match.

Bug: b:153364303
Bug: b:153659559
Change-Id: I0a7ecedbcf043b93473334e666c93532586eca3b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2399122
Auto-Submit: Leonid Baraz <lbaraz@chromium.org>
Reviewed-by: default avatarZach Trudo <zatrudo@google.com>
Commit-Queue: Leonid Baraz <lbaraz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806744}
parent c8083e3c
...@@ -920,9 +920,7 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> { ...@@ -920,9 +920,7 @@ class StorageQueue::WriteContext : public TaskRunnerContext<Status> {
} }
scoped_refptr<SingleFile> last_file = assign_result.ValueOrDie(); scoped_refptr<SingleFile> last_file = assign_result.ValueOrDie();
// Prepare and initiate writing generation into to a new file. // Writing metadata ahead of the data write.
// Pick up when both WriteMetadata and WriteHeaderAndBlock
// have finished.
Status write_result = storage_queue_->WriteMetadata(); Status write_result = storage_queue_->WriteMetadata();
if (!write_result.ok()) { if (!write_result.ok()) {
Response(write_result); Response(write_result);
......
...@@ -76,7 +76,16 @@ class TestEvent { ...@@ -76,7 +76,16 @@ class TestEvent {
class MockUploadClient : public StorageQueue::UploaderInterface { class MockUploadClient : public StorageQueue::UploaderInterface {
public: public:
MockUploadClient() = default; // Mapping of (generation, seq number) to matching record digest. Whenever a
// record is uploaded and includes last record digest, this map should have
// that digest already recorded. Only the first record in a generation is
// uploaded without last record digest.
using LastRecordDigestMap = std::map<
std::pair<uint64_t /*generation */, uint64_t /*sequencing number*/>,
std::string /*digest*/>;
explicit MockUploadClient(LastRecordDigestMap* last_record_digest_map)
: last_record_digest_map_(last_record_digest_map) {}
void ProcessRecord(StatusOr<EncryptedRecord> encrypted_record, void ProcessRecord(StatusOr<EncryptedRecord> encrypted_record,
base::OnceCallback<void(bool)> processed_cb) override { base::OnceCallback<void(bool)> processed_cb) override {
...@@ -89,25 +98,22 @@ class MockUploadClient : public StorageQueue::UploaderInterface { ...@@ -89,25 +98,22 @@ class MockUploadClient : public StorageQueue::UploaderInterface {
ASSERT_TRUE(wrapped_record.ParseFromString( ASSERT_TRUE(wrapped_record.ParseFromString(
encrypted_record.ValueOrDie().encrypted_wrapped_record())); encrypted_record.ValueOrDie().encrypted_wrapped_record()));
// Verify generation match. // Verify generation match.
const auto& sequencing_information =
encrypted_record.ValueOrDie().sequencing_information();
if (generation_id_.has_value() && if (generation_id_.has_value() &&
generation_id_.value() != encrypted_record.ValueOrDie() generation_id_.value() != sequencing_information.generation_id()) {
.sequencing_information()
.generation_id()) {
std::move(processed_cb) std::move(processed_cb)
.Run(UploadRecordFailure(Status( .Run(UploadRecordFailure(Status(
error::DATA_LOSS, error::DATA_LOSS,
base::StrCat({"Generation id mismatch, expected=", base::StrCat({"Generation id mismatch, expected=",
base::NumberToString(generation_id_.value()), base::NumberToString(generation_id_.value()),
" actual=", " actual=",
base::NumberToString(encrypted_record.ValueOrDie() base::NumberToString(
.sequencing_information() sequencing_information.generation_id())}))));
.generation_id())}))));
return; return;
} }
if (!generation_id_.has_value()) { if (!generation_id_.has_value()) {
generation_id_ = encrypted_record.ValueOrDie() generation_id_ = sequencing_information.generation_id();
.sequencing_information()
.generation_id();
} }
// Verify digest and its match. // Verify digest and its match.
...@@ -124,12 +130,26 @@ class MockUploadClient : public StorageQueue::UploaderInterface { ...@@ -124,12 +130,26 @@ class MockUploadClient : public StorageQueue::UploaderInterface {
Status(error::DATA_LOSS, "Record digest mismatch"))); Status(error::DATA_LOSS, "Record digest mismatch")));
return; return;
} }
if (wrapped_record.has_last_record_digest()) {
auto it = last_record_digest_map_->find(
std::make_pair(sequencing_information.sequencing_id() - 1,
sequencing_information.generation_id()));
if (it == last_record_digest_map_->end() ||
it->second != wrapped_record.last_record_digest()) {
std::move(processed_cb)
.Run(UploadRecordFailure(
Status(error::DATA_LOSS, "Last record digest mismatch")));
return;
}
}
last_record_digest_map_->emplace(
std::make_pair(sequencing_information.sequencing_id(),
sequencing_information.generation_id()),
record_digest);
} }
std::move(processed_cb) std::move(processed_cb)
.Run(UploadRecord(encrypted_record.ValueOrDie() .Run(UploadRecord(sequencing_information.sequencing_id(),
.sequencing_information()
.sequencing_id(),
wrapped_record.record().data())); wrapped_record.record().data()));
} }
...@@ -176,6 +196,8 @@ class MockUploadClient : public StorageQueue::UploaderInterface { ...@@ -176,6 +196,8 @@ class MockUploadClient : public StorageQueue::UploaderInterface {
private: private:
base::Optional<uint64_t> generation_id_; base::Optional<uint64_t> generation_id_;
LastRecordDigestMap* const last_record_digest_map_;
Sequence test_upload_sequence_; Sequence test_upload_sequence_;
}; };
...@@ -218,7 +240,8 @@ class StorageQueueTest : public ::testing::TestWithParam<size_t> { ...@@ -218,7 +240,8 @@ class StorageQueueTest : public ::testing::TestWithParam<size_t> {
StatusOr<std::unique_ptr<StorageQueue::UploaderInterface>> StatusOr<std::unique_ptr<StorageQueue::UploaderInterface>>
BuildMockUploader() { BuildMockUploader() {
auto uploader = std::make_unique<MockUploadClient>(); auto uploader =
std::make_unique<MockUploadClient>(&last_record_digest_map_);
set_mock_uploader_expectations_.Call(uploader.get()); set_mock_uploader_expectations_.Call(uploader.get());
return uploader; return uploader;
} }
...@@ -250,6 +273,10 @@ class StorageQueueTest : public ::testing::TestWithParam<size_t> { ...@@ -250,6 +273,10 @@ class StorageQueueTest : public ::testing::TestWithParam<size_t> {
scoped_refptr<test::TestEncryptionModule> test_encryption_module_; scoped_refptr<test::TestEncryptionModule> test_encryption_module_;
scoped_refptr<StorageQueue> storage_queue_; scoped_refptr<StorageQueue> storage_queue_;
// Test-wide global mapping of seq number to record digest.
// Serves all MockUploadClients created by test fixture.
MockUploadClient::LastRecordDigestMap last_record_digest_map_;
::testing::MockFunction<void(MockUploadClient*)> ::testing::MockFunction<void(MockUploadClient*)>
set_mock_uploader_expectations_; set_mock_uploader_expectations_;
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
#include "chrome/browser/policy/messaging_layer/storage/storage.h" #include "chrome/browser/policy/messaging_layer/storage/storage.h"
#include <cstdint> #include <cstdint>
#include <tuple>
#include <utility> #include <utility>
#include <vector>
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/optional.h" #include "base/optional.h"
...@@ -78,7 +78,18 @@ class TestEvent { ...@@ -78,7 +78,18 @@ class TestEvent {
class MockUploadClient : public Storage::UploaderInterface { class MockUploadClient : public Storage::UploaderInterface {
public: public:
MockUploadClient() = default; // Mapping of (generation, seq number) to matching record digest. Whenever a
// record is uploaded and includes last record digest, this map should have
// that digest already recorded. Only the first record in a generation is
// uploaded without last record digest.
using LastRecordDigestMap =
std::map<std::tuple<Priority,
uint64_t /*generation */,
uint64_t /*sequencing number*/>,
std::string /*digest*/>;
explicit MockUploadClient(LastRecordDigestMap* last_record_digest_map)
: last_record_digest_map_(last_record_digest_map) {}
void ProcessRecord(StatusOr<EncryptedRecord> encrypted_record, void ProcessRecord(StatusOr<EncryptedRecord> encrypted_record,
base::OnceCallback<void(bool)> processed_cb) override { base::OnceCallback<void(bool)> processed_cb) override {
...@@ -91,25 +102,22 @@ class MockUploadClient : public Storage::UploaderInterface { ...@@ -91,25 +102,22 @@ class MockUploadClient : public Storage::UploaderInterface {
ASSERT_TRUE(wrapped_record.ParseFromString( ASSERT_TRUE(wrapped_record.ParseFromString(
encrypted_record.ValueOrDie().encrypted_wrapped_record())); encrypted_record.ValueOrDie().encrypted_wrapped_record()));
// Verify generation match. // Verify generation match.
const auto& sequencing_information =
encrypted_record.ValueOrDie().sequencing_information();
if (generation_id_.has_value() && if (generation_id_.has_value() &&
generation_id_.value() != encrypted_record.ValueOrDie() generation_id_.value() != sequencing_information.generation_id()) {
.sequencing_information()
.generation_id()) {
std::move(processed_cb) std::move(processed_cb)
.Run(UploadRecordFailure(Status( .Run(UploadRecordFailure(Status(
error::DATA_LOSS, error::DATA_LOSS,
base::StrCat({"Generation id mismatch, expected=", base::StrCat({"Generation id mismatch, expected=",
base::NumberToString(generation_id_.value()), base::NumberToString(generation_id_.value()),
" actual=", " actual=",
base::NumberToString(encrypted_record.ValueOrDie() base::NumberToString(
.sequencing_information() sequencing_information.generation_id())}))));
.generation_id())}))));
return; return;
} }
if (!generation_id_.has_value()) { if (!generation_id_.has_value()) {
generation_id_ = encrypted_record.ValueOrDie() generation_id_ = sequencing_information.generation_id();
.sequencing_information()
.generation_id();
} }
// Verify digest and its match. // Verify digest and its match.
...@@ -126,15 +134,30 @@ class MockUploadClient : public Storage::UploaderInterface { ...@@ -126,15 +134,30 @@ class MockUploadClient : public Storage::UploaderInterface {
Status(error::DATA_LOSS, "Record digest mismatch"))); Status(error::DATA_LOSS, "Record digest mismatch")));
return; return;
} }
if (wrapped_record.has_last_record_digest()) {
auto it = last_record_digest_map_->find(
std::make_tuple(sequencing_information.priority(),
sequencing_information.sequencing_id() - 1,
sequencing_information.generation_id()));
if (it == last_record_digest_map_->end() ||
it->second != wrapped_record.last_record_digest()) {
std::move(processed_cb)
.Run(UploadRecordFailure(
Status(error::DATA_LOSS, "Last record digest mismatch")));
return;
}
}
last_record_digest_map_->emplace(
std::make_tuple(sequencing_information.priority(),
sequencing_information.sequencing_id(),
sequencing_information.generation_id()),
record_digest);
} }
std::move(processed_cb) std::move(processed_cb)
.Run(UploadRecord( .Run(UploadRecord(sequencing_information.priority(),
encrypted_record.ValueOrDie().sequencing_information().priority(), sequencing_information.sequencing_id(),
encrypted_record.ValueOrDie() wrapped_record.record().data()));
.sequencing_information()
.sequencing_id(),
wrapped_record.record().data()));
} }
void Completed(Status status) override { UploadComplete(status); } void Completed(Status status) override { UploadComplete(status); }
...@@ -205,6 +228,8 @@ class MockUploadClient : public Storage::UploaderInterface { ...@@ -205,6 +228,8 @@ class MockUploadClient : public Storage::UploaderInterface {
private: private:
base::Optional<uint64_t> generation_id_; base::Optional<uint64_t> generation_id_;
LastRecordDigestMap* const last_record_digest_map_;
Sequence test_upload_sequence_; Sequence test_upload_sequence_;
}; };
...@@ -234,14 +259,15 @@ class StorageTest : public ::testing::Test { ...@@ -234,14 +259,15 @@ class StorageTest : public ::testing::Test {
StatusOr<std::unique_ptr<Storage::UploaderInterface>> BuildMockUploader( StatusOr<std::unique_ptr<Storage::UploaderInterface>> BuildMockUploader(
Priority priority) { Priority priority) {
auto uploader = std::make_unique<MockUploadClient>(); auto uploader =
std::make_unique<MockUploadClient>(&last_record_digest_map_);
set_mock_uploader_expectations_.Call(priority, uploader.get()); set_mock_uploader_expectations_.Call(priority, uploader.get());
return uploader; return uploader;
} }
Status WriteString(Priority priority, base::StringPiece data) { Status WriteString(Priority priority, base::StringPiece data) {
TestEvent<Status> w;
EXPECT_TRUE(storage_) << "Storage not created yet"; EXPECT_TRUE(storage_) << "Storage not created yet";
TestEvent<Status> w;
Record record; Record record;
record.set_data(std::string(data)); record.set_data(std::string(data));
record.set_destination(UPLOAD_EVENTS); record.set_destination(UPLOAD_EVENTS);
...@@ -266,6 +292,10 @@ class StorageTest : public ::testing::Test { ...@@ -266,6 +292,10 @@ class StorageTest : public ::testing::Test {
scoped_refptr<test::TestEncryptionModule> test_encryption_module_; scoped_refptr<test::TestEncryptionModule> test_encryption_module_;
scoped_refptr<Storage> storage_; scoped_refptr<Storage> storage_;
// Test-wide global mapping of seq number to record digest.
// Serves all MockUploadClients created by test fixture.
MockUploadClient::LastRecordDigestMap last_record_digest_map_;
::testing::MockFunction<void(Priority, MockUploadClient*)> ::testing::MockFunction<void(Priority, MockUploadClient*)>
set_mock_uploader_expectations_; set_mock_uploader_expectations_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment