// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/test/rectify_callback.h"

#include <sstream>
#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/test/bind.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

// Test rectifying functions with no return value.

#define CALLBACK_TESTS_VOID(TestNameSuffix, Signature, LambdaToRectify, ...)  \
  TEST(RectifyCallbackTest, RepeatingCallbackSignatureVoid##TestNameSuffix) { \
    auto cb = RectifyCallback<Signature>(BindRepeating(LambdaToRectify));     \
    cb.Run(__VA_ARGS__);                                                      \
  }                                                                           \
  TEST(RectifyCallbackTest, RepeatingCallbackTypeVoid##TestNameSuffix) {      \
    auto cb = RectifyCallback<RepeatingCallback<Signature>>(                  \
        BindRepeating(LambdaToRectify));                                      \
    cb.Run(__VA_ARGS__);                                                      \
  }                                                                           \
  TEST(RectifyCallbackTest, RepeatingToOnceVoid##TestNameSuffix) {            \
    auto cb = RectifyCallback<OnceCallback<Signature>>(                       \
        BindRepeating(LambdaToRectify));                                      \
    std::move(cb).Run(__VA_ARGS__);                                           \
  }                                                                           \
  TEST(RectifyCallbackTest, OnceCallbackSignatureVoid##TestNameSuffix) {      \
    auto cb = RectifyCallback<Signature>(BindOnce(LambdaToRectify));          \
    std::move(cb).Run(__VA_ARGS__);                                           \
  }                                                                           \
  TEST(RectifyCallbackTest, OnceCallbackTypeVoid##TestNameSuffix) {           \
    auto cb =                                                                 \
        RectifyCallback<OnceCallback<Signature>>(BindOnce(LambdaToRectify));  \
    std::move(cb).Run(__VA_ARGS__);                                           \
  }

CALLBACK_TESTS_VOID(NoArgsSameSignature, void(), []() {})

CALLBACK_TESTS_VOID(
    OneArgRemoveOneArg,
    void(int),
    []() {},
    1)

CALLBACK_TESTS_VOID(
    TwoArgsRemoveTwoArgs,
    void(float, int),
    []() {},
    0.25f,
    1)

CALLBACK_TESTS_VOID(
    OneArgSameSignature,
    void(int),
    [](int x) { EXPECT_EQ(1, x); },
    1)

CALLBACK_TESTS_VOID(
    TwoArgsRemoveOneArg,
    void(float, int),
    [](int x) { EXPECT_EQ(1, x); },
    0.25f,
    1)

CALLBACK_TESTS_VOID(
    ThreeArgsRemoveTwoArgs,
    void(bool, float, int),
    [](int x) { EXPECT_EQ(1, x); },
    true,
    0.25f,
    1)

// Test rectifying functions that do return a value.

#define CALLBACK_TESTS_RETURN(TestNameSuffix, Signature, LambdaToRectify,    \
                              ExpectedReturn, ...)                           \
  TEST(RectifyCallbackTest,                                                  \
       RepeatingCallbackSignatureReturn##TestNameSuffix) {                   \
    auto cb = RectifyCallback<Signature>(BindRepeating(LambdaToRectify));    \
    EXPECT_EQ(ExpectedReturn, cb.Run(__VA_ARGS__));                          \
  }                                                                          \
  TEST(RectifyCallbackTest, RepeatingCallbackTypeReturn##TestNameSuffix) {   \
    auto cb = RectifyCallback<RepeatingCallback<Signature>>(                 \
        BindRepeating(LambdaToRectify));                                     \
    EXPECT_EQ(ExpectedReturn, cb.Run(__VA_ARGS__));                          \
  }                                                                          \
  TEST(RectifyCallbackTest, RepeatingToOnceReturn##TestNameSuffix) {         \
    auto cb = RectifyCallback<OnceCallback<Signature>>(                      \
        BindRepeating(LambdaToRectify));                                     \
    EXPECT_EQ(ExpectedReturn, std::move(cb).Run(__VA_ARGS__));               \
  }                                                                          \
  TEST(RectifyCallbackTest, OnceCallbackSignatureReturn##TestNameSuffix) {   \
    auto cb = RectifyCallback<Signature>(BindOnce(LambdaToRectify));         \
    EXPECT_EQ(ExpectedReturn, std::move(cb).Run(__VA_ARGS__));               \
  }                                                                          \
  TEST(RectifyCallbackTest, OnceCallbackTypeReturn##TestNameSuffix) {        \
    auto cb =                                                                \
        RectifyCallback<OnceCallback<Signature>>(BindOnce(LambdaToRectify)); \
    EXPECT_EQ(ExpectedReturn, std::move(cb).Run(__VA_ARGS__));               \
  }

