Commit 88dca968 authored by Leonid Baraz's avatar Leonid Baraz Committed by Commit Bot

Refactor ReportingQueue.

Remove immediate Status returning, make fully asynchronous.

Bug: None
Change-Id: I6d8619e5a90641b645f6109a6a6e7db42e16aced
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2453489
Auto-Submit: Leonid Baraz <lbaraz@chromium.org>
Commit-Queue: Leonid Baraz <lbaraz@chromium.org>
Commit-Queue: Zach Trudo <zatrudo@google.com>
Reviewed-by: default avatarZach Trudo <zatrudo@google.com>
Cr-Commit-Position: refs/heads/master@{#814324}
parent d681aaee
...@@ -23,32 +23,32 @@ class MockReportQueue : public ReportQueue { ...@@ -23,32 +23,32 @@ class MockReportQueue : public ReportQueue {
MockReportQueue(); MockReportQueue();
~MockReportQueue() override; ~MockReportQueue() override;
Status Enqueue(base::StringPiece record, void Enqueue(base::StringPiece record,
EnqueueCallback callback) const override { EnqueueCallback callback) const override {
return StringPieceEnqueue_(record, std::move(callback)); StringPieceEnqueue_(record, std::move(callback));
} }
Status Enqueue(const base::Value& record, void Enqueue(const base::Value& record,
EnqueueCallback callback) const override { EnqueueCallback callback) const override {
return ValueEnqueue_(record, std::move(callback)); ValueEnqueue_(record, std::move(callback));
} }
Status Enqueue(google::protobuf::MessageLite* record, void Enqueue(google::protobuf::MessageLite* record,
EnqueueCallback callback) const override { EnqueueCallback callback) const override {
return MessageLiteEnqueue_(record, std::move(callback)); MessageLiteEnqueue_(record, std::move(callback));
} }
MOCK_METHOD(Status, MOCK_METHOD(void,
StringPieceEnqueue_, StringPieceEnqueue_,
(base::StringPiece record, EnqueueCallback callback), (base::StringPiece record, EnqueueCallback callback),
(const)); (const));
MOCK_METHOD(Status, MOCK_METHOD(void,
ValueEnqueue_, ValueEnqueue_,
(const base::Value& record, EnqueueCallback callback), (const base::Value& record, EnqueueCallback callback),
(const)); (const));
MOCK_METHOD(Status, MOCK_METHOD(void,
MessageLiteEnqueue_, MessageLiteEnqueue_,
(google::protobuf::MessageLite * record, (google::protobuf::MessageLite * record,
EnqueueCallback callback), EnqueueCallback callback),
......
...@@ -25,14 +25,45 @@ namespace reporting { ...@@ -25,14 +25,45 @@ namespace reporting {
// It ensures that all ReportQueues are created with the same storage settings. // It ensures that all ReportQueues are created with the same storage settings.
// //
// Example Usage: // Example Usage:
// Status SendMessage(google::protobuf::ImportantMessage important_message, // void SendMessage(google::protobuf::ImportantMessage important_message,
// base::OnceCallback<void(Status)> callback) { // reporting::ReportQueue::EnqueueCallback done_cb) {
// ASSIGN_OR_RETURN(std::unique_ptr<ReportQueueConfiguration> config, // // Create configuration.
// ReportQueueConfiguration::Create(...)); // auto config_result = reporting::ReportQueueConfiguration::Create(...);
// ASSIGN_OR_RETURN(std::unique_ptr<ReportQueue> report_queue, // // Bail out if configuration failed to create.
// ReportingClient::CreateReportQueue(config)); // if (!config_result.ok()) {
// return report_queue->Enqueue(important_message, callback); // std::move(done_cb).Run(config_result.status());
// return;
// }
// // Asynchronously create ReportingQueue.
// base::ThreadPool::PostTask(
// FROM_HERE,
// base::BindOnce(
// [](google::protobuf::ImportantMessage important_message,
// reporting::ReportQueue::EnqueueCallback done_cb,
// std::unique_ptr<reporting::ReportQueueConfiguration> config) {
// // Asynchronously create ReportingQueue.
// reporting::ReportingClient::CreateReportQueue(
// std::move(config),
// base::BindOnce(
// [](base::StringPiece data,
// reporting::ReportQueue::EnqueueCallback done_cb,
// reporting::StatusOr<std::unique_ptr<
// reporting::ReportQueue>> report_queue_result) {
// // Bail out if queue failed to create.
// if (!report_queue_result.ok()) {
// std::move(done_cb).Run(report_queue_result.status());
// return;
// }
// // Queue created successfully, enqueue the message.
// report_queue_result.ValueOrDie()->Enqueue(
// important_message, std::move(done_cb));
// },
// important_message, std::move(done_cb)));
// },
// important_message, std::move(done_cb),
// std::move(config_result.ValueOrDie())))
// } // }
class ReportingClient { class ReportingClient {
public: public:
struct Configuration { struct Configuration {
......
...@@ -34,23 +34,43 @@ using policy::DMToken; ...@@ -34,23 +34,43 @@ using policy::DMToken;
using reporting::Destination; using reporting::Destination;
using reporting::Priority; using reporting::Priority;
class TestCallbackWaiter { // Usage (in tests only):
//
// TestEvent<ResType> e;
// ... Do some async work passing e.cb() as a completion callback of
// base::OnceCallback<void(ResType* res)> type which also may perform some
// other action specified by |done| callback provided by the caller.
// ... = e.result(); // Will wait for e.cb() to be called and return the
// collected result.
//
template <typename ResType>
class TestEvent {
public: public:
TestCallbackWaiter() : run_loop_(std::make_unique<base::RunLoop>()) {} TestEvent() : run_loop_(std::make_unique<base::RunLoop>()) {}
~TestEvent() = default;
virtual void Signal() { run_loop_->Quit(); } TestEvent(const TestEvent& other) = delete;
TestEvent& operator=(const TestEvent& other) = delete;
ResType result() {
run_loop_->Run();
return std::forward<ResType>(result_);
}
void Wait() { run_loop_->Run(); } // Completion callback to hand over to the processing method.
void Reset() { base::OnceCallback<void(ResType res)> cb() {
run_loop_.reset(); return base::BindOnce(
run_loop_ = std::make_unique<base::RunLoop>(); [](base::RunLoop* run_loop, ResType* result, ResType res) {
*result = std::forward<ResType>(res);
run_loop->Quit();
},
base::Unretained(run_loop_.get()), base::Unretained(&result_));
} }
protected: private:
std::unique_ptr<base::RunLoop> run_loop_; std::unique_ptr<base::RunLoop> run_loop_;
ResType result_;
}; };
class ReportingClientTest : public testing::Test { class ReportClientTest : public testing::Test {
public: public:
void SetUp() override { void SetUp() override {
#ifdef OS_CHROMEOS #ifdef OS_CHROMEOS
...@@ -99,67 +119,37 @@ class ReportingClientTest : public testing::Test { ...@@ -99,67 +119,37 @@ class ReportingClientTest : public testing::Test {
}; };
// Tests that a ReportQueue can be created using the ReportingClient. // Tests that a ReportQueue can be created using the ReportingClient.
TEST_F(ReportingClientTest, CreatesReportQueue) { TEST_F(ReportClientTest, CreatesReportQueue) {
auto config_result = ReportQueueConfiguration::Create( auto config_result = ReportQueueConfiguration::Create(
dm_token_, destination_, priority_, policy_checker_callback_); dm_token_, destination_, priority_, policy_checker_callback_);
ASSERT_OK(config_result); ASSERT_OK(config_result);
TestCallbackWaiter waiter; TestEvent<StatusOr<std::unique_ptr<ReportQueue>>> a;
StatusOr<std::unique_ptr<ReportQueue>> result;
auto create_report_queue_cb = base::BindOnce(
[](TestCallbackWaiter* waiter,
StatusOr<std::unique_ptr<ReportQueue>>* result,
StatusOr<std::unique_ptr<ReportQueue>> create_result) {
*result = std::move(create_result);
waiter->Signal();
},
&waiter, &result);
ReportingClient::CreateReportQueue(std::move(config_result.ValueOrDie()), ReportingClient::CreateReportQueue(std::move(config_result.ValueOrDie()),
std::move(create_report_queue_cb)); a.cb());
ASSERT_OK(a.result());
waiter.Wait();
waiter.Reset();
ASSERT_OK(result);
} }
// Ensures that created ReportQueues are actually different. // Ensures that created ReportQueues are actually different.
TEST_F(ReportingClientTest, CreatesTwoDifferentReportQueues) { TEST_F(ReportClientTest, CreatesTwoDifferentReportQueues) {
auto config_result = ReportQueueConfiguration::Create( auto config_result = ReportQueueConfiguration::Create(
dm_token_, destination_, priority_, policy_checker_callback_); dm_token_, destination_, priority_, policy_checker_callback_);
EXPECT_TRUE(config_result.ok()); EXPECT_TRUE(config_result.ok());
TestCallbackWaiter waiter; TestEvent<StatusOr<std::unique_ptr<ReportQueue>>> a1;
StatusOr<std::unique_ptr<ReportQueue>> result;
auto create_report_queue_cb = base::BindOnce(
[](TestCallbackWaiter* waiter,
StatusOr<std::unique_ptr<ReportQueue>>* result,
StatusOr<std::unique_ptr<ReportQueue>> create_result) {
*result = std::move(create_result);
waiter->Signal();
},
&waiter, &result);
ReportingClient::CreateReportQueue(std::move(config_result.ValueOrDie()), ReportingClient::CreateReportQueue(std::move(config_result.ValueOrDie()),
std::move(create_report_queue_cb)); a1.cb());
waiter.Wait(); auto result = a1.result();
waiter.Reset();
ASSERT_OK(result); ASSERT_OK(result);
auto report_queue_1 = std::move(result.ValueOrDie()); auto report_queue_1 = std::move(result.ValueOrDie());
TestEvent<StatusOr<std::unique_ptr<ReportQueue>>> a2;
config_result = ReportQueueConfiguration::Create( config_result = ReportQueueConfiguration::Create(
dm_token_, destination_, priority_, policy_checker_callback_); dm_token_, destination_, priority_, policy_checker_callback_);
create_report_queue_cb = base::BindOnce(
[](TestCallbackWaiter* waiter,
StatusOr<std::unique_ptr<ReportQueue>>* result,
StatusOr<std::unique_ptr<ReportQueue>> create_result) {
*result = std::move(create_result);
waiter->Signal();
},
&waiter, &result);
ReportingClient::CreateReportQueue(std::move(config_result.ValueOrDie()), ReportingClient::CreateReportQueue(std::move(config_result.ValueOrDie()),
std::move(create_report_queue_cb)); a2.cb());
waiter.Wait(); result = a2.result();
ASSERT_OK(result); ASSERT_OK(result);
auto report_queue_2 = std::move(result.ValueOrDie()); auto report_queue_2 = std::move(result.ValueOrDie());
EXPECT_NE(report_queue_1.get(), report_queue_2.get()); EXPECT_NE(report_queue_1.get(), report_queue_2.get());
......
...@@ -50,42 +50,47 @@ ReportQueue::ReportQueue(std::unique_ptr<ReportQueueConfiguration> config, ...@@ -50,42 +50,47 @@ ReportQueue::ReportQueue(std::unique_ptr<ReportQueueConfiguration> config,
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
} }
Status ReportQueue::Enqueue(base::StringPiece record, void ReportQueue::Enqueue(base::StringPiece record,
EnqueueCallback callback) const { EnqueueCallback callback) const {
return AddRecord(record, std::move(callback)); AddRecord(record, std::move(callback));
} }
Status ReportQueue::Enqueue(const base::Value& record, void ReportQueue::Enqueue(const base::Value& record,
EnqueueCallback callback) const { EnqueueCallback callback) const {
std::string json_record; std::string json_record;
if (!base::JSONWriter::Write(record, &json_record)) { if (!base::JSONWriter::Write(record, &json_record)) {
return Status(error::INVALID_ARGUMENT, std::move(callback).Run(
"Provided record was not convertable to a std::string"); Status(error::INVALID_ARGUMENT,
"Provided record was not convertable to a std::string"));
return;
} }
return AddRecord(json_record, std::move(callback)); AddRecord(json_record, std::move(callback));
} }
Status ReportQueue::Enqueue(google::protobuf::MessageLite* record, void ReportQueue::Enqueue(google::protobuf::MessageLite* record,
EnqueueCallback callback) const { EnqueueCallback callback) const {
std::string protobuf_record; std::string protobuf_record;
if (!record->SerializeToString(&protobuf_record)) { if (!record->SerializeToString(&protobuf_record)) {
return Status(error::INVALID_ARGUMENT, std::move(callback).Run(
"Unabled to serialize record to string. Most likely due to " Status(error::INVALID_ARGUMENT,
"unset required fields."); "Unabled to serialize record to string. Most likely due to "
"unset required fields."));
return;
} }
return AddRecord(protobuf_record, std::move(callback)); return AddRecord(protobuf_record, std::move(callback));
} }
Status ReportQueue::AddRecord(base::StringPiece record, void ReportQueue::AddRecord(base::StringPiece record,
EnqueueCallback callback) const { EnqueueCallback callback) const {
RETURN_IF_ERROR(config_->CheckPolicy()); const Status status = config_->CheckPolicy();
if (!sequenced_task_runner_->PostTask( if (!status.ok()) {
FROM_HERE, base::BindOnce(&ReportQueue::SendRecordToStorage, std::move(callback).Run(status);
base::Unretained(this), std::string(record), return;
std::move(callback)))) {
return Status(error::INTERNAL, "Failed to post the record for processing.");
} }
return Status::StatusOK(); sequenced_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ReportQueue::SendRecordToStorage, base::Unretained(this),
std::string(record), std::move(callback)));
} }
void ReportQueue::SendRecordToStorage(base::StringPiece record_data, void ReportQueue::SendRecordToStorage(base::StringPiece record_data,
......
...@@ -60,25 +60,25 @@ class ReportQueue { ...@@ -60,25 +60,25 @@ class ReportQueue {
// UPLOAD_EVENTS : UploadEventsRequest // UPLOAD_EVENTS : UploadEventsRequest
// //
// |record| will be sent as a string with no conversion. // |record| will be sent as a string with no conversion.
virtual Status Enqueue(base::StringPiece record, virtual void Enqueue(base::StringPiece record,
EnqueueCallback callback) const; EnqueueCallback callback) const;
// |record| will be converted to a JSON string with base::JsonWriter::Write. // |record| will be converted to a JSON string with base::JsonWriter::Write.
virtual Status Enqueue(const base::Value& record, virtual void Enqueue(const base::Value& record,
EnqueueCallback callback) const; EnqueueCallback callback) const;
// |record| will be converted to a string with SerializeToString(). The // |record| will be converted to a string with SerializeToString(). The
// handler is responsible for converting the record back to a proto with a // handler is responsible for converting the record back to a proto with a
// ParseFromString() call. // ParseFromString() call.
virtual Status Enqueue(google::protobuf::MessageLite* record, virtual void Enqueue(google::protobuf::MessageLite* record,
EnqueueCallback callback) const; EnqueueCallback callback) const;
protected: protected:
ReportQueue(std::unique_ptr<ReportQueueConfiguration> config, ReportQueue(std::unique_ptr<ReportQueueConfiguration> config,
scoped_refptr<StorageModule> storage); scoped_refptr<StorageModule> storage);
private: private:
Status AddRecord(base::StringPiece record, EnqueueCallback callback) const; void AddRecord(base::StringPiece record, EnqueueCallback callback) const;
void SendRecordToStorage(base::StringPiece record, void SendRecordToStorage(base::StringPiece record,
EnqueueCallback callback) const; EnqueueCallback callback) const;
......
...@@ -44,52 +44,35 @@ namespace { ...@@ -44,52 +44,35 @@ namespace {
// //
// TestEvent<ResType> e; // TestEvent<ResType> e;
// ... Do some async work passing e.cb() as a completion callback of // ... Do some async work passing e.cb() as a completion callback of
// base::OnceCallback<void(ResType* res)> type which also may perform // base::OnceCallback<void(ResType* res)> type which also may perform some
// some other action specified by |done| callback provided by the caller. // other action specified by |done| callback provided by the caller.
// ... = e.result(); // Will wait for e.cb() to be called and return the // ... = e.result(); // Will wait for e.cb() to be called and return the
// // collected result. // collected result.
//
// Or, when the callback is not expected to be invoked:
//
// TestEvent<ResType> e(/*expected_to_complete=*/false);
// ... Start work passing e.cb() as a completion callback,
// which will not happen.
// //
template <typename ResType> template <typename ResType>
class TestEvent { class TestEvent {
public: public:
explicit TestEvent(bool expected_to_complete = true) TestEvent() : run_loop_(std::make_unique<base::RunLoop>()) {}
: expected_to_complete_(expected_to_complete), ~TestEvent() = default;
completed_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
~TestEvent() {
if (expected_to_complete_) {
EXPECT_TRUE(completed_.IsSignaled()) << "Not responded";
} else {
EXPECT_FALSE(completed_.IsSignaled()) << "Responded";
}
}
TestEvent(const TestEvent& other) = delete; TestEvent(const TestEvent& other) = delete;
TestEvent& operator=(const TestEvent& other) = delete; TestEvent& operator=(const TestEvent& other) = delete;
ResType result() { ResType result() {
completed_.Wait(); run_loop_->Run();
return std::forward<ResType>(result_); return std::forward<ResType>(result_);
} }
// Completion callback to hand over to the processing method. // Completion callback to hand over to the processing method.
base::OnceCallback<void(ResType res)> cb() { base::OnceCallback<void(ResType res)> cb() {
DCHECK(!completed_.IsSignaled());
return base::BindOnce( return base::BindOnce(
[](base::WaitableEvent* completed, ResType* result, ResType res) { [](base::RunLoop* run_loop, ResType* result, ResType res) {
*result = std::forward<ResType>(res); *result = std::forward<ResType>(res);
completed->Signal(); run_loop->Quit();
}, },
base::Unretained(&completed_), base::Unretained(&result_)); base::Unretained(run_loop_.get()), base::Unretained(&result_));
} }
private: private:
bool expected_to_complete_; std::unique_ptr<base::RunLoop> run_loop_;
base::WaitableEvent completed_;
ResType result_; ResType result_;
}; };
...@@ -152,12 +135,9 @@ class ReportQueueTest : public testing::Test { ...@@ -152,12 +135,9 @@ class ReportQueueTest : public testing::Test {
TEST_F(ReportQueueTest, SuccessfulStringRecord) { TEST_F(ReportQueueTest, SuccessfulStringRecord) {
constexpr char kTestString[] = "El-Chupacabra"; constexpr char kTestString[] = "El-Chupacabra";
TestEvent<Status> a; TestEvent<Status> a;
Status status = report_queue_->Enqueue(kTestString, a.cb()); report_queue_->Enqueue(kTestString, a.cb());
ASSERT_OK(status);
EXPECT_OK(a.result()); EXPECT_OK(a.result());
EXPECT_EQ(test_storage_module()->priority(), priority_); EXPECT_EQ(test_storage_module()->priority(), priority_);
EXPECT_EQ(test_storage_module()->record().data(), kTestString); EXPECT_EQ(test_storage_module()->record().data(), kTestString);
} }
...@@ -169,8 +149,7 @@ TEST_F(ReportQueueTest, SuccessfulBaseValueRecord) { ...@@ -169,8 +149,7 @@ TEST_F(ReportQueueTest, SuccessfulBaseValueRecord) {
base::Value test_dict(base::Value::Type::DICTIONARY); base::Value test_dict(base::Value::Type::DICTIONARY);
test_dict.SetStringKey(kTestKey, kTestValue); test_dict.SetStringKey(kTestKey, kTestValue);
TestEvent<Status> a; TestEvent<Status> a;
Status status = report_queue_->Enqueue(test_dict, a.cb()); report_queue_->Enqueue(test_dict, a.cb());
ASSERT_OK(status);
EXPECT_OK(a.result()); EXPECT_OK(a.result());
EXPECT_EQ(test_storage_module()->priority(), priority_); EXPECT_EQ(test_storage_module()->priority(), priority_);
...@@ -187,8 +166,7 @@ TEST_F(ReportQueueTest, SuccessfulProtoRecord) { ...@@ -187,8 +166,7 @@ TEST_F(ReportQueueTest, SuccessfulProtoRecord) {
reporting::test::TestMessage test_message; reporting::test::TestMessage test_message;
test_message.set_test("TEST_MESSAGE"); test_message.set_test("TEST_MESSAGE");
TestEvent<Status> a; TestEvent<Status> a;
Status status = report_queue_->Enqueue(&test_message, a.cb()); report_queue_->Enqueue(&test_message, a.cb());
ASSERT_OK(status);
EXPECT_OK(a.result()); EXPECT_OK(a.result());
EXPECT_EQ(test_storage_module()->priority(), priority_); EXPECT_EQ(test_storage_module()->priority(), priority_);
...@@ -212,9 +190,8 @@ TEST_F(ReportQueueTest, CallSuccessCallbackFailure) { ...@@ -212,9 +190,8 @@ TEST_F(ReportQueueTest, CallSuccessCallbackFailure) {
reporting::test::TestMessage test_message; reporting::test::TestMessage test_message;
test_message.set_test("TEST_MESSAGE"); test_message.set_test("TEST_MESSAGE");
TestEvent<Status> a; TestEvent<Status> a;
Status status = report_queue_->Enqueue(&test_message, a.cb()); report_queue_->Enqueue(&test_message, a.cb());
ASSERT_OK(status); const auto result = a.result();
auto result = a.result();
EXPECT_FALSE(result.ok()); EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNKNOWN); EXPECT_EQ(result.error_code(), error::UNKNOWN);
} }
...@@ -223,10 +200,11 @@ TEST_F(ReportQueueTest, EnqueueStringFailsOnPolicy) { ...@@ -223,10 +200,11 @@ TEST_F(ReportQueueTest, EnqueueStringFailsOnPolicy) {
EXPECT_CALL(*this, MockedPolicyCheck) EXPECT_CALL(*this, MockedPolicyCheck)
.WillOnce(Return(Status(error::UNAUTHENTICATED, "Failing for tests"))); .WillOnce(Return(Status(error::UNAUTHENTICATED, "Failing for tests")));
constexpr char kTestString[] = "El-Chupacabra"; constexpr char kTestString[] = "El-Chupacabra";
TestEvent<Status> a(/*expected_to_complete=*/false); TestEvent<Status> a;
Status status = report_queue_->Enqueue(kTestString, a.cb()); report_queue_->Enqueue(kTestString, a.cb());
EXPECT_FALSE(status.ok()); const auto result = a.result();
EXPECT_EQ(status.error_code(), error::UNAUTHENTICATED); EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNAUTHENTICATED);
} }
TEST_F(ReportQueueTest, EnqueueProtoFailsOnPolicy) { TEST_F(ReportQueueTest, EnqueueProtoFailsOnPolicy) {
...@@ -234,10 +212,11 @@ TEST_F(ReportQueueTest, EnqueueProtoFailsOnPolicy) { ...@@ -234,10 +212,11 @@ TEST_F(ReportQueueTest, EnqueueProtoFailsOnPolicy) {
.WillOnce(Return(Status(error::UNAUTHENTICATED, "Failing for tests"))); .WillOnce(Return(Status(error::UNAUTHENTICATED, "Failing for tests")));
reporting::test::TestMessage test_message; reporting::test::TestMessage test_message;
test_message.set_test("TEST_MESSAGE"); test_message.set_test("TEST_MESSAGE");
TestEvent<Status> a(/*expected_to_complete=*/false); TestEvent<Status> a;
Status status = report_queue_->Enqueue(&test_message, a.cb()); report_queue_->Enqueue(&test_message, a.cb());
EXPECT_FALSE(status.ok()); const auto result = a.result();
EXPECT_EQ(status.error_code(), error::UNAUTHENTICATED); EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNAUTHENTICATED);
} }
TEST_F(ReportQueueTest, EnqueueValueFailsOnPolicy) { TEST_F(ReportQueueTest, EnqueueValueFailsOnPolicy) {
...@@ -247,10 +226,11 @@ TEST_F(ReportQueueTest, EnqueueValueFailsOnPolicy) { ...@@ -247,10 +226,11 @@ TEST_F(ReportQueueTest, EnqueueValueFailsOnPolicy) {
constexpr char kTestValue[] = "TEST_VALUE"; constexpr char kTestValue[] = "TEST_VALUE";
base::Value test_dict(base::Value::Type::DICTIONARY); base::Value test_dict(base::Value::Type::DICTIONARY);
test_dict.SetStringKey(kTestKey, kTestValue); test_dict.SetStringKey(kTestKey, kTestValue);
TestEvent<Status> a(/*expected_to_complete=*/false); TestEvent<Status> a;
Status status = report_queue_->Enqueue(test_dict, a.cb()); report_queue_->Enqueue(test_dict, a.cb());
EXPECT_FALSE(status.ok()); const auto result = a.result();
EXPECT_EQ(status.error_code(), error::UNAUTHENTICATED); EXPECT_FALSE(result.ok());
EXPECT_EQ(result.error_code(), error::UNAUTHENTICATED);
} }
} // namespace } // namespace
......
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