blob: 0e818a1010f8bb50c52eaa9e245ac532d51a3708 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "missive/util/status_macros.h"
#include <memory>
#include <stdio.h>
#include <utility>
#include <base/bind.h>
#include <base/callback.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "missive/util/status.h"
#include "missive/util/statusor.h"
namespace reporting {
namespace {
Status StatusTestFunction(bool fail) {
if (fail) {
return Status(error::INVALID_ARGUMENT, "Fail was true.");
}
return Status::StatusOK();
}
Status ReturnIfErrorWrapperFunction(bool fail) {
RETURN_IF_ERROR(StatusTestFunction(fail));
// Return error here to make sure that we aren't just returning the OK from
// StatusTestFunction.
return Status(error::INTERNAL, "Returning Internal Error");
}
// RETURN_IF_ERROR macro actually returns on a non-OK status.
TEST(StatusMacros, ReturnsOnError) {
Status test_status = ReturnIfErrorWrapperFunction(/*fail=*/true);
EXPECT_FALSE(test_status.ok());
EXPECT_EQ(test_status.code(), error::INVALID_ARGUMENT);
}
// RETURN_IF_ERROR macro continues on an OK status.
TEST(StatusMacros, ReturnIfErrorContinuesOnOk) {
Status test_status = ReturnIfErrorWrapperFunction(/*fail=*/false);
EXPECT_FALSE(test_status.ok());
EXPECT_EQ(test_status.code(), error::INTERNAL);
}
// Function to test StatusOr macros.
template <typename T>
StatusOr<T> StatusOrTestFunction(bool fail, T return_value) {
if (fail) {
return Status(error::INVALID_ARGUMENT, "Test failure requested.");
}
return std::forward<T>(return_value);
}
// Function for testing ASSIGN_OR_RETURN.
template <typename T>
StatusOr<T> AssignOrReturnWrapperFunction(bool fail, T return_value) {
ASSIGN_OR_RETURN(T value,
StatusOrTestFunction(fail, std::forward<T>(return_value)));
return std::forward<T>(value);
}
// ASSIGN_OR_RETURN actually assigns the value if the status is OK.
TEST(StatusMacros, AssignOnOk) {
constexpr int kReturnValue = 42;
StatusOr<int> status_or_result =
AssignOrReturnWrapperFunction(/*fail=*/false, kReturnValue);
ASSERT_TRUE(status_or_result.ok());
EXPECT_EQ(status_or_result.ValueOrDie(), kReturnValue);
}
// ASSIGN_OR_RETURN actually returns on a non-OK status.
TEST(StatusMacros, ReturnOnError) {
StatusOr<int> status_or_result =
AssignOrReturnWrapperFunction(/*fail=*/true, /*return_value=*/0);
EXPECT_FALSE(status_or_result.ok());
}
StatusOr<int> MultipleAssignOrReturnWrapperFunction(int return_value) {
bool fail = false;
int value;
ASSIGN_OR_RETURN(value, StatusOrTestFunction(fail, return_value));
ASSIGN_OR_RETURN(value, StatusOrTestFunction(fail, return_value));
ASSIGN_OR_RETURN(value, StatusOrTestFunction(fail, return_value));
ASSIGN_OR_RETURN(value, StatusOrTestFunction(fail, return_value));
return value;
}
// ASSIGN_OR_RETURN compiles when used multiple times.
TEST(StatusMacros, MultipleAssignsSucceed) {
constexpr int kReturnValue = 42;
StatusOr<int> status_or_result =
MultipleAssignOrReturnWrapperFunction(kReturnValue);
ASSERT_TRUE(status_or_result.ok());
EXPECT_EQ(status_or_result.ValueOrDie(), kReturnValue);
}
// ASSIGN_OR_RETURN actually moves the value if the status is OK.
TEST(StatusMacros, AssignOnOkMoveable) {
constexpr int kReturnValue = 42;
StatusOr<std::unique_ptr<int>> status_or_result =
AssignOrReturnWrapperFunction(/*fail=*/false,
std::make_unique<int>(kReturnValue));
ASSERT_TRUE(status_or_result.ok());
EXPECT_EQ(*status_or_result.ValueOrDie(), kReturnValue);
}
// ASSIGN_OR_RETURN actually returns on a non-OK status.
TEST(StatusMacros, ReturnOnErrorMoveable) {
StatusOr<std::unique_ptr<int>> status_or_result =
AssignOrReturnWrapperFunction(/*fail=*/true,
std::make_unique<int>(/*return_value=*/0));
EXPECT_FALSE(status_or_result.ok());
}
// ASSIGN_OR_ONCE_CALLBACK_AND_RETURN testing
void AssignOrOnceCallbackWrapperFunction(
bool fail, base::OnceCallback<void(Status)> callback) {
constexpr int kReturnValue = 42;
int value;
ASSIGN_OR_ONCE_CALLBACK_AND_RETURN(value, callback,
StatusOrTestFunction(fail, kReturnValue));
ASSERT_EQ(value, kReturnValue);
}
class CallbackTestClass {
public:
explicit CallbackTestClass(Status test_status) : test_status_(test_status) {}
void AssignInCallback(Status status) {
num_callback_invocations_++;
test_status_ = status;
}
int num_callback_invocations() { return num_callback_invocations_; }
Status status() { return test_status_; }
private:
Status test_status_;
int num_callback_invocations_ = 0;
};
// ASSIGN_OR_ONCE_CALLBACK_AND_RETURN assigns on OK error.
TEST(StatusMacros, OnceCallbackAssignOnOk) {
CallbackTestClass callback_test_class(Status::StatusOK());
base::OnceCallback<void(Status)> callback =
base::BindOnce(&CallbackTestClass::AssignInCallback,
base::Unretained(&callback_test_class));
AssignOrOnceCallbackWrapperFunction(/*fail=*/false, std::move(callback));
constexpr int kExpectedNumberOfCallbackInvocations = 0;
EXPECT_EQ(callback_test_class.num_callback_invocations(),
kExpectedNumberOfCallbackInvocations);
EXPECT_EQ(callback_test_class.status(), Status::StatusOK());
}
// ASSIGN_OR_ONCE_CALLBACK_AND_RETURN calls the callback and returns on non-OK
// status.
TEST(StatusMacros, OnceCallbackAndReturnOnError) {
CallbackTestClass callback_test_class(Status::StatusOK());
base::OnceCallback<void(Status)> callback =
base::BindOnce(&CallbackTestClass::AssignInCallback,
base::Unretained(&callback_test_class));
AssignOrOnceCallbackWrapperFunction(/*fail=*/true, std::move(callback));
constexpr int kExpectedNumberOfCallbackInvocations = 1;
EXPECT_EQ(callback_test_class.num_callback_invocations(),
kExpectedNumberOfCallbackInvocations);
EXPECT_EQ(callback_test_class.status().code(), error::INVALID_ARGUMENT);
}
void MultipleAssignOrOnceCallbackWrapperFunction(
base::OnceCallback<void(Status)> callback) {
constexpr int kReturnValue = 42;
constexpr bool kFail = false;
int value;
ASSIGN_OR_ONCE_CALLBACK_AND_RETURN(value, callback,
StatusOrTestFunction(kFail, kReturnValue));
ASSIGN_OR_ONCE_CALLBACK_AND_RETURN(value, callback,
StatusOrTestFunction(kFail, kReturnValue));
ASSIGN_OR_ONCE_CALLBACK_AND_RETURN(value, callback,
StatusOrTestFunction(kFail, kReturnValue));
ASSERT_EQ(value, kReturnValue);
}
// ASSIGN_OR_ONCE_CALLBACK_AND_RETURN can be used multiple times in a function.
TEST(StatusMacros, MultipleAssignOrOnceCallbackCompletes) {
CallbackTestClass callback_test_class(Status::StatusOK());
base::OnceCallback<void(Status)> callback =
base::BindOnce(&CallbackTestClass::AssignInCallback,
base::Unretained(&callback_test_class));
MultipleAssignOrOnceCallbackWrapperFunction(std::move(callback));
constexpr int kExpectedNumberOfCallbackInvocations = 0;
EXPECT_EQ(callback_test_class.num_callback_invocations(),
kExpectedNumberOfCallbackInvocations);
EXPECT_EQ(callback_test_class.status(), Status::StatusOK());
}
} // namespace
} // namespace reporting