#ifndef UI_BASE_INTERACTION_INTERACTIVE_TEST_DEFINITIONS_H_
#define UI_BASE_INTERACTION_INTERACTIVE_TEST_DEFINITIONS_H_
#include <type_traits>
#include <utility>
#include <variant>
#include "base/functional/callback_helpers.h"
#include "base/test/bind.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/interaction_sequence.h"
namespace ui::test::internal {
using MultiStep = std::vector<InteractionSequence::StepBuilder>;
template <typename T>
concept IsCallable = requires { &std::decay_t<T>::operator(); };
template <typename T>
concept HasState = !std::is_empty_v<std::remove_reference_t<T>>;
template <typename T>
inline constexpr bool IsFunctionPointerValue = false;
template <typename R, typename... Args>
inline constexpr bool IsFunctionPointerValue<R (*)(Args...)> = true;
template <typename T>
concept IsFunctionPointer = IsFunctionPointerValue<T>;
template <typename F>
auto MaybeBind(F&& function) {
if constexpr (base::IsBaseCallback<F>) {
return std::forward<F>(function);
} else if constexpr (IsCallable<F> && HasState<F>) {
return base::BindLambdaForTesting(std::forward<F>(function));
} else if constexpr ((IsCallable<F> && !HasState<F>) ||
IsFunctionPointer<F>) {
return base::BindOnce(std::forward<F>(function));
} else if constexpr (std::same_as<F, decltype(base::DoNothing())>) {
return function;
} else {
static_assert(false, "Can only bind callable objects.");
}
}
template <typename F>
struct MaybeBindTypeHelper {
using CallbackType = std::invoke_result_t<decltype(&MaybeBind<F>), F>;
using ReturnType = typename CallbackType::ResultType;
using Signature = typename CallbackType::RunType;
};
template <>
struct MaybeBindTypeHelper<decltype(base::DoNothing())> {
using ReturnType = void;
};
template <typename F>
base::RepeatingCallback<typename MaybeBindTypeHelper<F>::Signature>
MaybeBindRepeating(F&& function) {
if constexpr (IsCallable<F> && !HasState<F> &&
std::copy_constructible<std::decay_t<F>>) {
return base::BindRepeating(std::forward<F>(function));
} else {
return MaybeBind(std::forward<F>(function));
}
}
template <typename T>
struct ArgsExtractor;
template <typename R, typename... Args>
struct ArgsExtractor<R(Args...)> {
using Holder = std::tuple<Args...>;
};
template <typename F>
using ReturnTypeOf = MaybeBindTypeHelper<F>::ReturnType;
template <size_t N, typename F>
using NthArgumentOf = std::tuple_element_t<
N,
typename ArgsExtractor<typename MaybeBindTypeHelper<F>::Signature>::Holder>;
template <typename F, typename S>
concept HasSignature =
std::same_as<typename MaybeBindTypeHelper<F>::Signature, S> ||
std::same_as<F, decltype(base::DoNothing())>;
template <typename F, typename S>
inline constexpr bool HasCompatibleSignatureValue = false;
template <typename F, typename S>
concept HasCompatibleSignature = HasCompatibleSignatureValue<F, S>;
template <typename F, typename R>
requires HasSignature<F, R()>
inline constexpr bool HasCompatibleSignatureValue<F, R()> = true;
template <typename F, typename R, typename A, typename... Args>
requires HasSignature<F, R(A, Args...)> ||
HasCompatibleSignature<F, R(Args...)>
inline constexpr bool HasCompatibleSignatureValue<F, R(A, Args...)> = true;
template <typename T>
concept IsReferenceWrapper = base::is_instantiation<T, std::reference_wrapper>;
template <typename T>
struct MatcherTypeHelper {
using ActualType = T;
};
template <typename C>
requires(std::same_as<std::remove_const_t<C>, char> ||
std::same_as<std::remove_const_t<C>, char16_t>)
struct MatcherTypeHelper<C*> {
using ActualType = std::basic_string<std::remove_const_t<C>>;
};
template <typename CharT, typename Traits>
struct MatcherTypeHelper<std::basic_string_view<CharT, Traits>> {
using ActualType = std::basic_string<CharT, Traits>;
};
template <typename T>
using MatcherTypeFor = MatcherTypeHelper<std::decay_t<T>>::ActualType;
template <typename T>
concept IsValidMatcherType = std::same_as<T, MatcherTypeFor<T>>;
template <typename T>
concept IsGtestMatcher = requires { typename T::is_gtest_matcher; };
template <typename T>
concept HasMatchAndExplain = requires { &T::MatchAndExplain; };
template <typename T>
concept IsMatcher = IsGtestMatcher<T> || HasMatchAndExplain<T> ||
base::is_instantiation<T, testing::PolymorphicMatcher>;
template <typename F>
concept IsStepCallback = internal::
HasCompatibleSignature<F, void(InteractionSequence*, TrackedElement*)>;
template <typename F, typename R>
concept IsCheckCallback =
internal::HasCompatibleSignature<F,
R(const InteractionSequence*,
const TrackedElement*)>;
std::string DescribeElement(ElementSpecifier spec);
InteractionSequence::Builder BuildSubsequence(MultiStep steps);
template <typename Arg>
auto UnwrapArgument(Arg arg) {
if constexpr (base::IsBaseCallback<Arg>) {
return std::move(arg).Run();
} else if constexpr (internal::IsFunctionPointer<Arg>) {
return (*arg)();
} else if constexpr (internal::IsCallable<Arg>) {
return arg();
} else {
return arg;
}
}
template <typename T, typename U>
testing::Matcher<T> CreateMatcherFromValue(U value) {
if constexpr (internal::IsReferenceWrapper<U>) {
return testing::Matcher<T>(T(value.get()));
} else if constexpr (std::derived_from<U, testing::Matcher<T>> ||
std::convertible_to<U, testing::Matcher<T>>) {
return value;
} else if constexpr (internal::IsMatcher<U>) {
return testing::Matcher<T>(value);
} else {
return testing::Matcher<T>(
T(internal::UnwrapArgument<U>(std::move(value))));
}
}
template <typename T>
class StepBlock {
public:
StepBlock();
explicit StepBlock(MultiStep steps) : steps_(std::move(steps)) {}
StepBlock(StepBlock<T>&& other) noexcept = default;
StepBlock& operator=(StepBlock<T>&& other) noexcept = default;
~StepBlock() = default;
MultiStep& steps() { return steps_; }
const MultiStep& steps() const { return steps_; }
private:
MultiStep steps_;
};
template <typename T>
concept IsValueOrRvalue = !std::is_lvalue_reference_v<T>;
}
#endif