blob: 4d33f4c70b1cb50df66f219b53698625c4e08d0f [file] [log] [blame]
// 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 "ui/base/interaction/interactive_test.h"
#include <functional>
#include <list>
#include <memory>
#include <string>
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_test_util.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/expect_call_in_scope.h"
#include "ui/base/interaction/interaction_sequence.h"
#include "ui/base/interaction/interactive_test_internal.h"
#include "ui/base/interaction/state_observer.h"
#if !BUILDFLAG(IS_IOS)
#include "ui/base/accelerators/accelerator.h"
#endif
namespace ui::test {
namespace {
enum class ActionType {
kPressButton,
kSelectMenuItem,
kDoDefaultAction,
kSelectTab,
kSelectDropdownItem,
kEnterText,
kActivateSurface,
kFocusElement,
kSendAccelerator,
kSendKeyPress,
kConfirm
};
using ActionRecord = std::tuple<ActionType,
ElementIdentifier,
ElementContext,
InteractionTestUtil::InputType>;
constexpr ui::ElementContext kTestContext1 =
ui::ElementContext::CreateFakeContextForTesting(1);
constexpr ui::ElementContext kTestContext2 =
ui::ElementContext::CreateFakeContextForTesting(2);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestId1);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestId2);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestId3);
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kTestId4);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kTestEvent1);
DEFINE_LOCAL_CUSTOM_ELEMENT_EVENT_TYPE(kTestEvent2);
constexpr char kSetOnIncompatibleActionMessage[] =
"Explicitly testing incompatibility-handling.";
class TestSimulator : public InteractionTestUtil::Simulator {
public:
TestSimulator() = default;
~TestSimulator() override = default;
void set_result(ActionResult result) { result_ = result; }
ActionResult PressButton(TrackedElement* element,
InputType input_type) override {
DoAction(ActionType::kPressButton, element, input_type);
return result_;
}
ActionResult SelectMenuItem(TrackedElement* element,
InputType input_type) override {
DoAction(ActionType::kSelectMenuItem, element, input_type);
return result_;
}
ActionResult DoDefaultAction(TrackedElement* element,
InputType input_type) override {
DoAction(ActionType::kDoDefaultAction, element, input_type);
return result_;
}
ActionResult SelectTab(
TrackedElement* tab_collection,
size_t index,
InputType input_type,
std::optional<size_t> expected_index_after_selection) override {
DoAction(ActionType::kSelectTab, tab_collection, input_type);
return result_;
}
ActionResult SelectDropdownItem(TrackedElement* collection,
size_t item,
InputType input_type) override {
DoAction(ActionType::kSelectDropdownItem, collection, input_type);
return result_;
}
ActionResult EnterText(TrackedElement* element,
std::u16string text,
TextEntryMode mode) override {
DoAction(ActionType::kEnterText, element, InputType::kKeyboard);
return result_;
}
ActionResult ActivateSurface(TrackedElement* element) override {
DoAction(ActionType::kActivateSurface, element, InputType::kMouse);
return result_;
}
ActionResult FocusElement(TrackedElement* element) override {
DoAction(ActionType::kFocusElement, element, InputType::kMouse);
return result_;
}
#if !BUILDFLAG(IS_IOS)
ActionResult SendAccelerator(TrackedElement* element,
Accelerator accel) override {
DoAction(ActionType::kSendAccelerator, element, InputType::kKeyboard);
return result_;
}
ActionResult SendKeyPress(TrackedElement* element,
KeyboardCode key,
int flags) override {
DoAction(ActionType::kSendKeyPress, element, InputType::kKeyboard);
return result_;
}
#endif // !BUILDFLAG(IS_IOS)
ActionResult Confirm(TrackedElement* element) override {
DoAction(ActionType::kConfirm, element, InputType::kDontCare);
return result_;
}
const std::vector<ActionRecord>& records() const { return records_; }
private:
void DoAction(ActionType action_type,
TrackedElement* element,
InputType input_type) {
records_.emplace_back(action_type, element->identifier(),
element->context(), input_type);
}
ActionResult result_ = ActionResult::kSucceeded;
std::vector<ActionRecord> records_;
};
void DoFunction() {
LOG(INFO) << "In normal function.";
}
const TrackedElement* CheckElementFunction(const TrackedElement* el) {
return el;
}
int ValueGeneratingFunction() {
return 5;
}
struct CallableObject {
bool operator()() const { return i != 0; }
int i = 0;
};
struct MutableCallableObject {
bool operator()() { return i != 0; }
int i = 0;
};
struct EmptyCallableObject {
void operator()() const {}
};
} // namespace
class InteractiveTestTest : public InteractiveTestMixin<testing::Test> {
public:
InteractiveTestTest() {
auto simulator = std::make_unique<TestSimulator>();
simulator_ = simulator.get();
test_util().AddSimulator(std::move(simulator));
internal::InteractiveTestPrivate::set_interactive_test_verbs_allowed(
base::PassKey<InteractiveTestTest>());
}
protected:
TestSimulator* simulator() { return simulator_.get(); }
// Posts the given `actions`, spaced out in time a bit so they don't flood the
// test faster than it can process the steps being tested.
//
// In a real test, each action would be triggered by the step before it, but
// since this test suite is testing the low-level primitives, the events must
// be simulated *and* cannot be tied to the sequence itself.
//
// This is in general not a great way to test things, as there is technically
// still a race condition.
template <typename... C>
void QueueActions(C&&... actions) {
(queued_actions_.emplace_back(
internal::MaybeBind(std::forward<C>(actions))),
...);
MaybePostQueuedAction();
}
const auto& state_observers() {
return private_test_impl().state_observer_elements_;
}
raw_ptr<TestSimulator> simulator_ = nullptr;
private:
void MaybePostQueuedAction() {
if (queued_actions_.empty()) {
return;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&InteractiveTestTest::RunQueuedAction,
weak_ptr_factory_.GetWeakPtr()),
base::Milliseconds(100));
}
void RunQueuedAction() {
if (queued_actions_.empty()) {
return;
}
std::move(queued_actions_.front()).Run();
queued_actions_.pop_front();
MaybePostQueuedAction();
}
std::list<base::OnceClosure> queued_actions_;
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::UI};
base::WeakPtrFactory<InteractiveTestTest> weak_ptr_factory_{this};
};
TEST_F(InteractiveTestTest, StepsConstructsMultiStep) {
auto result =
Steps(StepBuilder(), Steps(StepBuilder(), StepBuilder()), StepBuilder());
EXPECT_EQ(4U, result.size());
}
TEST_F(InteractiveTestTest, RunTestSequenceInContext) {
TestElement el(kTestId1, kTestContext1);
el.Show();
EXPECT_TRUE(RunTestSequenceInContext(kTestContext1, WaitForShow(kTestId1)));
}
TEST_F(InteractiveTestTest, WaitInAnyContext) {
TestElement e1(kTestId1, kTestContext2);
TestElement e2(kTestId2, kTestContext2);
QueueActions([&]() { e1.Show(); }, [&]() { e2.Show(); },
[&]() { e2.SendCustomEvent(kTestEvent1); },
[&]() { e1.Hide(); });
RunTestSequenceInContext(
kTestContext1,
InAnyContext(WaitForShow(kTestId1), WaitForShow(kTestId2),
WaitForEvent(kTestId2, kTestEvent1), WaitForHide(kTestId1)));
}
TEST_F(InteractiveTestTest, FlushInAnyContext) {
TestElement e1(kTestId1, kTestContext2);
TestElement e2(kTestId2, kTestContext2);
e1.Show();
e2.Show();
RunTestSequenceInContext(kTestContext1, InAnyContext(WaitForShow(kTestId1),
WaitForShow(kTestId2)));
}
TEST_F(InteractiveTestTest, InteractionVerbs) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
TestElement e3(kTestId3, kTestContext1);
TestElement e4(kTestId4, kTestContext1);
e1.Show();
e2.Show();
e3.Show();
e4.Show();
RunTestSequenceInContext(
kTestContext1, PressButton(kTestId1, InputType::kDontCare),
SelectMenuItem(kTestId2, InputType::kKeyboard),
DoDefaultAction(kTestId3, InputType::kMouse),
SelectTab(kTestId4, 3U, InputType::kTouch),
SelectDropdownItem(kTestId1, 2U, InputType::kDontCare),
EnterText(kTestId2, u"The quick brown fox.", TextEntryMode::kAppend),
ActivateSurface(kTestId3), FocusElement(kTestId1),
#if !BUILDFLAG(IS_IOS)
SendAccelerator(kTestId4, Accelerator()),
SendKeyPress(kTestId2, KeyboardCode::VKEY_A, EF_NONE),
#endif
Confirm(kTestId1));
EXPECT_THAT(simulator()->records(),
testing::ElementsAre(
ActionRecord{ActionType::kPressButton, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kSelectMenuItem, kTestId2,
kTestContext1, InputType::kKeyboard},
ActionRecord{ActionType::kDoDefaultAction, kTestId3,
kTestContext1, InputType::kMouse},
ActionRecord{ActionType::kSelectTab, kTestId4, kTestContext1,
InputType::kTouch},
ActionRecord{ActionType::kSelectDropdownItem, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kEnterText, kTestId2, kTestContext1,
InputType::kKeyboard},
ActionRecord{ActionType::kActivateSurface, kTestId3,
kTestContext1, InputType::kMouse},
ActionRecord{ActionType::kFocusElement, kTestId1,
kTestContext1, InputType::kMouse},
#if !BUILDFLAG(IS_IOS)
ActionRecord{ActionType::kSendAccelerator, kTestId4,
kTestContext1, InputType::kKeyboard},
ActionRecord{ActionType::kSendKeyPress, kTestId2,
kTestContext1, InputType::kKeyboard},
#endif
ActionRecord{ActionType::kConfirm, kTestId1, kTestContext1,
InputType::kDontCare}));
}
TEST_F(InteractiveTestTest, InteractionVerbsInAnyContext) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
TestElement e3(kTestId3, kTestContext1);
TestElement e4(kTestId4, kTestContext1);
e1.Show();
e2.Show();
e3.Show();
e4.Show();
RunTestSequenceInContext(
kTestContext2, InAnyContext(PressButton(kTestId1, InputType::kDontCare)),
InAnyContext(SelectMenuItem(kTestId2, InputType::kKeyboard)),
InAnyContext(DoDefaultAction(kTestId3, InputType::kMouse)),
InAnyContext(SelectTab(kTestId4, 3U, InputType::kTouch),
SelectDropdownItem(kTestId1, 2U, InputType::kDontCare),
EnterText(kTestId2, u"The quick brown fox."),
ActivateSurface(kTestId3), FocusElement(kTestId1),
#if !BUILDFLAG(IS_IOS)
SendAccelerator(kTestId4, Accelerator()),
SendKeyPress(kTestId2, VKEY_A, EF_NONE),
#endif
Confirm(kTestId1)));
EXPECT_THAT(simulator()->records(),
testing::ElementsAre(
ActionRecord{ActionType::kPressButton, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kSelectMenuItem, kTestId2,
kTestContext1, InputType::kKeyboard},
ActionRecord{ActionType::kDoDefaultAction, kTestId3,
kTestContext1, InputType::kMouse},
ActionRecord{ActionType::kSelectTab, kTestId4, kTestContext1,
InputType::kTouch},
ActionRecord{ActionType::kSelectDropdownItem, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kEnterText, kTestId2, kTestContext1,
InputType::kKeyboard},
ActionRecord{ActionType::kActivateSurface, kTestId3,
kTestContext1, InputType::kMouse},
ActionRecord{ActionType::kFocusElement, kTestId1,
kTestContext1, InputType::kMouse},
#if !BUILDFLAG(IS_IOS)
ActionRecord{ActionType::kSendAccelerator, kTestId4,
kTestContext1, InputType::kKeyboard},
ActionRecord{ActionType::kSendKeyPress, kTestId2,
kTestContext1, InputType::kKeyboard},
#endif
ActionRecord{ActionType::kConfirm, kTestId1, kTestContext1,
InputType::kDontCare}));
}
TEST_F(InteractiveTestTest, InteractionVerbsInSameContext) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
TestElement e3(kTestId3, kTestContext1);
TestElement e4(kTestId4, kTestContext1);
e1.Show();
e2.Show();
e3.Show();
e4.Show();
RunTestSequenceInContext(
kTestContext2, InAnyContext(PressButton(kTestId1, InputType::kDontCare)),
InSameContext(SelectMenuItem(kTestId2, InputType::kKeyboard)),
InSameContext(DoDefaultAction(kTestId3, InputType::kMouse)),
InSameContext(SelectTab(kTestId4, 3U, InputType::kTouch),
SelectDropdownItem(kTestId1, 2U, InputType::kDontCare),
EnterText(kTestId2, u"The quick brown fox."),
ActivateSurface(kTestId3),
#if !BUILDFLAG(IS_IOS)
SendAccelerator(kTestId4, Accelerator()),
#endif
Confirm(kTestId1)));
EXPECT_THAT(simulator()->records(),
testing::ElementsAre(
ActionRecord{ActionType::kPressButton, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kSelectMenuItem, kTestId2,
kTestContext1, InputType::kKeyboard},
ActionRecord{ActionType::kDoDefaultAction, kTestId3,
kTestContext1, InputType::kMouse},
ActionRecord{ActionType::kSelectTab, kTestId4, kTestContext1,
InputType::kTouch},
ActionRecord{ActionType::kSelectDropdownItem, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kEnterText, kTestId2, kTestContext1,
InputType::kKeyboard},
ActionRecord{ActionType::kActivateSurface, kTestId3,
kTestContext1, InputType::kMouse},
#if !BUILDFLAG(IS_IOS)
ActionRecord{ActionType::kSendAccelerator, kTestId4,
kTestContext1, InputType::kKeyboard},
#endif
ActionRecord{ActionType::kConfirm, kTestId1, kTestContext1,
InputType::kDontCare}));
}
TEST_F(InteractiveTestTest, InteractionVerbsInSameContextAs) {
constexpr char kElementName[] = "name";
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
TestElement e3(kTestId3, kTestContext1);
TestElement e4(kTestId4, kTestContext1);
e1.Show();
e2.Show();
e3.Show();
e4.Show();
RunTestSequenceInContext(
kTestContext2,
// Name element 1.
InAnyContext(NameElementRelative(
kTestId1, kElementName, [](ui::TrackedElement* el) { return el; })),
// Use the named element to find an element.
InSameContextAs(kElementName,
SelectMenuItem(kTestId2, InputType::kKeyboard)),
// Use the element ID instead as it is unique.
InSameContextAs(kTestId1, DoDefaultAction(kTestId3, InputType::kMouse)),
// Ensure that we handle groups of steps with a named element as well.
InSameContextAs(
kElementName,
Steps(SelectTab(kTestId4, 3U, InputType::kTouch),
SelectDropdownItem(kTestId1, 2U, InputType::kDontCare),
EnterText(kTestId2, u"The quick brown fox."))),
// Ensure that we handle groups of steps with a unique element ID.
InSameContextAs(kTestId1, Steps(ActivateSurface(kTestId3),
#if !BUILDFLAG(IS_IOS)
SendAccelerator(kTestId4, Accelerator()),
#endif
Confirm(kTestId1))));
EXPECT_THAT(simulator()->records(),
testing::ElementsAre(
ActionRecord{ActionType::kSelectMenuItem, kTestId2,
kTestContext1, InputType::kKeyboard},
ActionRecord{ActionType::kDoDefaultAction, kTestId3,
kTestContext1, InputType::kMouse},
ActionRecord{ActionType::kSelectTab, kTestId4, kTestContext1,
InputType::kTouch},
ActionRecord{ActionType::kSelectDropdownItem, kTestId1,
kTestContext1, InputType::kDontCare},
ActionRecord{ActionType::kEnterText, kTestId2, kTestContext1,
InputType::kKeyboard},
ActionRecord{ActionType::kActivateSurface, kTestId3,
kTestContext1, InputType::kMouse},
#if !BUILDFLAG(IS_IOS)
ActionRecord{ActionType::kSendAccelerator, kTestId4,
kTestContext1, InputType::kKeyboard},
#endif
ActionRecord{ActionType::kConfirm, kTestId1, kTestContext1,
InputType::kDontCare}));
}
TEST_F(InteractiveTestTest, Do) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, f);
EXPECT_CALL_IN_SCOPE(f, Run,
RunTestSequenceInContext(kTestContext1, Do(f.Get())));
}
TEST_F(InteractiveTestTest, Check) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool()>, check);
EXPECT_CALL(check, Run).WillOnce([]() { return true; });
RunTestSequenceInContext(kTestContext1, Check(check.Get()));
}
TEST_F(InteractiveTestTest, CheckFails) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool()>, check);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL(check, Run).WillOnce([]() { return false; });
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(kTestContext1, Check(check.Get())));
});
}
TEST_F(InteractiveTestTest, CheckResult) {
UNCALLED_MOCK_CALLBACK(base::RepeatingCallback<int()>, f);
EXPECT_CALL(f, Run).WillOnce([]() { return 2; }).WillOnce([]() { return 3; });
UNCALLED_MOCK_CALLBACK(base::OnceCallback<std::string()>, f2);
const char kString[] = "a string";
EXPECT_CALL(f2, Run).WillOnce([=]() { return std::string(kString); });
RunTestSequenceInContext(kTestContext1, CheckResult(f.Get(), 2),
CheckResult(f.Get(), testing::Gt(2)),
CheckResult(f2.Get(), kString));
}
TEST_F(InteractiveTestTest, CheckResultFails) {
UNCALLED_MOCK_CALLBACK(base::RepeatingCallback<int()>, f);
EXPECT_CALL(f, Run).WillOnce([]() { return 2; });
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(
RunTestSequenceInContext(kTestContext1, CheckResult(f.Get(), 3)));
});
}
TEST_F(InteractiveTestTest, CheckVariable) {
int x = 0;
const int y = 0;
const char* text = "foo";
constexpr char kNewValue[] = "bar";
RunTestSequenceInContext(kTestContext1, CheckVariable(y, 0), Do([&]() {
x = 1;
text = kNewValue;
}),
CheckVariable(x, testing::Gt(0)),
CheckVariable(text, kNewValue));
}
TEST_F(InteractiveTestTest, CheckVariableFails) {
int x = 0;
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(kTestContext1, Do([&]() { x = 1; }),
CheckVariable(x, 0)));
});
}
TEST_F(InteractiveTestTest, CheckElement) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
UNCALLED_MOCK_CALLBACK(base::RepeatingCallback<bool(TrackedElement * el)>,
cb1);
EXPECT_CALL(cb1, Run).WillRepeatedly(
[&e1](TrackedElement* el) { return el == &e1; });
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(TrackedElement * el)>, cb2);
EXPECT_CALL(cb2, Run).WillOnce(
[&e2](TrackedElement* el) { return el == &e2; });
e1.Show();
e2.Show();
RunTestSequenceInContext(kTestContext1, CheckElement(kTestId1, cb1.Get()),
CheckElement(kTestId2, cb2.Get()),
CheckElement(kTestId1, cb1.Get()));
}
TEST_F(InteractiveTestTest, CheckElementFails) {
TestElement e1(kTestId1, kTestContext1);
UNCALLED_MOCK_CALLBACK(base::RepeatingCallback<bool(TrackedElement * el)>,
cb1);
EXPECT_CALL(cb1, Run).WillRepeatedly(
[&e1](TrackedElement* el) { return el != &e1; });
e1.Show();
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(kTestContext1,
CheckElement(kTestId1, cb1.Get())));
});
}
TEST_F(InteractiveTestTest, CheckElementWithMatcher) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
UNCALLED_MOCK_CALLBACK(
base::RepeatingCallback<TrackedElement*(TrackedElement * el)>, cb1);
EXPECT_CALL(cb1, Run).WillRepeatedly([](TrackedElement* el) { return el; });
UNCALLED_MOCK_CALLBACK(base::OnceCallback<int(TrackedElement * el)>, cb2);
EXPECT_CALL(cb2, Run).WillOnce(
[&e1](TrackedElement* el) { return el == &e1 ? 1 : 2; });
e1.Show();
e2.Show();
RunTestSequenceInContext(kTestContext1,
// Implicitly create testing::Eq from value.
CheckElement(kTestId1, cb1.Get(), &e1),
// Test explicit matchers.
CheckElement(kTestId2, cb2.Get(), testing::Gt(1)),
CheckElement(kTestId2, cb1.Get(), testing::Ne(&e1)));
}
TEST_F(InteractiveTestTest, CheckElementWithMatcherFails) {
TestElement e1(kTestId1, kTestContext1);
UNCALLED_MOCK_CALLBACK(base::RepeatingCallback<int(TrackedElement * el)>,
cb1);
EXPECT_CALL(cb1, Run).WillRepeatedly(
[&e1](TrackedElement* el) { return el == &e1 ? 1 : 2; });
e1.Show();
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(
kTestContext1, CheckElement(kTestId1, cb1.Get(), 2)));
});
}
TEST_F(InteractiveTestTest, After) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, cb1);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, cb2);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, cb3);
TestElement el(kTestId1, kTestContext1);
testing::InSequence in_sequence;
EXPECT_CALL(cb1, Run);
EXPECT_CALL(cb2, Run);
EXPECT_CALL(cb3, Run);
QueueActions([&]() { el.Show(); }, [&]() { el.SendCustomEvent(kTestEvent1); },
[&]() { el.SendCustomEvent(kTestEvent2); },
[&]() { el.Hide(); });
RunTestSequenceInContext(kTestContext1, AfterShow(kTestId1, cb1.Get()),
AfterEvent(kTestId1, kTestEvent2, cb2.Get()),
AfterHide(kTestId1, cb3.Get()));
}
TEST_F(InteractiveTestTest, WaitFor) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
QueueActions(
// Already in step 1, this triggers step 2.
[&]() { e2.Show(); },
// Hide before moving to step 3.
[&]() { e1.Hide(); },
// This should transition both 3 and 4.
[&]() { e2.SendCustomEvent(kTestEvent1); },
// This should transition step 5.
[&]() { e2.Hide(); });
e1.Show();
RunTestSequenceInContext(
kTestContext1, WaitForShow(kTestId1),
WaitForShow(kTestId2, /* transition_only_on_event =*/true),
WaitForEvent(kTestId2, kTestEvent1), WaitForHide(kTestId1),
WaitForHide(kTestId2, /* transition_only_on_event =*/true));
}
TEST_F(InteractiveTestTest, PresentOrNotPresentInAnyContext) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext2);
e1.Show();
e2.Show();
RunTestSequenceInContext(kTestContext1, EnsurePresent(kTestId1),
// Not present in the current context.
EnsureNotPresent(kTestId2),
InAnyContext(EnsureNotPresent(kTestId3)),
InAnyContext(EnsurePresent(kTestId2)));
}
TEST_F(InteractiveTestTest, WithElementFails) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, callback);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(
kTestContext1, WithElement(kTestId1, callback.Get())));
});
}
TEST_F(InteractiveTestTest, EnsureNotPresentFails) {
TestElement e1(kTestId1, kTestContext1);
e1.Show();
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(
RunTestSequenceInContext(kTestContext1, EnsureNotPresent(kTestId1)));
});
}
TEST_F(InteractiveTestTest, EnsureNotPresentInAnyContextFails) {
TestElement e1(kTestId1, kTestContext1);
e1.Show();
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(
kTestContext2, InAnyContext(EnsureNotPresent(kTestId1))));
});
}
TEST_F(InteractiveTestTest, EnsurePresentFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL_IN_SCOPE(aborted, Run, {
EXPECT_FALSE(RunTestSequenceInContext(
kTestContext2, InAnyContext(EnsurePresent(kTestId2))));
});
}
TEST_F(InteractiveTestTest, NameElementWithPointer) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(TrackedElement*)>, cb);
TestElement el(kTestId1, kTestContext1);
el.Show();
constexpr char kName[] = "name";
EXPECT_CALL_IN_SCOPE(
cb, Run(&el),
RunTestSequenceInContext(kTestContext1, NameElement(kName, &el),
WithElement(kName, cb.Get())));
}
TEST_F(InteractiveTestTest, NameElementWithReference) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(TrackedElement*)>, cb);
TestElement el(kTestId1, kTestContext1);
el.Show();
constexpr char kName[] = "name";
TrackedElement* ptr = nullptr;
EXPECT_CALL_IN_SCOPE(
cb, Run(&el),
RunTestSequenceInContext(kTestContext1, Do([&]() { ptr = &el; }),
NameElement(kName, std::ref(ptr)),
WithElement(kName, cb.Get())));
}
TEST_F(InteractiveTestTest, NameElementWithCallback) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(TrackedElement*)>, cb);
TestElement el(kTestId1, kTestContext1);
el.Show();
constexpr char kName[] = "name";
EXPECT_CALL_IN_SCOPE(
cb, Run(&el),
RunTestSequenceInContext(
kTestContext1,
NameElement(kName, base::BindLambdaForTesting(
[&]() -> TrackedElement* { return &el; })),
WithElement(kName, cb.Get())));
}
TEST_F(InteractiveTestTest, NameElementWithContext) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(TrackedElement*)>, cb);
TestElement el(kTestId1, kTestContext2);
el.Show();
constexpr char kName[] = "name";
EXPECT_CALL_IN_SCOPE(
cb, Run(&el),
RunTestSequenceInContext(
kTestContext1,
InContext(kTestContext2,
NameElement(
kName,
base::BindLambdaForTesting([&](ElementContext context) {
return ElementTracker::GetElementTracker()
->GetUniqueElement(kTestId1, context);
}))),
WithElement(kName, cb.Get())));
}
TEST_F(InteractiveTestTest, SimulatorSucceeds_SkipOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorSucceeds_IgnoreOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kIgnoreAndContinue,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorFailureFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kFailed);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(kTestContext1, PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorFailureFails_SkipOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kFailed);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorFailureFails_IgnoreOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kFailed);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kIgnoreAndContinue,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorCannotSimulateFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kNotAttempted);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(kTestContext1, PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorCannotSimulateFails_SkipOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kNotAttempted);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorCannotSimulateFails_IgnoreOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kNotAttempted);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kIgnoreAndContinue,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorNotSupportedFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kKnownIncompatible);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(kTestContext1, PressButton(kTestId1)));
EXPECT_FALSE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorNotSupportedSkipsOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kKnownIncompatible);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1)));
EXPECT_TRUE(private_test_impl().sequence_skipped());
}
TEST_F(InteractiveTestTest, SimulatorNotSupportedContinuesOnUnsupported) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
bool result = false;
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kKnownIncompatible);
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kIgnoreAndContinue,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1), Do([&result]() { result = true; }));
EXPECT_TRUE(result);
}
TEST_F(InteractiveTestTest, CanChangeOnIncompatibleAction) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
simulator_->set_result(ActionResult::kKnownIncompatible);
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(
kTestContext1,
// Based on previous tests, this will fall through to the next step.
SetOnIncompatibleAction(OnIncompatibleAction::kIgnoreAndContinue,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1),
// By changing the incompatible mode, the step after this one should
// fail.
SetOnIncompatibleAction(OnIncompatibleAction::kFailTest, ""),
PressButton(kTestId1)));
}
TEST_F(InteractiveTestTest, SimulatorNotSupportedHaltAndSucceedOnUnsupported) {
TestElement e1(kTestId1, kTestContext1);
e1.Show();
bool result = false;
simulator_->set_result(ActionResult::kKnownIncompatible);
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kHaltTest,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1), Do([&result]() { result = true; }));
EXPECT_FALSE(result);
}
TEST_F(InteractiveTestTest, ActuallySkipsTestOnSimulatorFailure) {
TestElement e1(kTestId1, kTestContext1);
e1.Show();
simulator_->set_result(ActionResult::kKnownIncompatible);
RunTestSequenceInContext(
kTestContext1,
SetOnIncompatibleAction(OnIncompatibleAction::kSkipTest,
kSetOnIncompatibleActionMessage),
PressButton(kTestId1));
// Note: this test will either be marked as skipped or failed, but never
// succeeded. The important thing is that it does not fail.
if (!testing::Test::IsSkipped()) {
GTEST_FAIL();
}
}
TEST_F(InteractiveTestTest, IfTrue) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(true));
EXPECT_CALL(step, Run);
RunTestSequenceInContext(e1.context(),
If(condition.Get(), Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfFalse) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(false));
RunTestSequenceInContext(e1.context(),
If(condition.Get(), Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfMatcherTrue) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<int(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(1));
EXPECT_CALL(step, Run);
RunTestSequenceInContext(
e1.context(),
IfMatches(condition.Get(), testing::Eq(1), Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfMatcherFalse) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<int(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(0));
RunTestSequenceInContext(
e1.context(),
IfMatches(condition.Get(), testing::Eq(1), Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfImplicitMatcherTrue) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<int(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(1));
EXPECT_CALL(step, Run);
RunTestSequenceInContext(e1.context(),
IfMatches(condition.Get(), 1, Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfImplicitMatcherFalse) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<int(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(0));
RunTestSequenceInContext(e1.context(),
IfMatches(condition.Get(), 1, Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfWithMultiStep) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step1);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step2);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(true));
EXPECT_CALL(step1, Run);
EXPECT_CALL(step2, Run);
RunTestSequenceInContext(
e1.context(),
If(condition.Get(), Then(Do(step1.Get()), Do(step2.Get()))));
}
TEST_F(InteractiveTestTest, IfElementTrue) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(const TrackedElement*)>,
condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run(&e1)).WillOnce(testing::Return(true));
EXPECT_CALL(step, Run);
RunTestSequenceInContext(
e1.context(),
IfElement(e1.identifier(), condition.Get(), Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfElementFalse) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(const TrackedElement*)>,
condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run(&e1)).WillOnce(testing::Return(false));
RunTestSequenceInContext(
e1.context(),
IfElement(e1.identifier(), condition.Get(), Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfElementMatchesTrue) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<std::string(const TrackedElement*)>,
condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run(&e1))
.WillOnce(testing::Return(std::string("foo")));
EXPECT_CALL(step, Run);
RunTestSequenceInContext(
e1.context(), IfElementMatches(e1.identifier(), condition.Get(), "foo",
Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfElementMatchesFalse) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<std::string(const TrackedElement*)>,
condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run(&e1))
.WillOnce(testing::Return(std::string("bar")));
RunTestSequenceInContext(
e1.context(), IfElementMatches(e1.identifier(), condition.Get(), "foo",
Then(Do(step.Get()))));
}
TEST_F(InteractiveTestTest, IfElementWithMultiStep) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(const TrackedElement*)>,
condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step1);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, step2);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run(&e1)).WillOnce(testing::Return(true));
EXPECT_CALL(step1, Run);
EXPECT_CALL(step2, Run);
RunTestSequenceInContext(e1.context(),
IfElement(e1.identifier(), condition.Get(),
Then(Do(step1.Get()), Do(step2.Get()))));
}
TEST_F(InteractiveTestTest, IfFails) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(condition, Run).WillOnce(testing::Return(true));
EXPECT_CALL(aborted, Run);
RunTestSequenceInContext(
e1.context(),
If(condition.Get(), Then(Check(base::BindOnce([]() { return false; })))));
}
TEST_F(InteractiveTestTest, IfThenElse_OnlyRunsThen) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, a);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, b);
EXPECT_CALL(condition, Run).WillOnce(testing::Return(true));
EXPECT_CALL(a, Run);
RunTestSequenceInContext(
kTestContext1, If(condition.Get(), Then(Do(a.Get())), Else(Do(b.Get()))));
}
TEST_F(InteractiveTestTest, IfThenElse_OnlyRunsElse) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, a);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, b);
EXPECT_CALL(condition, Run).WillOnce(testing::Return(false));
EXPECT_CALL(b, Run);
RunTestSequenceInContext(
kTestContext1, If(condition.Get(), Then(Do(a.Get())), Else(Do(b.Get()))));
}
TEST_F(InteractiveTestTest, IfThenElse_ThenFails) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL(condition, Run).WillOnce(testing::Return(true));
EXPECT_CALL(aborted, Run);
RunTestSequenceInContext(
kTestContext1,
If(condition.Get(), Then(Check(base::BindOnce([]() { return false; }))),
Else(Do(base::BindOnce([]() {})))));
}
TEST_F(InteractiveTestTest, IfThenElse_ElseFails) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<bool(void)>, condition);
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
EXPECT_CALL(condition, Run).WillOnce(testing::Return(false));
EXPECT_CALL(aborted, Run);
RunTestSequenceInContext(
kTestContext1, If(condition.Get(), Then(Do(base::BindOnce([]() {}))),
Else(Check(base::BindOnce([]() { return false; })))));
}
TEST_F(InteractiveTestTest, InParallel) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq1);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq2);
EXPECT_CALL(seq1, Run);
EXPECT_CALL(seq2, Run);
RunTestSequenceInContext(kTestContext1,
InParallel(RunSubsequence(Do(seq1.Get())),
RunSubsequence(Do(seq2.Get()))));
}
TEST_F(InteractiveTestTest, InParallelMultiStep) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq11);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq12);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq21);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq22);
EXPECT_CALL(seq11, Run);
EXPECT_CALL(seq12, Run);
EXPECT_CALL(seq21, Run);
EXPECT_CALL(seq22, Run);
RunTestSequenceInContext(
kTestContext1,
InParallel(RunSubsequence(Do(seq11.Get()), Do(seq12.Get())),
RunSubsequence(Do(seq21.Get()), Do(seq22.Get()))));
}
TEST_F(InteractiveTestTest, InParallelAsync) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq1);
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq2);
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
QueueActions([&e1]() { e1.Show(); }, [&e2]() { e2.Show(); });
EXPECT_CALL(seq1, Run(&e1));
EXPECT_CALL(seq2, Run(&e2));
RunTestSequenceInContext(
kTestContext1,
InParallel(RunSubsequence(AfterShow(e1.identifier(), seq1.Get())),
RunSubsequence(AfterShow(e2.identifier(), seq2.Get()))));
}
// Parallel sequences where one sequence triggers a step in another.
TEST_F(InteractiveTestTest, InParallelDependent) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq1);
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq2);
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
QueueActions([&e1]() { e1.Show(); });
EXPECT_CALL(seq1, Run(&e1)).WillOnce([&e2](TrackedElement*) { e2.Show(); });
EXPECT_CALL(seq2, Run(&e2));
RunTestSequenceInContext(
kTestContext1,
InParallel(RunSubsequence(AfterShow(e1.identifier(), seq1.Get())),
RunSubsequence(AfterShow(e2.identifier(), seq2.Get()))));
}
// Parallel sequences where one sequence triggers a step in another, which then
// triggers the final step in the first subsequence.
TEST_F(InteractiveTestTest, InParallelPingPong) {
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq1);
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq2);
UNCALLED_MOCK_CALLBACK(base::OnceCallback<void(ui::TrackedElement*)>, seq3);
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
QueueActions([&e1]() { e1.Show(); });
EXPECT_CALL(seq1, Run(&e1)).WillOnce([&e2](TrackedElement*) { e2.Show(); });
EXPECT_CALL(seq2, Run(&e2)).WillOnce([&e1](TrackedElement*) {
e1.SendCustomEvent(kTestEvent1);
});
EXPECT_CALL(seq3, Run(&e1));
RunTestSequenceInContext(
kTestContext1,
InParallel(
RunSubsequence(AfterShow(e1.identifier(), seq1.Get()),
AfterEvent(e1.identifier(), kTestEvent1, seq3.Get())),
RunSubsequence(AfterShow(e2.identifier(), seq2.Get()))));
}
TEST_F(InteractiveTestTest, InParallelFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(aborted, Run);
RunTestSequenceInContext(
e1.context(),
InParallel(
RunSubsequence(Do(base::DoNothing())),
RunSubsequence(Check(base::BindOnce([]() { return false; })))));
}
TEST_F(InteractiveTestTest, AnyOf) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq1);
EXPECT_CALL(seq1, Run).Times(1);
RunTestSequenceInContext(
kTestContext1,
AnyOf(RunSubsequence(Do(seq1.Get())), RunSubsequence(Do(seq1.Get()))));
}
TEST_F(InteractiveTestTest, AnyOfOneFailsOneSucceeds) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, seq1);
EXPECT_CALL(seq1, Run).Times(1);
RunTestSequenceInContext(
kTestContext1,
AnyOf(RunSubsequence(Check(base::BindOnce([]() { return false; }))),
RunSubsequence(Do(seq1.Get()))));
}
TEST_F(InteractiveTestTest, AnyOfAllFail) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
TestElement e1(kTestId1, kTestContext1);
e1.Show();
EXPECT_CALL(aborted, Run);
RunTestSequenceInContext(
e1.context(),
InParallel(
RunSubsequence(Check(base::BindOnce([]() { return false; }))),
RunSubsequence(Check(base::BindOnce([]() { return false; })))));
}
// This is a regression test for an issue where there is a UAF when tearing down
// an AnyOf() inside an If().
TEST_F(InteractiveTestTest, AnyOfInsideIf) {
TestElement el(kTestId1, kTestContext1);
QueueActions([&el]() { el.Show(); },
[&el]() { el.SendCustomEvent(kTestEvent1); });
RunTestSequenceInContext(
kTestContext1,
If([]() { return true; },
Then(AnyOf(
RunSubsequence(std::move(WaitForEvent(kTestId1, kTestEvent1)
.SetMustBeVisibleAtStart(false))),
RunSubsequence(WaitForShow(kTestId1),
WaitForEvent(kTestId1, kTestEvent2))))));
}
// This test that various types of logging can compile with different types of
// parameters. The output of this test must be verified manually.
TEST_F(InteractiveTestTest, Log) {
TestElement e1(kTestId1, kTestContext1);
int x = 1;
int y = 0;
constexpr char kSomeString[] = "A string.";
std::u16string deferred_string1;
const char* deferred_string2;
struct {
bool b = false;
} unnamed_struct, *unnamed_struct_ptr = nullptr;
RunTestSequenceInContext(
e1.context(), Do([&]() {
y = 2;
deferred_string1 = u"The quick brown fox";
deferred_string2 = "Lorem ipsum";
unnamed_struct_ptr = &unnamed_struct;
}),
Log(
"Log() output follows:\nliteral int: ", x,
"\ndeferred int: ", std::ref(y), "\nconstexpr string: ", kSomeString,
"\ndeferred string 1: ", std::ref(deferred_string1),
"\ndeferred string 2: ", std::ref(deferred_string2),
"\npointer to object: ", &unnamed_struct,
"\ndeferred pointer to object: ", std::ref(unnamed_struct_ptr),
"\nlambda - should be 7: ", [x, &y]() { return x + y + 4; },
"\nOnceCallback - should be 3: ",
base::BindOnce([](int x, int* y) { return x + *y; }, x,
base::Unretained(&y)),
"\nRepeatingCallback - should be 4: ",
base::BindRepeating([](int x, int* y) { return x + *y + 1; }, x,
base::Unretained(&y)),
"\nfunction pointer - should be 5: ", &ValueGeneratingFunction));
}
// This test that the element tree can be dumped.
// The output of this test must be checked manually.
TEST_F(InteractiveTestTest, DumpElements) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
TestElement e3(kTestId1, kTestContext1);
TestElement e4(kTestId3, kTestContext1);
TestElement e5(kTestId1, kTestContext2);
TestElement e6(kTestId2, kTestContext2);
TestElement e7(kTestId3, kTestContext2);
e1.Show();
e2.Show();
e3.Show();
e4.Show();
e5.Show();
// e6 not shown
e7.Show();
RunTestSequenceInContext(e1.context(), DumpElements());
}
// This test that the element tree can be dumped.
// The output of this test must be checked manually.
TEST_F(InteractiveTestTest, DumpElementsInContext) {
TestElement e1(kTestId1, kTestContext1);
TestElement e2(kTestId2, kTestContext1);
TestElement e3(kTestId1, kTestContext1);
TestElement e4(kTestId3, kTestContext1);
TestElement e5(kTestId1, kTestContext2);
TestElement e6(kTestId2, kTestContext2);
TestElement e7(kTestId3, kTestContext2);
e1.Show();
e2.Show();
e3.Show();
e4.Show();
e5.Show();
// e6 not shown
e7.Show();
RunTestSequenceInContext(e1.context(), DumpElementsInContext(),
InContext(e5.context(), DumpElementsInContext()));
}
// This test ensures that binding of various types of functions and function
// arguments works correctly with actions. If the template logic is not correct,
// this test will likely not compile.
TEST_F(InteractiveTestTest, ActionBindingMethods) {
TestElement e1(kTestId1, kTestContext1);
e1.Show();
CallableObject callable{2};
MutableCallableObject mutable_callable{0};
EmptyCallableObject empty_callable;
auto lambda = []() { LOG(INFO) << "Stored lambda."; };
auto once_callback = base::BindOnce([]() { LOG(INFO) << "Once callback."; });
auto repeating_callback =
base::BindRepeating([]() { LOG(INFO) << "Repeating callback."; });
int x = 1;
int y = 2;
RunTestSequenceInContext(
e1.context(),
// Check all of the various ways to bind methods with Do().
Do(base::DoNothing()), Do(&DoFunction), Do(lambda),
Do(std::move(once_callback)), Do(repeating_callback),
Do([x, &y]() { LOG(INFO) << "Bound args " << x << ", " << y; }),
Do(empty_callable),
// Check various ways to verify a return value.
Check(base::BindOnce([]() { return true; })),
Check([]() { return true; }), CheckResult([x, &y]() { return x + y; }, 3),
Check(callable), CheckResult(std::move(mutable_callable), false),
CheckElement(
e1.identifier(), [](TrackedElement* el) { return el; }, &e1),
// Verify that argument list reduction works with bare callbacks.
AfterShow(e1.identifier(),
[&e1](TrackedElement* el) { EXPECT_EQ(el, &e1); }),
WithElement(e1.identifier(), []() {}),
WithElement(e1.identifier(),
[](InteractionSequence*, TrackedElement*) {}));
}
// This test ensures that binding of various types of functions and function
// arguments works correctly with conditionals. If the template logic is not
// correct, this test will likely not compile.
TEST_F(InteractiveTestTest, ConditionalBindingMethods) {
UNCALLED_MOCK_CALLBACK(base::OnceClosure, correct);
UNCALLED_MOCK_CALLBACK(base::OnceClosure, incorrect);
TestElement e1(kTestId1, kTestContext1);
e1.Show();
int x = 1;
int y = 2;
EXPECT_CALL(correct, Run).Times(4);
RunTestSequenceInContext(
e1.context(),
If([]() { return true; }, Then(Do(correct.Get())),
Else(Do(incorrect.Get()))),
IfMatches([x, &y]() { return x + y; }, 2, Then(Do(incorrect.Get())),
Else(Do(correct.Get()))),
IfElement(
e1.identifier(),
[&e1](const TrackedElement* el) { return el == &e1; },
Then(Do(correct.Get())), Else(Do(incorrect.Get()))),
IfElementMatches(kTestId2, &CheckElementFunction, testing::Ne(nullptr),
Then(Do(incorrect.Get())), Else(Do(correct.Get()))));
}
namespace {
template <typename T>
class TestObservable;
template <typename T>
class TestObserver : public base::CheckedObserver {
public:
TestObserver() = default;
~TestObserver() override = default;
virtual void OnObservableValueChanged(TestObservable<T>* observable,
T value) {}
virtual void OnObservableDestroying(TestObservable<T>* observable) {}
};
template <typename T>
class TestObservable {
public:
explicit TestObservable(T value) : value_(value) {}
~TestObservable() {
observers_.Notify(&TestObserver<T>::OnObservableDestroying, this);
}
T value() const { return value_; }
void SetValue(T value) {
value_ = value;
observers_.Notify(&TestObserver<T>::OnObservableValueChanged, this, value);
}
void AddObserver(TestObserver<T>* observer) {
observers_.AddObserver(observer);
}
void RemoveObserver(TestObserver<T>* observer) {
observers_.RemoveObserver(observer);
}
private:
T value_ = 0;
base::ObserverList<TestObserver<T>> observers_;
};
template <typename T>
class TestStateObserver
: public ObservationStateObserver<T, TestObservable<T>, TestObserver<T>> {
public:
using Base = ObservationStateObserver<T, TestObservable<T>, TestObserver<T>>;
explicit TestStateObserver(TestObservable<T>* observable)
: Base(observable) {}
~TestStateObserver() override = default;
// ObservationStateObserver:
T GetStateObserverInitialState() const override {
return Base::source()->value();
}
// TestObserver:
void OnObservableValueChanged(TestObservable<T>* observable,
T value) override {
Base::OnStateObserverStateChanged(value);
}
void OnObservableDestroying(TestObservable<T>* observable) override {
Base::OnObservationStateObserverSourceDestroyed();
}
};
struct MyStruct {
MyStruct() : my_int(0), my_bool(false) {}
MyStruct(int a, bool b) : my_int(a), my_bool(b) {}
MyStruct(const MyStruct&) = default;
bool operator==(const MyStruct& b) const = default;
MyStruct& operator=(const MyStruct& b) = default;
int my_int;
bool my_bool;
};
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(TestStateObserver<int>, kIntTestState);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(TestStateObserver<std::string>,
kStringTestState);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(TestStateObserver<std::u16string>,
kWStringTestState);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(TestStateObserver<MyStruct>,
kStructTestState);
} // namespace
TEST_F(InteractiveTestTest, ObserveStateFromUniquePtr) {
TestObservable<int> observable(2);
QueueActions([&observable]() { observable.SetValue(0); },
[&observable]() { observable.SetValue(3); },
[&observable]() { observable.SetValue(1); });
RunTestSequenceInContext(
kTestContext1,
ObserveState(kIntTestState,
std::make_unique<TestStateObserver<int>>(&observable)),
WaitForState(kIntTestState, 3),
CheckResult([&observable]() { return observable.value(); }, 3),
WaitForState(kIntTestState, testing::Ne(3)),
CheckResult([&observable]() { return observable.value(); }, 1));
}
TEST_F(InteractiveTestTest, ObserveExistingStateProceedsImmediately) {
TestObservable<int> observable(2);
RunTestSequenceInContext(kTestContext1,
ObserveState(kIntTestState, &observable),
WaitForState(kIntTestState, 2));
}
TEST_F(InteractiveTestTest, ObserveStateFromArgsWithReferences) {
TestObservable<int> observable(2);
int target = 0;
TestObservable<int>* obs_ptr = nullptr;
QueueActions([&observable]() { observable.SetValue(0); },
[&observable]() { observable.SetValue(3); });
RunTestSequenceInContext(
kTestContext1, Do([&]() {
target = 3;
obs_ptr = &observable;
}),
ObserveState(kIntTestState, std::ref(obs_ptr)),
WaitForState(kIntTestState, std::ref(target)),
CheckResult([&observable]() { return observable.value(); }, 3));
}
TEST_F(InteractiveTestTest, ObserveStateFromArgsWithFunctions) {
TestObservable<int> observable(2);
QueueActions([&observable]() { observable.SetValue(0); },
[&observable]() { observable.SetValue(3); });
RunTestSequenceInContext(
kTestContext1, ObserveState(kIntTestState, [&]() { return &observable; }),
WaitForState(kIntTestState, base::BindRepeating([]() { return 3; })),
CheckResult([&observable]() { return observable.value(); }, 3));
}
TEST_F(InteractiveTestTest, ObserveStateResetsOnDestruction) {
auto observable = std::make_unique<TestObservable<int>>(2);
QueueActions([&observable]() { observable->SetValue(0); },
[&observable]() { observable->SetValue(3); },
[&observable]() { observable.reset(); });
RunTestSequenceInContext(kTestContext1,
ObserveState(kIntTestState, observable.get()),
WaitForState(kIntTestState, 3),
WaitForState(kIntTestState, testing::Ne(3)));
}
TEST_F(InteractiveTestTest, ObserveStateWithString) {
TestObservable<std::string> observable("foo");
static const char* const kBar = "bar";
constexpr char kBaz[] = "baz";
QueueActions([&]() { observable.SetValue(kBar); },
[&]() { observable.SetValue(kBaz); });
RunTestSequenceInContext(kTestContext1,
ObserveState(kStringTestState, &observable),
WaitForState(kStringTestState, kBar),
WaitForState(kStringTestState, testing::Eq(kBaz)));
}
TEST_F(InteractiveTestTest, ObserveStateWithWideString) {
TestObservable<std::u16string> observable(u"foo");
static const char16_t* const kBar = u"bar";
constexpr char16_t kBaz[] = u"baz";
QueueActions([&]() { observable.SetValue(kBar); },
[&]() { observable.SetValue(kBaz); });
RunTestSequenceInContext(kTestContext1,
ObserveState(kWStringTestState, &observable),
WaitForState(kWStringTestState, kBar),
WaitForState(kWStringTestState, testing::Eq(kBaz)));
}
TEST_F(InteractiveTestTest, ObserveStateWithStruct) {
TestObservable<MyStruct> observable(MyStruct(0, false));
QueueActions([&]() { observable.SetValue(MyStruct(123, false)); });
RunTestSequenceInContext(
kTestContext1, ObserveState(kStructTestState, &observable),
WaitForState(kStructTestState, testing::Field(&MyStruct::my_int, 123)));
}
TEST_F(InteractiveTestTest, StopObservingState) {
TestObservable<int> observable(1);
RunTestSequenceInContext(
kTestContext1,
ObserveState(kIntTestState,
std::make_unique<TestStateObserver<int>>(&observable)),
WaitForState(kIntTestState, 1), StopObservingState(kIntTestState),
EnsureNotPresent(kIntTestState.identifier()),
Check([this]() { return state_observers().empty(); }));
}
TEST_F(InteractiveTestTest, CheckStateSucceeds) {
TestObservable<std::string> observable("foo");
static const char* const kBar = "bar";
QueueActions([&]() { observable.SetValue(kBar); });
RunTestSequenceInContext(
kTestContext1, ObserveState(kStringTestState, &observable),
WaitForState(kStringTestState, kBar), CheckState(kStringTestState, kBar),
CheckState(kStringTestState, testing::Ne("foo")));
}
TEST_F(InteractiveTestTest, CheckStateFails) {
UNCALLED_MOCK_CALLBACK(InteractionSequence::AbortedCallback, aborted);
private_test_impl().set_aborted_callback_for_testing(aborted.Get());
TestObservable<std::string> observable("foo");
static const char* const kBar = "bar";
EXPECT_CALL_IN_SCOPE(
aborted, Run,
RunTestSequenceInContext(kTestContext1,
ObserveState(kStringTestState, &observable),
CheckState(kStringTestState, kBar)));
}
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(PollingStateObserver<int>,
kPollingTestState);
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(PollingStateObserver<int>,
kPollingTestState2);
TEST_F(InteractiveTestTest, PollingStateObserver) {
UNCALLED_MOCK_CALLBACK(PollingStateObserver<int>::PollCallback, poll_cb);
EXPECT_CALL(poll_cb, Run)
.WillOnce(testing::Return(0))
.WillOnce(testing::Return(1));
RunTestSequenceInContext(
kTestContext1,
PollState(kPollingTestState, poll_cb.Get(), base::Milliseconds(50)),
WaitForState(kPollingTestState, 1));
}
// Tests that the callback is issued exactly once to determine its initial
// state, and not triggered unnecessarily once the desired condition is met.
TEST_F(InteractiveTestTest, PollingStateCalledOnce) {
UNCALLED_MOCK_CALLBACK(PollingStateObserver<int>::PollCallback, callback);
EXPECT_CALL(callback, Run).Times(1).WillOnce(testing::Return(1));
RunTestSequenceInContext(kTestContext1,
PollState(kPollingTestState, callback.Get()),
WaitForState(kPollingTestState, 1));
EXPECT_CALL(callback, Run).Times(0);
}
DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(PollingElementStateObserver<std::string>,
kPollingElementTestState);
TEST_F(InteractiveTestTest, PollingElementStateObserver) {
UNCALLED_MOCK_CALLBACK(
PollingElementStateObserver<std::string>::PollElementCallback, poll_cb);
TestElement el(kTestId1, kTestContext1);
EXPECT_CALL(poll_cb, Run(&el))
.WillOnce(testing::Return(std::string("foo")))
.WillOnce(testing::Return(std::string("bar")))
.WillOnce(testing::Return(std::string("baz")));
// Start with the element not visible, then show it.
QueueActions([&el] { el.Show(); });
RunTestSequenceInContext(
kTestContext1,
PollElement(kPollingElementTestState, el.identifier(), poll_cb.Get(),
base::Milliseconds(50)),
WaitForState(kPollingElementTestState, "baz"));
}
TEST_F(InteractiveTestTest, PollStateUntil) {
UNCALLED_MOCK_CALLBACK(PollingStateObserver<int>::PollCallback, poll_cb);
EXPECT_CALL(poll_cb, Run)
.WillOnce(testing::Return(0))
.WillOnce(testing::Return(1));
RunTestSequenceInContext(
kTestContext1, PollStateUntil(kPollingTestState, poll_cb.Get(),
testing::Gt(0), base::Milliseconds(50)));
}
TEST_F(InteractiveTestTest, PollStateUntilRepeatedly) {
UNCALLED_MOCK_CALLBACK(PollingStateObserver<int>::PollCallback, poll_cb);
EXPECT_CALL(poll_cb, Run)
.WillOnce(testing::Return(0))
.WillOnce(testing::Return(1))
.WillOnce(testing::Return(0))
.WillOnce(testing::Return(-1));
RunTestSequenceInContext(
kTestContext1,
PollStateUntil(kPollingTestState, poll_cb.Get(), testing::Gt(0),
base::Milliseconds(50)),
PollStateUntil(kPollingTestState, poll_cb.Get(), testing::Lt(0),
base::Milliseconds(50)));
}
TEST_F(InteractiveTestTest, PollStateUntilInParallel) {
const auto kPollTime = base::Milliseconds(50);
UNCALLED_MOCK_CALLBACK(PollingStateObserver<int>::PollCallback, poll_cb);
UNCALLED_MOCK_CALLBACK(PollingStateObserver<int>::PollCallback, poll_cb2);
EXPECT_CALL(poll_cb, Run)
.WillOnce(testing::Return(0))
.WillOnce(testing::Return(1));
EXPECT_CALL(poll_cb2, Run)
.WillOnce(testing::Return(0))
.WillOnce(testing::Return(-1));
RunTestSequenceInContext(
kTestContext1,
InParallel(
RunSubsequence(PollStateUntil(kPollingTestState, poll_cb.Get(),
testing::Gt(0), kPollTime)),
RunSubsequence(PollStateUntil(kPollingTestState2, poll_cb2.Get(),
testing::Lt(0), kPollTime))));
}
TEST_F(InteractiveTestTest, PollUntil) {
int count = 0;
RunTestSequenceInContext(
kTestContext1,
PollUntil([&count]() { return count++ > 1; }, "Polling integer"));
}
TEST_F(InteractiveTestTest, PollUntilRepeatedly) {
int count1 = 0;
int count2 = 0;
RunTestSequenceInContext(
kTestContext1, PollUntil([&count1]() { return count1++ > 2; }, "count1"),
PollUntil([&count2]() { return count2++ > 1; }, "count2"));
}
TEST_F(InteractiveTestTest, SubsequenceHidesElement) {
TestElement el1(kTestId1, kTestContext1);
TestElement el2(kTestId2, kTestContext1);
QueueActions([&]() { el1.Show(); }, [&]() { el2.Show(); });
RunTestSequenceInContext(
kTestContext1, WaitForShow(el1.identifier()),
InParallel(RunSubsequence(Do([&el1]() { el1.Hide(); })),
RunSubsequence(WaitForShow(el2.identifier()))));
}
namespace {
static constexpr char kAdditionalContext1[] = "context1";
static constexpr char kAdditionalContext2[] = "context2";
} // namespace
TEST_F(InteractiveTestTest, SetAndClearAdditionalContext) {
AdditionalContext context = private_test_impl().CreateAdditionalContext();
RunTestSequenceInContext(
kTestContext1,
// Verify the context is empty.
Check([this]() {
return private_test_impl().GetAdditionalContext().empty();
}),
CheckResult([context]() { return context.Get(); }, ""),
// Set context and verify value across steps.
CheckResult(
[this, context]() mutable {
context.Set(kAdditionalContext1);
return private_test_impl().GetAdditionalContext();
},
testing::Contains(kAdditionalContext1)),
CheckResult([context]() { return context.Get(); }, kAdditionalContext1),
CheckResult(
[this]() { return private_test_impl().GetAdditionalContext(); },
testing::Contains(kAdditionalContext1)),
// Clear context and verify value across steps.
Check([this, context]() mutable {
context.Clear();
return private_test_impl().GetAdditionalContext().empty();
}),
Check([this]() {
return private_test_impl().GetAdditionalContext().empty();
}),
CheckResult([context]() { return context.Get(); }, ""),
// Set value again and verify value across steps.
CheckResult(
[this, context]() mutable {
context.Set(kAdditionalContext1);
return private_test_impl().GetAdditionalContext();
},
testing::Contains(kAdditionalContext1)),
CheckResult(
[this]() { return private_test_impl().GetAdditionalContext(); },
testing::Contains(kAdditionalContext1)),
CheckResult([context]() { return context.Get(); }, kAdditionalContext1));
}
TEST_F(InteractiveTestTest, AdditionalContextNotCleared) {
AdditionalContext context = private_test_impl().CreateAdditionalContext();
RunTestSequenceInContext(
kTestContext1,
// Set context.
Do([context]() mutable { context.Set(kAdditionalContext1); }));
EXPECT_EQ(kAdditionalContext1, context.Get());
EXPECT_THAT(private_test_impl().GetAdditionalContext(),
testing::ElementsAre(kAdditionalContext1));
}
TEST_F(InteractiveTestTest, DestructAdditionalContext) {
// Create a custom verb that has a local context.
auto custom_verb = [this]() {
AdditionalContext context = private_test_impl().CreateAdditionalContext();
return Steps(
CheckResult(
[this, context]() mutable {
context.Set(kAdditionalContext1);
return private_test_impl().GetAdditionalContext();
},
testing::Contains(kAdditionalContext1)),
CheckResult(
[this, context]() {
return private_test_impl().GetAdditionalContext();
},
testing::Contains(kAdditionalContext1)),
CheckResult([context]() { return context.Get(); }, kAdditionalContext1),
Do([context]() mutable { context.Clear(); }));
};
RunTestSequenceInContext(
kTestContext1,
// Run the custom verb.
custom_verb(),
// After the verb has completed, there are no more references to the
// context.
Check([this]() {
return private_test_impl().GetAdditionalContext().empty();
}));
}
TEST_F(InteractiveTestTest, TwoAdditionalContexts) {
// Create a custom verb that has a local context. This will be called from
// inside `custom_verb()` below.
auto custom_verb2 = [this]() {
AdditionalContext context = private_test_impl().CreateAdditionalContext();
// The context for both this and the outer verb will be active.
auto expected =
testing::ElementsAre(kAdditionalContext1, kAdditionalContext2);
return Steps(CheckResult(
[this, context]() mutable {
context.Set(kAdditionalContext2);
return private_test_impl().GetAdditionalContext();
},
expected),
CheckResult(
[this, context]() {
return private_test_impl().GetAdditionalContext();
},
expected),
Do([context]() mutable { context.Clear(); }));
};
// Create a custom verb that has a local context and calls another verb with a
// local context.
auto custom_verb = [this, &custom_verb2]() {
AdditionalContext context = private_test_impl().CreateAdditionalContext();
return Steps(Do([context]() mutable { context.Set(kAdditionalContext1); }),
custom_verb2(),
// Outside of custom_verb(), only our context exists.
CheckResult(
[this, context]() {
return private_test_impl().GetAdditionalContext();
},
testing::Contains(kAdditionalContext1)),
Do([context]() mutable { context.Clear(); }));
};
RunTestSequenceInContext(
kTestContext1,
// Run the custom verb.
custom_verb(),
// After the verb has completed, there are no more references to the
// context.
Check([this]() {
return private_test_impl().GetAdditionalContext().empty();
}));
}
} // namespace ui::test