
This is an automated patch. Please avoid assigning unrelated bug. #cleanup This is a follow-up to @thestig comment: https://chromium-review.googlesource.com/c/chromium/src/+/5009410/7/pdf/accessibility_helper.cc#11 Several developers, including myself rewrote optional without properly reordering the #includes at the beginning. This patches automatically fixes it. Script: ------- ``` function replace { echo "Replacing $1 by $2" git grep -l "$1" \ | cut -f1 -d: \ | grep -v \ -e "*win*" \ -e "third_party/*" \ -e "tools/*" \ | grep \ -e "\.h" \ -e "\.cc" \ | sort \ | uniq \ | xargs sed -i "s/$1/$2/g" } replace "#include <optional>" "" git add -u git commit -m "remove optional" git revert HEAD --no-commit git add -u echo "Formatting": echo "IncludeBlocks: Regroup" >> ".clang-format" echo "IncludeIsMainRegex: \"(_(android|apple|chromeos|freebsd|fuchsia|fuzzer|ios|linux|mac|nacl|openbsd|posix|stubs?|win))?(_(unit|browser|perf)?tests?)?$\"" >> ".clang-format" git add -u git cl format --upstream=HEAD git checkout origin/main -- ".clang-format" git add -u git commit -m "revert with format" git reset --soft origin/main git add -u git commit -m "Automated format" ``` cleanup: This is a no-op patch formatting includes. Bug: 40288126 Change-Id: I5f61b1207c097a4c6b20a034f9d1b323975b1851 AX-Relnotes: n/a. Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5335142 Owners-Override: Lei Zhang <thestig@chromium.org> Reviewed-by: Lei Zhang <thestig@chromium.org> Commit-Queue: Arthur Sonzogni <arthursonzogni@chromium.org> Cr-Commit-Position: refs/heads/main@{#1267143}
221 lines
7.1 KiB
C++
221 lines
7.1 KiB
C++
// 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 "headless/test/headless_browser_test_utils.h"
|
|
|
|
#include <optional>
|
|
|
|
#include "base/functional/bind.h"
|
|
#include "base/json/json_writer.h"
|
|
#include "base/logging.h"
|
|
#include "base/run_loop.h"
|
|
#include "components/devtools/simple_devtools_protocol_client/simple_devtools_protocol_client.h"
|
|
#include "content/public/browser/web_contents.h"
|
|
#include "content/public/test/browser_test_utils.h"
|
|
#include "content/public/test/test_navigation_observer.h"
|
|
#include "headless/lib/browser/headless_web_contents_impl.h"
|
|
#include "headless/public/headless_web_contents.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
using simple_devtools_protocol_client::SimpleDevToolsProtocolClient;
|
|
|
|
namespace headless {
|
|
|
|
base::Value::Dict SendCommandSync(SimpleDevToolsProtocolClient& devtools_client,
|
|
const std::string& command) {
|
|
return SendCommandSync(devtools_client, command, base::Value::Dict());
|
|
}
|
|
|
|
base::Value::Dict SendCommandSync(
|
|
simple_devtools_protocol_client::SimpleDevToolsProtocolClient&
|
|
devtools_client,
|
|
const std::string& command,
|
|
base::Value::Dict params) {
|
|
base::Value::Dict command_result;
|
|
|
|
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
|
|
devtools_client.SendCommand(
|
|
command, std::move(params),
|
|
base::BindOnce(
|
|
[](base::RunLoop* run_loop, base::Value::Dict* command_result,
|
|
base::Value::Dict result) {
|
|
*command_result = std::move(result);
|
|
run_loop->Quit();
|
|
},
|
|
base::Unretained(&run_loop), base::Unretained(&command_result)));
|
|
run_loop.Run();
|
|
|
|
return command_result;
|
|
}
|
|
|
|
base::Value::Dict EvaluateScript(HeadlessWebContents* web_contents,
|
|
const std::string& script) {
|
|
SimpleDevToolsProtocolClient devtools_client;
|
|
devtools_client.AttachToWebContents(
|
|
HeadlessWebContentsImpl::From(web_contents)->web_contents());
|
|
|
|
base::Value::Dict result = SendCommandSync(
|
|
devtools_client, "Runtime.evaluate", Param("expression", script));
|
|
|
|
devtools_client.DetachClient();
|
|
|
|
return result;
|
|
}
|
|
|
|
bool WaitForLoad(HeadlessWebContents* web_contents, net::Error* error) {
|
|
content::WebContents* content_web_contents =
|
|
HeadlessWebContentsImpl::From(web_contents)->web_contents();
|
|
|
|
content::TestNavigationObserver observer(content_web_contents, 1);
|
|
observer.Wait();
|
|
|
|
if (error)
|
|
*error = observer.last_net_error_code();
|
|
|
|
return observer.last_navigation_succeeded();
|
|
}
|
|
|
|
void WaitForLoadAndGainFocus(HeadlessWebContents* web_contents) {
|
|
content::WebContents* content_web_contents =
|
|
HeadlessWebContentsImpl::From(web_contents)->web_contents();
|
|
|
|
// To finish loading and to gain focus are two independent events. Which one
|
|
// is issued first is undefined. The following code is waiting on both, in any
|
|
// order.
|
|
content::TestNavigationObserver load_observer(content_web_contents, 1);
|
|
content::FrameFocusedObserver focus_observer(
|
|
content_web_contents->GetPrimaryMainFrame());
|
|
load_observer.Wait();
|
|
focus_observer.Wait();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// base::Value::Dict helpers.
|
|
|
|
std::string DictString(const base::Value::Dict& dict, std::string_view path) {
|
|
const std::string* result = dict.FindStringByDottedPath(path);
|
|
CHECK(result) << "Missing value for '" << path << "' in:\n"
|
|
<< dict.DebugString();
|
|
return *result;
|
|
}
|
|
|
|
int DictInt(const base::Value::Dict& dict, std::string_view path) {
|
|
std::optional<int> result = dict.FindIntByDottedPath(path);
|
|
CHECK(result) << "Missing value for '" << path << "' in:\n"
|
|
<< dict.DebugString();
|
|
return *result;
|
|
}
|
|
|
|
bool DictBool(const base::Value::Dict& dict, std::string_view path) {
|
|
std::optional<bool> result = dict.FindBoolByDottedPath(path);
|
|
CHECK(result) << "Missing value for '" << path << "' in:\n"
|
|
<< dict.DebugString();
|
|
return *result;
|
|
}
|
|
|
|
bool DictHas(const base::Value::Dict& dict, std::string_view path) {
|
|
return dict.FindByDottedPath(path) != nullptr;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// GMock matchers
|
|
|
|
namespace {
|
|
// Cannot use Value::DebugString here due to newlines.
|
|
std::string ToJSON(const base::ValueView& value) {
|
|
std::string json;
|
|
base::JSONWriter::Write(value, &json);
|
|
return json;
|
|
}
|
|
|
|
class DictHasPathValueMatcher
|
|
: public testing::MatcherInterface<const base::Value::Dict&> {
|
|
public:
|
|
DictHasPathValueMatcher(const std::string& path, base::Value expected_value)
|
|
: path_(path), expected_value_(std::move(expected_value)) {}
|
|
|
|
DictHasPathValueMatcher& operator=(const DictHasPathValueMatcher& other) =
|
|
delete;
|
|
|
|
~DictHasPathValueMatcher() override = default;
|
|
|
|
bool MatchAndExplain(const base::Value::Dict& dict,
|
|
testing::MatchResultListener* listener) const override {
|
|
const base::Value* dict_value = dict.FindByDottedPath(path_);
|
|
if (!dict_value) {
|
|
*listener << "Dictionary '" << ToJSON(dict) << "' does not have path '"
|
|
<< path_ << "'";
|
|
return false;
|
|
}
|
|
if (*dict_value != expected_value_) {
|
|
*listener << "Dictionary path value '" << path_ << "' is '"
|
|
<< ToJSON(*dict_value) << "', expected '"
|
|
<< ToJSON(expected_value_) << "'";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DescribeTo(std::ostream* os) const override {
|
|
*os << "has path '" << path_ << "' with value '" << ToJSON(expected_value_)
|
|
<< "'";
|
|
}
|
|
|
|
void DescribeNegationTo(std::ostream* os) const override {
|
|
*os << "does not have path '" << path_ << "' with value '"
|
|
<< ToJSON(expected_value_) << "'";
|
|
}
|
|
|
|
private:
|
|
const std::string path_;
|
|
const base::Value expected_value_;
|
|
};
|
|
|
|
class DictHasKeyMatcher
|
|
: public testing::MatcherInterface<const base::Value::Dict&> {
|
|
public:
|
|
explicit DictHasKeyMatcher(const std::string& key) : key_(key) {}
|
|
|
|
DictHasKeyMatcher& operator=(const DictHasKeyMatcher& other) = delete;
|
|
|
|
~DictHasKeyMatcher() override = default;
|
|
|
|
bool MatchAndExplain(const base::Value::Dict& dict,
|
|
testing::MatchResultListener* listener) const override {
|
|
const base::Value* dict_value = dict.Find(key_);
|
|
if (!dict_value) {
|
|
*listener << "Dictionary '" << ToJSON(dict) << "' does not have key '"
|
|
<< key_ << "'";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void DescribeTo(std::ostream* os) const override {
|
|
*os << "has key '" << key_ << "'";
|
|
}
|
|
|
|
void DescribeNegationTo(std::ostream* os) const override {
|
|
*os << "does not have key '" << key_ << "'";
|
|
}
|
|
|
|
private:
|
|
const std::string key_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
testing::Matcher<const base::Value::Dict&> DictHasPathValue(
|
|
const std::string& path,
|
|
base::Value expected_value) {
|
|
return testing::MakeMatcher(
|
|
new DictHasPathValueMatcher(path, std::move(expected_value)));
|
|
}
|
|
|
|
testing::Matcher<const base::Value::Dict&> DictHasKey(const std::string& key) {
|
|
return testing::MakeMatcher(new DictHasKeyMatcher(key));
|
|
}
|
|
|
|
} // namespace headless
|