CALLBACK_TESTS_RETURN(
    NoArgsSameSignature,
    int(),
    []() { return 2; },
    2)

CALLBACK_TESTS_RETURN(
    OneArgRemoveOneArg,
    int(int),
    []() { return 2; },
    2,
    1)

CALLBACK_TESTS_RETURN(
    TwoArgsRemoveTwoArgs,
    int(float, int),
    []() { return 2; },
    2,
    0.25f,
    1)

CALLBACK_TESTS_RETURN(
    OneArgSameSignature,
    int(int),
    [](int x) { return x; },
    2,
    2)

CALLBACK_TESTS_RETURN(
    TwoArgsRemoveOneArg,
    int(float, int),
    [](int x) { return x; },
    2,
    0.25f,
    2)

CALLBACK_TESTS_RETURN(
    ThreeArgsRemoveTwoArgs,
    int(bool, float, int),
    [](int x) { return x; },
    2,
    true,
    0.25f,
    2)

// Test proper forwarding of move-only arguments.

CALLBACK_TESTS_RETURN(
    DiscardsMoveOnlyArgs,
    bool(int, std::unique_ptr<int>),
    []() { return true; },
    true,
    2,
    std::make_unique<int>(3))

CALLBACK_TESTS_RETURN(
    UsesMoveOnlyArg,
    int(float, int, std::unique_ptr<int>),
    [](std::unique_ptr<int> p) { return *p; },
    3,
    0.25f,
    2,
    std::make_unique<int>(3))

// Test rectifying DoNothing().

#define CALLBACK_TESTS_DO_NOTHING(TestNameSuffix, Signature, ...)         \
  TEST(RectifyCallbackTest, SignatureDoNothing##TestNameSuffix) {         \
    auto cb = RectifyCallback<Signature>(DoNothing());                    \
    cb.Run(__VA_ARGS__);                                                  \
  }                                                                       \
  TEST(RectifyCallbackTest, RepeatingCallbackDoNothing##TestNameSuffix) { \
    auto cb = RectifyCallback<RepeatingCallback<Signature>>(DoNothing()); \
    cb.Run(__VA_ARGS__);                                                  \
  }                                                                       \
  TEST(RectifyCallbackTest, OnceCallbackDoNothing##TestNameSuffix) {      \
    auto cb = RectifyCallback<OnceCallback<Signature>>(DoNothing());      \
    std::move(cb).Run(__VA_ARGS__);                                       \
  }

CALLBACK_TESTS_DO_NOTHING(NoArgs, void())
CALLBACK_TESTS_DO_NOTHING(OneArg, void(int), 2)
CALLBACK_TESTS_DO_NOTHING(TwoArgs, void(float, int), 0.25, 2)
CALLBACK_TESTS_DO_NOTHING(ThreeArgs, void(bool, float, int), false, 0.25, 2)

// Test passing callbacks to RectifyCallback() in different ways.

TEST(RectifyCallbackTest, RepeatingMove) {
  auto cb = BindRepeating([](int x) { return x != 0; });
  auto cb2 = RectifyCallback<bool(float, int)>(std::move(cb));
  EXPECT_EQ(true, cb2.Run(1.0, 1));
  EXPECT_EQ(false, cb2.Run(1.0, 0));
}

TEST(RectifyCallbackTest, RepeatingCopy) {
  const auto cb = BindRepeating([](int x) { return x != 0; });
  auto cb2 = RectifyCallback<bool(float, int)>(cb);
  EXPECT_EQ(true, cb2.Run(1.0, 1));
  EXPECT_EQ(false, cb2.Run(1.0, 0));
}

TEST(RectifyCallbackTest, RepeatingConstReference) {
  const auto cb = BindRepeating([](int x) { return x != 0; });
  const auto& cb_ref = cb;
  auto cb2 = RectifyCallback<bool(float, int)>(cb_ref);
  EXPECT_EQ(true, cb2.Run(1.0, 1));
  EXPECT_EQ(false, cb2.Run(1.0, 0));
}

TEST(RectifyCallbackTest, OnceMove) {
  auto cb = BindOnce([](int x) { return x != 0; });
  auto cb2 = RectifyCallback<bool(float, int)>(std::move(cb));
  EXPECT_EQ(true, std::move(cb2).Run(1.0, 1));
}

