// Copyright 2012 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/gmock_callback_support.h"

#include <memory>

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

using testing::_;
using testing::ByRef;
using testing::MockFunction;

namespace base::test {

using TestCallback = base::RepeatingCallback<void(const bool& src, bool* dst)>;
using TestOnceCallback = base::OnceCallback<void(const bool& src, bool* dst)>;
using TestOnceCallbackMove =
    base::OnceCallback<void(std::unique_ptr<int>, int* dst)>;

void SetBool(const bool& src, bool* dst) {
  *dst = src;
}

void SetIntFromPtr(std::unique_ptr<int> ptr, int* dst) {
  *dst = *ptr;
}

TEST(GmockCallbackSupportTest, IsNullCallback) {
  MockFunction<void(const TestCallback&)> check;
  EXPECT_CALL(check, Call(IsNullCallback()));
  check.Call(TestCallback());
}

TEST(GmockCallbackSupportTest, IsNotNullCallback) {
  MockFunction<void(const TestCallback&)> check;
  EXPECT_CALL(check, Call(IsNotNullCallback()));
  check.Call(base::BindRepeating(&SetBool));
}

TEST(GmockCallbackSupportTest, IsNullOnceCallback) {
  MockFunction<void(TestOnceCallback)> mock;
  EXPECT_CALL(mock, Call(IsNullCallback()));
  mock.Call(TestOnceCallback());
}

TEST(GmockCallbackSupportTest, IsNotNullOnceCallback) {
  MockFunction<void(TestOnceCallback)> mock;
  EXPECT_CALL(mock, Call(IsNotNullCallback()));
  mock.Call(base::BindOnce(&SetBool));
}

TEST(GmockCallbackSupportTest, RunClosure0) {
  MockFunction<void(const base::RepeatingClosure&)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(IsNotNullCallback())).WillOnce(RunClosure<0>());
  check.Call(base::BindRepeating(&SetBool, true, &dst));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunClosureByRefNotReset) {
  // Check that RepeatingClosure isn't reset by RunClosure<N>().
  MockFunction<void(base::RepeatingClosure&)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(IsNotNullCallback())).WillOnce(RunClosure<0>());
  auto closure = base::BindRepeating(&SetBool, true, &dst);
  check.Call(closure);
  EXPECT_TRUE(dst);
  EXPECT_FALSE(closure.is_null());
}

TEST(GmockCallbackSupportTest, RunCallback0) {
  MockFunction<void(const TestCallback&)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(IsNotNullCallback()))
      .WillOnce(RunCallback<0>(true, &dst));
  check.Call(base::BindRepeating(&SetBool));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunCallback1) {
  MockFunction<void(int, const TestCallback&)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(0, IsNotNullCallback()))
      .WillOnce(RunCallback<1>(true, &dst));
  check.Call(0, base::BindRepeating(&SetBool));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunCallbackPassByRef) {
  MockFunction<void(const TestCallback&)> check;
  bool dst = false;
  bool src = false;
  EXPECT_CALL(check, Call(IsNotNullCallback()))
      .WillOnce(RunCallback<0>(ByRef(src), &dst));
  src = true;
  check.Call(base::BindRepeating(&SetBool));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunCallbackPassByValue) {
  MockFunction<void(const TestCallback&)> check;
  bool dst = false;
  bool src = true;
  EXPECT_CALL(check, Call(IsNotNullCallback()))
      .WillOnce(RunCallback<0>(src, &dst));
  src = false;
  check.Call(base::BindRepeating(&SetBool));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunOnceClosure0) {
  MockFunction<void(base::OnceClosure)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(IsNotNullCallback())).WillOnce(RunOnceClosure<0>());
  check.Call(base::BindOnce(&SetBool, true, &dst));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunOnceCallback0) {
  MockFunction<void(TestOnceCallback)> check;
  bool dst = false;
  bool src = true;
  EXPECT_CALL(check, Call(IsNotNullCallback()))
      .WillOnce(RunOnceCallback<0>(src, &dst));
  src = false;
  check.Call(base::BindOnce(&SetBool));
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunOnceCallbackTwice) {
  MockFunction<void(TestOnceCallback)> check;
  bool dst = false;
  bool src = true;

  EXPECT_CALL(check, Call)
      .WillRepeatedly(RunOnceCallbackRepeatedly<0>(std::ref(src), &dst));

  check.Call(base::BindOnce(&SetBool));
  EXPECT_TRUE(dst);

  src = false;
  check.Call(base::BindOnce(&SetBool));
  EXPECT_FALSE(dst);
}

