blob: 4b45a975954f4bdd0053bd168787cc3e8530a556 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "components/autofill_assistant/browser/batch_element_checker.h"
#include <map>
#include <set>
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "components/autofill_assistant/browser/mock_run_once_callback.h"
#include "components/autofill_assistant/browser/mock_web_controller.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::Contains;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Key;
using ::testing::Not;
using ::testing::Pair;
namespace autofill_assistant {
namespace {
static constexpr base::TimeDelta kTimeUnit = base::TimeDelta::FromSeconds(1);
class BatchElementCheckerTest : public testing::Test {
protected:
BatchElementCheckerTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
checks_(&mock_web_controller_) {}
void OnElementExistenceCheck(const std::string& name, bool result) {
element_exists_results_[name] = result;
}
BatchElementChecker::ElementCheckCallback ElementExistenceCallback(
const std::string& name) {
return base::BindOnce(&BatchElementCheckerTest::OnElementExistenceCheck,
base::Unretained(this), name);
}
void OnElementVisibilityCheck(const std::string& name, bool result) {
element_visible_results_[name] = result;
}
BatchElementChecker::ElementCheckCallback ElementVisibilityCallback(
const std::string& name) {
return base::BindOnce(&BatchElementCheckerTest::OnElementVisibilityCheck,
base::Unretained(this), name);
}
void OnFieldValueCheck(const std::string& name,
bool exists,
const std::string& value) {
get_field_value_results_[name] = value;
}
BatchElementChecker::GetFieldValueCallback FieldValueCallback(
const std::string& name) {
return base::BindOnce(&BatchElementCheckerTest::OnFieldValueCheck,
base::Unretained(this), name);
}
void OnDone(const std::string& name) { all_done_.insert(name); }
base::OnceCallback<void()> DoneCallback(const std::string& name) {
return base::BindOnce(&BatchElementCheckerTest::OnDone,
base::Unretained(this), name);
}
void OnTry(const std::string& name) { ++try_done_[name]; }
base::RepeatingCallback<void()> TryCallback(const std::string& name) {
return base::BindRepeating(&BatchElementCheckerTest::OnTry,
base::Unretained(this), name);
}
void AdvanceTime() { scoped_task_environment_.FastForwardBy(kTimeUnit); }
void RunOnce(const std::string& callback_name) {
checks_.Run(base::TimeDelta::FromMilliseconds(0), base::DoNothing(),
DoneCallback(callback_name));
}
// scoped_task_environment_ must be first to guarantee other field
// creation run in that environment.
base::test::ScopedTaskEnvironment scoped_task_environment_;
MockWebController mock_web_controller_;
BatchElementChecker checks_;
std::map<std::string, bool> element_exists_results_;
std::map<std::string, bool> element_visible_results_;
std::map<std::string, std::string> get_field_value_results_;
std::set<std::string> all_done_;
std::map<std::string, int> try_done_;
};
TEST_F(BatchElementCheckerTest, AllFoundIfEmpty) {
EXPECT_TRUE(checks_.all_found());
checks_.AddElementCheck(kExistenceCheck, Selector({"exists"}),
ElementExistenceCallback("exists"));
EXPECT_FALSE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, OneElementFound) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"exists"})), _))
.WillOnce(RunOnceCallback<2>(true));
checks_.AddElementCheck(kExistenceCheck, Selector({"exists"}),
ElementExistenceCallback("exists"));
RunOnce("run_once");
EXPECT_THAT(element_exists_results_, Contains(Pair("exists", true)));
EXPECT_THAT(all_done_, Contains("run_once"));
EXPECT_TRUE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, OneElementNotFound) {
EXPECT_CALL(
mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"does_not_exist"})), _))
.WillOnce(RunOnceCallback<2>(false));
checks_.AddElementCheck(kExistenceCheck, Selector({"does_not_exist"}),
ElementExistenceCallback("does_not_exist"));
RunOnce("run_once");
EXPECT_THAT(element_exists_results_, Contains(Pair("does_not_exist", false)));
EXPECT_THAT(all_done_, Contains("run_once"));
EXPECT_FALSE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, OneFieldValueFound) {
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"field"})), _))
.WillOnce(RunOnceCallback<1>(true, "some value"));
checks_.AddFieldValueCheck(Selector({"field"}), FieldValueCallback("field"));
RunOnce("run_once");
EXPECT_THAT(get_field_value_results_, Contains(Pair("field", "some value")));
EXPECT_THAT(all_done_, Contains("run_once"));
EXPECT_TRUE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, OneFieldValueNotFound) {
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"field"})), _))
.WillOnce(RunOnceCallback<1>(false, ""));
checks_.AddFieldValueCheck(Selector({"field"}), FieldValueCallback("field"));
RunOnce("run_once");
EXPECT_THAT(get_field_value_results_, Contains(Pair("field", "")));
EXPECT_THAT(all_done_, Contains("run_once"));
EXPECT_FALSE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, OneFieldValueEmpty) {
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"field"})), _))
.WillOnce(RunOnceCallback<1>(true, ""));
checks_.AddFieldValueCheck(Selector({"field"}), FieldValueCallback("field"));
RunOnce("run_once");
EXPECT_THAT(get_field_value_results_, Contains(Pair("field", "")));
EXPECT_THAT(all_done_, Contains("run_once"));
EXPECT_TRUE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, MultipleElements) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"1"})), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"2"})), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"3"})), _))
.WillOnce(RunOnceCallback<2>(false));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"4"})), _))
.WillOnce(RunOnceCallback<1>(true, "value"));
EXPECT_CALL(mock_web_controller_, OnGetFieldValue(Eq(Selector({"5"})), _))
.WillOnce(RunOnceCallback<1>(false, ""));
checks_.AddElementCheck(kExistenceCheck, Selector({"1"}),
ElementExistenceCallback("1"));
checks_.AddElementCheck(kExistenceCheck, Selector({"2"}),
ElementExistenceCallback("2"));
checks_.AddElementCheck(kExistenceCheck, Selector({"3"}),
ElementExistenceCallback("3"));
checks_.AddFieldValueCheck(Selector({"4"}), FieldValueCallback("4"));
checks_.AddFieldValueCheck(Selector({"5"}), FieldValueCallback("5"));
RunOnce("run_once");
EXPECT_THAT(element_exists_results_, Contains(Pair("1", true)));
EXPECT_THAT(element_exists_results_, Contains(Pair("2", true)));
EXPECT_THAT(element_exists_results_, Contains(Pair("3", false)));
EXPECT_THAT(get_field_value_results_, Contains(Pair("4", "value")));
EXPECT_THAT(get_field_value_results_, Contains(Pair("5", "")));
EXPECT_THAT(all_done_, Contains("run_once"));
EXPECT_FALSE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, DeduplicateElementExists) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"1"})), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"2"})), _))
.WillOnce(RunOnceCallback<2>(true));
checks_.AddElementCheck(kExistenceCheck, Selector({"1"}),
ElementExistenceCallback("first 1"));
checks_.AddElementCheck(kExistenceCheck, Selector({"1"}),
ElementExistenceCallback("second 1"));
checks_.AddElementCheck(kExistenceCheck, Selector({"2"}),
ElementExistenceCallback("2"));
RunOnce("run_once");
EXPECT_THAT(element_exists_results_, Contains(Pair("first 1", true)));
EXPECT_THAT(element_exists_results_, Contains(Pair("second 1", true)));
EXPECT_THAT(element_exists_results_, Contains(Pair("2", true)));
EXPECT_THAT(all_done_, Contains("run_once"));
}
TEST_F(BatchElementCheckerTest, DeduplicateElementVisible) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kVisibilityCheck, Eq(Selector({"1"})), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kVisibilityCheck, Eq(Selector({"2"})), _))
.WillOnce(RunOnceCallback<2>(true));
checks_.AddElementCheck(kVisibilityCheck, Selector({"1"}),
ElementVisibilityCallback("first 1"));
checks_.AddElementCheck(kVisibilityCheck, Selector({"1"}),
ElementVisibilityCallback("second 1"));
checks_.AddElementCheck(kVisibilityCheck, Selector({"2"}),
ElementVisibilityCallback("2"));
RunOnce("run_once");
EXPECT_THAT(element_visible_results_, Contains(Pair("first 1", true)));
EXPECT_THAT(element_visible_results_, Contains(Pair("second 1", true)));
EXPECT_THAT(element_visible_results_, Contains(Pair("2", true)));
EXPECT_THAT(all_done_, Contains("run_once"));
}
// Deduplicate get field
TEST_F(BatchElementCheckerTest, EventuallyFindAll) {
{
InSequence seq;
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"1"})), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"2"})), _))
.WillOnce(RunOnceCallback<2>(false))
.WillOnce(RunOnceCallback<2>(true));
}
checks_.AddElementCheck(kExistenceCheck, Selector({"1"}),
ElementExistenceCallback("1"));
checks_.AddElementCheck(kExistenceCheck, Selector({"2"}),
ElementExistenceCallback("2"));
checks_.Run(base::TimeDelta::FromSeconds(10), base::DoNothing(),
DoneCallback("all_done"));
// The first try should have run, not fully successful, and should now be
// waiting for the second try.
EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask());
EXPECT_THAT(element_exists_results_, Contains(Pair("1", true)));
EXPECT_THAT(element_exists_results_, Not(Contains(Key("2"))));
EXPECT_THAT(all_done_, Not(Contains("all_done")));
EXPECT_FALSE(checks_.all_found());
// The second try should have found 2 and finished.
AdvanceTime();
EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask());
EXPECT_THAT(element_exists_results_, Contains(Pair("2", true)));
EXPECT_THAT(all_done_, Contains("all_done"));
EXPECT_TRUE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, EventuallyFindSome) {
{
InSequence seq;
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"1"})), _))
.WillOnce(RunOnceCallback<2>(true));
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"2"})), _))
.Times(3)
.WillRepeatedly(RunOnceCallback<2>(false));
}
checks_.AddElementCheck(kExistenceCheck, Selector({"1"}),
ElementExistenceCallback("1"));
checks_.AddElementCheck(kExistenceCheck, Selector({"2"}),
ElementExistenceCallback("2"));
checks_.Run(3 * kTimeUnit, base::DoNothing(), DoneCallback("all_done"));
// The first try should have run, not fully successful, and should now be
// waiting for the second try.
EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask());
EXPECT_THAT(element_exists_results_, Contains(Pair("1", true)));
EXPECT_THAT(element_exists_results_, Not(Contains(Key("2"))));
EXPECT_THAT(all_done_, Not(Contains("all_done")));
// The second try still doesn't work'
AdvanceTime();
EXPECT_TRUE(scoped_task_environment_.MainThreadHasPendingTask());
EXPECT_THAT(element_exists_results_, Not(Contains(Key("2"))));
// Give up after the third try
AdvanceTime();
EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask());
EXPECT_THAT(element_exists_results_, Contains(Pair("2", false)));
EXPECT_THAT(all_done_, Contains("all_done"));
EXPECT_FALSE(checks_.all_found());
}
TEST_F(BatchElementCheckerTest, TryDoneCallback) {
EXPECT_CALL(mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"element"})), _))
.WillOnce(RunOnceCallback<2>(false))
.WillOnce(RunOnceCallback<2>(true));
checks_.AddElementCheck(kExistenceCheck, Selector({"element"}),
base::DoNothing());
checks_.Run(base::TimeDelta::FromSeconds(10), TryCallback("try"),
DoneCallback("all_done"));
// The first try does not fully succeed.
EXPECT_THAT(try_done_, Contains(Pair("try", 1)));
EXPECT_THAT(all_done_, Not(Contains("all_done")));
// The second try succeeds and ends the run.
AdvanceTime();
EXPECT_THAT(try_done_, Contains(Pair("try", 2)));
EXPECT_THAT(all_done_, Contains("all_done"));
}
TEST_F(BatchElementCheckerTest, TryOnceGivenSmallDuration) {
EXPECT_CALL(
mock_web_controller_,
OnElementCheck(kExistenceCheck, Eq(Selector({"does_not_exist"})), _))
.WillOnce(RunOnceCallback<2>(false));
checks_.AddElementCheck(kExistenceCheck, Selector({"does_not_exist"}),
ElementExistenceCallback("does_not_exist"));
checks_.Run(base::TimeDelta::FromMilliseconds(10), base::DoNothing(),
DoneCallback("all_done"));
EXPECT_FALSE(scoped_task_environment_.MainThreadHasPendingTask());
EXPECT_THAT(element_exists_results_, Contains(Pair("does_not_exist", false)));
EXPECT_THAT(all_done_, Contains("all_done"));
EXPECT_FALSE(checks_.all_found());
}
} // namespace
} // namespace autofill_assistant