TEST(RectifyCallbackTest, OnceFromRepeating) {
  auto cb = BindRepeating([](int x) { return x != 0; });
  auto cb2 = RectifyCallback<OnceCallback<bool(float, int)>>(std::move(cb));
  EXPECT_EQ(true, std::move(cb2).Run(1.0, 1));
}

// Test that we can write a function that can rectify its callback argument into
// the signature it wants.
//
// If this is implemented incorrectly, it can result in strange/bad behavior,
// even if it manages to compile.

namespace {

using ExampleRepeatingTargetType = RepeatingCallback<bool(double, int)>;
using ExampleOnceTargetType = OnceCallback<bool(double, int)>;

// This is the call we'll be rectifying into the expected signature.
bool ExampleTargetFunction(int x) {
  return x != 0;
}

// Base version of the function that takes a repeating callback.
// This is the actual implementation.
void ExampleFunctionRepeatingCallback(ExampleRepeatingTargetType callback) {
  EXPECT_EQ(true, std::move(callback).Run(1.0, 1));
}

// Template version of the function that wants a repeating callback; it will
// rectify its input and call the base version.
//
// If the rectify goes awry (e.g. the wrong kind of callback is generated),
// this can result in an infinite loop.
template <typename T>
void ExampleFunctionRepeatingCallback(T&& callback) {
  ExampleFunctionRepeatingCallback(
      RectifyCallback<ExampleRepeatingTargetType>(std::forward<T>(callback)));
}

// Base version of the function that takes a once callback.
// This is the actual implementation.
void ExampleFunctionOnceCallback(ExampleOnceTargetType callback) {
  EXPECT_EQ(true, std::move(callback).Run(1.0, 1));
}

// Template version of the function that wants a once callback; it will
// rectify its input and call the base version.
//
// If the rectify goes awry (e.g. the wrong kind of callback is generated),
// this can result in an infinite loop.
template <typename T>
void ExampleFunctionOnceCallback(T&& callback) {
  ExampleFunctionOnceCallback(
      RectifyCallback<ExampleOnceTargetType>(std::forward<T>(callback)));
}

}  // namespace

TEST(RectifyCallbackTest, TemplateOverloadRectifiesOnceCallback) {
  ExampleFunctionOnceCallback(BindOnce(&ExampleTargetFunction));
  ExampleFunctionOnceCallback(BindOnce([]() { return true; }));
  ExampleFunctionOnceCallback(BindOnce([](double d, int i) { return d && i; }));
  auto cb = BindOnce(&ExampleTargetFunction);
  ExampleFunctionOnceCallback(std::move(cb));
}

TEST(RectifyCallbackTest, TemplateOverloadRectifiesRepeatingCallback) {
  ExampleFunctionOnceCallback(BindRepeating(&ExampleTargetFunction));
  ExampleFunctionOnceCallback(BindRepeating([]() { return true; }));
  ExampleFunctionOnceCallback(
      BindRepeating([](double d, int i) { return d && i; }));
  auto cb = BindRepeating(&ExampleTargetFunction);
  ExampleFunctionOnceCallback(cb);
  bool result = true;
  ExampleFunctionOnceCallback(BindLambdaForTesting([&]() { return result; }));
}

TEST(RectifyCallbackTest, TemplateOverloadCoerceRepeatingTarget) {
  ExampleFunctionRepeatingCallback(BindRepeating(&ExampleTargetFunction));
  ExampleFunctionRepeatingCallback(BindRepeating([]() { return true; }));
  ExampleFunctionRepeatingCallback(
      BindRepeating([](double d, int i) { return d && i; }));
  auto cb = BindRepeating(&ExampleTargetFunction);
  ExampleFunctionRepeatingCallback(cb);
  bool result = true;
  ExampleFunctionRepeatingCallback(
      BindLambdaForTesting([&]() { return result; }));
}

TEST(RectifyCallbackTest, NullCallbackPassthrough) {
  {
    OnceCallback<void()> once;
    RepeatingCallback<void()> repeating;
    EXPECT_TRUE(RectifyCallback<void()>(std::move(once)).is_null());
    EXPECT_TRUE(RectifyCallback<void()>(repeating).is_null());
    EXPECT_TRUE(RectifyCallback<void()>(NullCallback()).is_null());
  }

  {
    OnceCallback<void()> once;
    RepeatingCallback<void()> repeating;
    EXPECT_TRUE(RectifyCallback<void(int)>(std::move(once)).is_null());
    EXPECT_TRUE(RectifyCallback<void(int)>(repeating).is_null());
    EXPECT_TRUE(RectifyCallback<void(int)>(NullCallback()).is_null());
  }
}

}  // namespace base