TEST(GmockCallbackSupportTest, RunClosureValue) {
  MockFunction<void()> check;
  bool dst = false;
  EXPECT_CALL(check, Call())
      .WillOnce(RunClosure(base::BindRepeating(&SetBool, true, &dst)));
  check.Call();
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunClosureValueWithArgs) {
  MockFunction<void(bool, int)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(true, 42))
      .WillOnce(RunClosure(base::BindRepeating(&SetBool, true, &dst)));
  check.Call(true, 42);
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunOnceClosureValue) {
  MockFunction<void()> check;
  bool dst = false;
  EXPECT_CALL(check, Call())
      .WillOnce(RunOnceClosure(base::BindOnce(&SetBool, true, &dst)));
  check.Call();
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunOnceClosureValueWithArgs) {
  MockFunction<void(bool, int)> check;
  bool dst = false;
  EXPECT_CALL(check, Call(true, 42))
      .WillOnce(RunOnceClosure(base::BindOnce(&SetBool, true, &dst)));
  check.Call(true, 42);
  EXPECT_TRUE(dst);
}

TEST(GmockCallbackSupportTest, RunOnceClosureValueMultipleCall) {
  MockFunction<void()> check;
  bool dst = false;
  EXPECT_CALL(check, Call())
      .WillRepeatedly(RunOnceClosure(base::BindOnce(&SetBool, true, &dst)));
  check.Call();
  EXPECT_TRUE(dst);

  // Invoking the RunOnceClosure action more than once will trigger a
  // CHECK-failure.
  dst = false;
  EXPECT_DEATH_IF_SUPPORTED(check.Call(), "");
}

TEST(GmockCallbackSupportTest, RunOnceCallbackWithMoveOnlyType) {
  MockFunction<void(TestOnceCallbackMove)> check;
  auto val = std::make_unique<int>(42);
  int dst = 0;
  EXPECT_CALL(check, Call).WillOnce(RunOnceCallback<0>(std::move(val), &dst));

  check.Call(base::BindOnce(&SetIntFromPtr));

  EXPECT_EQ(dst, 42);
  EXPECT_FALSE(val);
}

TEST(GmockCallbackSupportTest, RunOnceCallbackMultipleTimesCrashes) {
  MockFunction<void(TestOnceCallback)> check;
  bool value = true;
  bool dst = false;
  EXPECT_CALL(check, Call).WillRepeatedly(RunOnceCallback<0>(value, &dst));

  EXPECT_FALSE(dst);
  check.Call(BindOnce(&SetBool));
  EXPECT_TRUE(dst);

  // The first `Call` has invalidated the captured args. Attempting
  // to run `Call` again should result in a CHECK().
  EXPECT_DEATH_IF_SUPPORTED(check.Call(BindOnce(&SetBool)), "");
}

TEST(GmockCallbackSupportTest, RunOnceCallbackReturnsValue) {
  MockFunction<int(base::OnceCallback<int(int)>)> check;
  EXPECT_CALL(check, Call).WillRepeatedly(RunOnceCallbackRepeatedly<0>(42));
  EXPECT_EQ(43, check.Call(base::BindOnce([](int i) { return i + 1; })));
  EXPECT_EQ(44, check.Call(base::BindOnce([](int i) { return i + 2; })));
  EXPECT_EQ(45, check.Call(base::BindOnce([](int i) { return i + 3; })));
}

TEST(GmockCallbackSupportTest, RunOnceCallbackReturnsValueMoveOnly) {
  MockFunction<int(base::OnceCallback<int(std::unique_ptr<int>)>)> check;
  EXPECT_CALL(check, Call)
      .WillOnce(RunOnceCallback<0>(std::make_unique<int>(42)));
  EXPECT_EQ(43, check.Call(base::BindOnce(
                    [](std::unique_ptr<int> i) { return *i + 1; })));
}

TEST(GmockCallbackSupportTest, RunCallbackReturnsValue) {
  MockFunction<int(base::RepeatingCallback<int(int)>)> check;
  EXPECT_CALL(check, Call).WillRepeatedly(RunCallback<0>(42));
  EXPECT_EQ(43, check.Call(base::BindRepeating([](int i) { return i + 1; })));
  EXPECT_EQ(44, check.Call(base::BindRepeating([](int i) { return i + 2; })));
  EXPECT_EQ(45, check.Call(base::BindRepeating([](int i) { return i + 3; })));
}

// Smoke test to validate that RunOnceCallback<> works with vectors of move-only
// args.
TEST(GmockCallbackSupportTest, VectorOfMoveOnlyArgs) {
  using VectorOfMoveOnly = std::vector<std::unique_ptr<std::string>>;
  MockFunction<void(base::OnceCallback<void(VectorOfMoveOnly)>)> check;

  EXPECT_CALL(check, Call).WillOnce(RunOnceCallback<0>([] {
    return VectorOfMoveOnly();
  }()));
  check.Call(base::DoNothing());
}

}  // namespace base::test
