blob: 7384d769f46ff8f93e3ef809fb78787965b4326a [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/test/chromedriver/capabilities.h"
#include <utility>
#include "base/containers/contains.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/strings/pattern.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/test/chromedriver/chrome/client_hints.h"
#include "chrome/test/chromedriver/chrome/log.h"
#include "chrome/test/chromedriver/chrome/status.h"
#include "chrome/test/chromedriver/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Eq;
using testing::Pointee;
namespace {
template <int Code>
testing::AssertionResult StatusCodeIs(const Status& status) {
if (status.code() == Code) {
return testing::AssertionSuccess();
} else {
return testing::AssertionFailure() << status.message();
}
}
testing::AssertionResult StatusOk(const Status& status) {
return StatusCodeIs<kOk>(status);
}
void CheckDefaults(const ClientHints& client_hints) {
EXPECT_EQ("", client_hints.architecture);
EXPECT_EQ(std::nullopt, client_hints.brands);
EXPECT_EQ("", client_hints.bitness);
EXPECT_EQ(std::nullopt, client_hints.full_version_list);
EXPECT_EQ("", client_hints.model);
EXPECT_EQ("", client_hints.platform_version);
EXPECT_FALSE(client_hints.wow64);
}
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentChromeOnWindows[] =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like "
"Gecko) "
"Chrome/114.0.0.0 Safari/537.36";
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentChromeOnMacOS[] =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 "
"(KHTML, like Gecko) "
"Chrome/114.0.0.0 Safari/537.36";
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentChromeOnLinux[] =
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/114.0.0.0 Safari/537.36";
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentChromeOnChromeOS[] =
"Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like "
"Gecko) "
"Chrome/114.0.0.0 Safari/537.36";
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentChromeOnFuchsia[] =
"Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/114.0.0.0 Safari/537.36";
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentMobileChromeOnAndroid[] =
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/114.0.0.0 Mobile Safari/537.36";
// Source: https://www.chromium.org/updates/ua-reduction/
const char kUserAgentNonMobileChromeOnAndroid[] =
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/114.0.0.0 Safari/537.36";
// Source: docs/ios/user_agent.md
const char kUserAgentMobileChromeOnIOS[] =
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) "
"AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 "
"Mobile/14E5239e Safari/602.1";
// UA used when Request Desktop Site features is enabled.
// Source: docs/ios/user_agent.md
const char kUserAgentNonMobileChromeOnIOS[] =
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) "
"AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/85 "
"Version/11.1.1 Safari/605.1.15";
const std::vector<std::string> kNonMobileUserAgents = {
kUserAgentNonMobileChromeOnAndroid,
kUserAgentNonMobileChromeOnIOS,
kUserAgentChromeOnChromeOS,
kUserAgentChromeOnFuchsia,
kUserAgentChromeOnLinux,
kUserAgentChromeOnMacOS,
kUserAgentChromeOnWindows,
};
} // namespace
TEST(Switches, Empty) {
Switches switches;
base::CommandLine cmd(base::CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_EQ(0u, cmd.GetSwitches().size());
ASSERT_EQ("", switches.ToString());
}
TEST(Switches, NoValue) {
Switches switches;
switches.SetSwitch("hello");
ASSERT_TRUE(switches.HasSwitch("hello"));
ASSERT_EQ("", switches.GetSwitchValue("hello"));
base::CommandLine cmd(base::CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_TRUE(cmd.HasSwitch("hello"));
ASSERT_EQ(FILE_PATH_LITERAL(""), cmd.GetSwitchValueNative("hello"));
ASSERT_EQ("--hello", switches.ToString());
}
TEST(Switches, Value) {
Switches switches;
switches.SetSwitch("hello", "there");
ASSERT_TRUE(switches.HasSwitch("hello"));
ASSERT_EQ("there", switches.GetSwitchValue("hello"));
base::CommandLine cmd(base::CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_TRUE(cmd.HasSwitch("hello"));
ASSERT_EQ(FILE_PATH_LITERAL("there"), cmd.GetSwitchValueNative("hello"));
ASSERT_EQ("--hello=there", switches.ToString());
}
TEST(Switches, Multivalued) {
Switches switches;
switches.SetMultivaluedSwitch("my-switch", "value1", ",");
ASSERT_EQ("value1", switches.GetSwitchValue("my-switch"));
switches.SetMultivaluedSwitch("my-switch", "value2", ",");
ASSERT_EQ("value1,value2", switches.GetSwitchValue("my-switch"));
// Test that trailing commas are removed.
switches.SetMultivaluedSwitch("my-switch", "value3,", ",");
ASSERT_EQ("value1,value2,value3,", switches.GetSwitchValue("my-switch"));
switches.SetMultivaluedSwitch("my-switch", "value4", ",");
ASSERT_EQ("value1,value2,value3,value4",
switches.GetSwitchValue("my-switch"));
}
TEST(Switches, FromOther) {
Switches switches;
switches.SetSwitch("a", "1");
switches.SetSwitch("b", "1");
Switches switches2;
switches2.SetSwitch("b", "2");
switches2.SetSwitch("c", "2");
switches.SetFromSwitches(switches2);
ASSERT_EQ("--a=1 --b=2 --c=2", switches.ToString());
}
TEST(Switches, FromOtherMultivalued) {
Switches switches;
switches.SetSwitch("enable-features", "one");
Switches switches2;
switches2.SetSwitch("enable-features", "two");
switches.SetFromSwitches(switches2);
// `enable-features` is a multivalued switch, so the values should be
// joined with commas.
ASSERT_EQ("one,two", switches.GetSwitchValue("enable-features"));
}
TEST(Switches, Remove) {
Switches switches;
switches.SetSwitch("a", "1");
switches.RemoveSwitch("a");
ASSERT_FALSE(switches.HasSwitch("a"));
}
TEST(Switches, Quoting) {
Switches switches;
switches.SetSwitch("hello", "a b");
switches.SetSwitch("hello2", " '\" ");
ASSERT_EQ("--hello=\"a b\" --hello2=\" '\\\" \"", switches.ToString());
}
TEST(Switches, Multiple) {
Switches switches;
switches.SetSwitch("switch");
switches.SetSwitch("hello", "there");
base::CommandLine cmd(base::CommandLine::NO_PROGRAM);
switches.AppendToCommandLine(&cmd);
ASSERT_TRUE(cmd.HasSwitch("switch"));
ASSERT_TRUE(cmd.HasSwitch("hello"));
ASSERT_EQ(FILE_PATH_LITERAL("there"), cmd.GetSwitchValueNative("hello"));
ASSERT_EQ("--hello=there --switch", switches.ToString());
}
TEST(Switches, Unparsed) {
Switches switches;
switches.SetUnparsedSwitch("a");
switches.SetUnparsedSwitch("--b");
switches.SetUnparsedSwitch("--c=1");
switches.SetUnparsedSwitch("d=1");
switches.SetUnparsedSwitch("-e=--1=1");
ASSERT_EQ("---e=--1=1 --a --b --c=1 --d=1", switches.ToString());
}
TEST(ParseCapabilities, UnknownCapabilityLegacy) {
// In legacy mode, unknown capabilities are ignored.
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("foo", "bar");
Status status = capabilities.Parse(caps, false);
ASSERT_TRUE(status.IsOk());
}
TEST(ParseCapabilities, UnknownCapabilityW3c) {
// In W3C mode, unknown capabilities results in error.
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("foo", "bar");
Status status = capabilities.Parse(caps);
ASSERT_EQ(status.code(), kInvalidArgument);
}
TEST(ParseCapabilities, WithAndroidPackage) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.androidPackage", "abc");
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.IsAndroid());
ASSERT_EQ("abc", capabilities.android_package);
}
TEST(ParseCapabilities, EmptyAndroidPackage) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.androidPackage", std::string());
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, IllegalAndroidPackage) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.androidPackage", 123);
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, LogPath) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.logPath", "path/to/logfile");
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_STREQ("path/to/logfile", capabilities.log_path.c_str());
}
TEST(ParseCapabilities, Args) {
Capabilities capabilities;
base::Value::List args;
args.Append("arg1");
args.Append("arg2=invalid");
args.Append("arg2=val");
args.Append("enable-blink-features=val1");
args.Append("enable-blink-features=val2,");
args.Append("--enable-blink-features=val3");
args.Append("js-flags=--flag1");
args.Append("--js-flags=--flag2");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.args", std::move(args));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(4u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.switches.HasSwitch("arg1"));
ASSERT_TRUE(capabilities.switches.HasSwitch("arg2"));
ASSERT_EQ("", capabilities.switches.GetSwitchValue("arg1"));
ASSERT_EQ("val", capabilities.switches.GetSwitchValue("arg2"));
ASSERT_EQ("val1,val2,val3",
capabilities.switches.GetSwitchValue("enable-blink-features"));
ASSERT_EQ("--flag1 --flag2",
capabilities.switches.GetSwitchValue("js-flags"));
}
TEST(ParseCapabilities, Prefs) {
Capabilities capabilities;
base::Value::Dict prefs;
prefs.Set("key1", "value1");
prefs.SetByDottedPath("key2.k", "value2");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.prefs", prefs.Clone());
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(*capabilities.prefs == prefs);
}
TEST(ParseCapabilities, LocalState) {
Capabilities capabilities;
base::Value::Dict local_state;
local_state.Set("s1", "v1");
local_state.SetByDottedPath("s2.s", "v2");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.localState", local_state.Clone());
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(*capabilities.local_state == local_state);
}
TEST(ParseCapabilities, Extensions) {
Capabilities capabilities;
base::Value::List extensions;
extensions.Append("ext1");
extensions.Append("ext2");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.extensions",
base::Value(std::move(extensions)));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(2u, capabilities.extensions.size());
ASSERT_EQ("ext1", capabilities.extensions[0]);
ASSERT_EQ("ext2", capabilities.extensions[1]);
}
TEST(ParseCapabilities, UnrecognizedProxyType) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "unknown proxy type");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, IllegalProxyType) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", 123);
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, DirectProxy) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "direct");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.switches.HasSwitch("no-proxy-server"));
}
TEST(ParseCapabilities, SystemProxy) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "system");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(0u, capabilities.switches.GetSize());
}
TEST(ParseCapabilities, PacProxy) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "pac");
proxy.Set("proxyAutoconfigUrl", "test.wpad");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_EQ("test.wpad", capabilities.switches.GetSwitchValue("proxy-pac-url"));
}
TEST(ParseCapabilities, MissingProxyAutoconfigUrl) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "pac");
proxy.Set("httpProxy", "http://localhost:8001");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, AutodetectProxy) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "autodetect");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.switches.HasSwitch("proxy-auto-detect"));
}
TEST(ParseCapabilities, ManualProxy) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "manual");
proxy.Set("ftpProxy", "localhost:9001");
proxy.Set("httpProxy", "localhost:8001");
proxy.Set("sslProxy", "localhost:10001");
proxy.Set("socksProxy", "localhost:12345");
proxy.Set("socksVersion", 5);
base::Value::List bypass;
bypass.Append("google.com");
bypass.Append("youtube.com");
proxy.Set("noProxy", std::move(bypass));
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(2u, capabilities.switches.GetSize());
ASSERT_EQ(
"ftp=localhost:9001;http=localhost:8001;https=localhost:10001;"
"socks=socks5://localhost:12345",
capabilities.switches.GetSwitchValue("proxy-server"));
ASSERT_EQ(
"google.com,youtube.com",
capabilities.switches.GetSwitchValue("proxy-bypass-list"));
}
TEST(ParseCapabilities, IgnoreNullValueForManualProxy) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "manual");
proxy.Set("ftpProxy", "localhost:9001");
proxy.Set("sslProxy", base::Value());
proxy.Set("noProxy", base::Value());
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.switches.GetSize());
ASSERT_TRUE(capabilities.switches.HasSwitch("proxy-server"));
ASSERT_EQ(
"ftp=localhost:9001",
capabilities.switches.GetSwitchValue("proxy-server"));
}
TEST(ParseCapabilities, MissingSocksVersion) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "manual");
proxy.Set("socksProxy", "localhost:6000");
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, BadSocksVersion) {
Capabilities capabilities;
base::Value::Dict proxy;
proxy.Set("proxyType", "manual");
proxy.Set("socksProxy", "localhost:6000");
proxy.Set("socksVersion", 256);
base::Value::Dict caps;
caps.Set("proxy", std::move(proxy));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, AcceptInsecureCertsDisabledByDefault) {
Capabilities capabilities;
base::Value::Dict caps;
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_FALSE(capabilities.accept_insecure_certs);
}
TEST(ParseCapabilities, EnableAcceptInsecureCerts) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("acceptInsecureCerts", true);
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.accept_insecure_certs);
}
TEST(ParseCapabilities, LoggingPrefsOk) {
Capabilities capabilities;
base::Value::Dict logging_prefs;
logging_prefs.Set("Network", "INFO");
base::Value::Dict caps;
caps.Set("goog:loggingPrefs", std::move(logging_prefs));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(1u, capabilities.logging_prefs.size());
ASSERT_EQ(Log::kInfo, capabilities.logging_prefs["Network"]);
}
TEST(ParseCapabilities, LoggingPrefsNotDict) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("goog:loggingPrefs", "INFO");
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, PerfLoggingPrefsInspectorDomainStatus) {
Capabilities capabilities;
// Perf log must be enabled if performance log preferences are specified.
base::Value::Dict logging_prefs;
logging_prefs.Set(WebDriverLog::kPerformanceType, "INFO");
base::Value::Dict desired_caps;
desired_caps.Set("goog:loggingPrefs", std::move(logging_prefs));
ASSERT_EQ(PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled,
capabilities.perf_logging_prefs.network);
ASSERT_EQ(PerfLoggingPrefs::InspectorDomainStatus::kDefaultEnabled,
capabilities.perf_logging_prefs.page);
base::Value::Dict perf_logging_prefs;
perf_logging_prefs.Set("enableNetwork", true);
perf_logging_prefs.Set("enablePage", false);
desired_caps.SetByDottedPath("goog:chromeOptions.perfLoggingPrefs",
std::move(perf_logging_prefs));
Status status = capabilities.Parse(desired_caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyEnabled,
capabilities.perf_logging_prefs.network);
ASSERT_EQ(PerfLoggingPrefs::InspectorDomainStatus::kExplicitlyDisabled,
capabilities.perf_logging_prefs.page);
}
TEST(ParseCapabilities, PerfLoggingPrefsTracing) {
Capabilities capabilities;
// Perf log must be enabled if performance log preferences are specified.
base::Value::Dict logging_prefs;
logging_prefs.Set(WebDriverLog::kPerformanceType, "INFO");
base::Value::Dict desired_caps;
desired_caps.Set("goog:loggingPrefs", std::move(logging_prefs));
ASSERT_EQ("", capabilities.perf_logging_prefs.trace_categories);
base::Value::Dict perf_logging_prefs;
perf_logging_prefs.Set("traceCategories", "benchmark,blink.console");
perf_logging_prefs.Set("bufferUsageReportingInterval", 1234);
desired_caps.SetByDottedPath("goog:chromeOptions.perfLoggingPrefs",
std::move(perf_logging_prefs));
Status status = capabilities.Parse(desired_caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ("benchmark,blink.console",
capabilities.perf_logging_prefs.trace_categories);
ASSERT_EQ(1234,
capabilities.perf_logging_prefs.buffer_usage_reporting_interval);
}
TEST(ParseCapabilities, PerfLoggingPrefsInvalidInterval) {
Capabilities capabilities;
// Perf log must be enabled if performance log preferences are specified.
base::Value::Dict logging_prefs;
logging_prefs.Set(WebDriverLog::kPerformanceType, "INFO");
base::Value::Dict desired_caps;
desired_caps.Set("goog:loggingPrefs", std::move(logging_prefs));
base::Value::Dict perf_logging_prefs;
// A bufferUsageReportingInterval interval <= 0 will cause DevTools errors.
perf_logging_prefs.Set("bufferUsageReportingInterval", 0);
desired_caps.SetByDottedPath("goog:chromeOptions.perfLoggingPrefs",
std::move(perf_logging_prefs));
Status status = capabilities.Parse(desired_caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, PerfLoggingPrefsNotDict) {
Capabilities capabilities;
// Perf log must be enabled if performance log preferences are specified.
base::Value::Dict logging_prefs;
logging_prefs.Set(WebDriverLog::kPerformanceType, "INFO");
base::Value::Dict desired_caps;
desired_caps.Set("goog:loggingPrefs", std::move(logging_prefs));
desired_caps.SetByDottedPath("goog:chromeOptions.perfLoggingPrefs",
"traceCategories");
Status status = capabilities.Parse(desired_caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, PerfLoggingPrefsNoPerfLogLevel) {
Capabilities capabilities;
base::Value::Dict desired_caps;
base::Value::Dict perf_logging_prefs;
perf_logging_prefs.Set("enableNetwork", true);
desired_caps.SetByDottedPath("goog:chromeOptions.perfLoggingPrefs",
std::move(perf_logging_prefs));
// Should fail because perf log must be enabled if perf log prefs specified.
Status status = capabilities.Parse(desired_caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, PerfLoggingPrefsPerfLogOff) {
Capabilities capabilities;
base::Value::Dict logging_prefs;
// Disable performance log by setting logging level to OFF.
logging_prefs.Set(WebDriverLog::kPerformanceType, "OFF");
base::Value::Dict desired_caps;
desired_caps.Set("goog:loggingPrefs", std::move(logging_prefs));
base::Value::Dict perf_logging_prefs;
perf_logging_prefs.Set("enableNetwork", true);
desired_caps.SetByDottedPath("goog:chromeOptions.perfLoggingPrefs",
std::move(perf_logging_prefs));
// Should fail because perf log must be enabled if perf log prefs specified.
Status status = capabilities.Parse(desired_caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, ExcludeSwitches) {
Capabilities capabilities;
base::Value::List exclude_switches;
exclude_switches.Append("switch1");
exclude_switches.Append("switch2");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.excludeSwitches",
base::Value(std::move(exclude_switches)));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(2u, capabilities.exclude_switches.size());
const std::set<std::string>& switches = capabilities.exclude_switches;
ASSERT_TRUE(base::Contains(switches, "switch1"));
ASSERT_TRUE(base::Contains(switches, "switch2"));
}
TEST(ParseCapabilities, UseRemoteBrowserHostName) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.debuggerAddress", "abc:123");
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.IsRemoteBrowser());
ASSERT_EQ("abc", capabilities.debugger_address.host());
ASSERT_EQ(123, capabilities.debugger_address.port());
}
TEST(ParseCapabilities, UseRemoteBrowserIpv4) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.debuggerAddress", "127.0.0.1:456");
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.IsRemoteBrowser());
ASSERT_EQ("127.0.0.1", capabilities.debugger_address.host());
ASSERT_EQ(456, capabilities.debugger_address.port());
}
TEST(ParseCapabilities, UseRemoteBrowserIpv6) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.debuggerAddress",
"[fe80::f2ef:86ff:fe69:cafe]:789");
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.IsRemoteBrowser());
ASSERT_EQ("[fe80::f2ef:86ff:fe69:cafe]",
capabilities.debugger_address.host());
ASSERT_EQ(789, capabilities.debugger_address.port());
}
TEST(ParseCapabilities, MobileEmulationUserAgent) {
Capabilities capabilities;
base::Value::Dict mobile_emulation;
mobile_emulation.Set("userAgent", "Agent Smith");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(mobile_emulation));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ("Agent Smith", capabilities.mobile_device->user_agent.value());
}
TEST(ParseCapabilities, MobileEmulationDeviceMetrics) {
Capabilities capabilities;
base::Value::Dict mobile_emulation;
mobile_emulation.SetByDottedPath("deviceMetrics.width", 360);
mobile_emulation.SetByDottedPath("deviceMetrics.height", 640);
mobile_emulation.SetByDottedPath("deviceMetrics.pixelRatio", 3.0);
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(mobile_emulation));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_EQ(360, capabilities.mobile_device->device_metrics->width);
ASSERT_EQ(640, capabilities.mobile_device->device_metrics->height);
ASSERT_EQ(3.0,
capabilities.mobile_device->device_metrics->device_scale_factor);
}
TEST(ParseCapabilities, MobileEmulationDeviceName) {
Capabilities capabilities;
base::Value::Dict mobile_emulation;
mobile_emulation.Set("deviceName", "Nexus 5");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(mobile_emulation));
Status status = capabilities.Parse(caps);
ASSERT_TRUE(status.IsOk());
ASSERT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_TRUE(base::MatchPattern(
capabilities.mobile_device->user_agent.value(),
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s Mobile "
"Safari/537.36"));
ASSERT_EQ(360, capabilities.mobile_device->device_metrics->width);
ASSERT_EQ(640, capabilities.mobile_device->device_metrics->height);
ASSERT_EQ(3.0,
capabilities.mobile_device->device_metrics->device_scale_factor);
}
TEST(ParseCapabilities, MobileEmulationNotDict) {
Capabilities capabilities;
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation", "Google Nexus 5");
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, MobileEmulationDeviceMetricsNotDict) {
Capabilities capabilities;
base::Value::Dict mobile_emulation;
mobile_emulation.Set("deviceMetrics", 360);
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(mobile_emulation));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, MobileEmulationDeviceMetricsNotNumbers) {
Capabilities capabilities;
base::Value::Dict mobile_emulation;
mobile_emulation.SetByDottedPath("deviceMetrics.width", "360");
mobile_emulation.SetByDottedPath("deviceMetrics.height", "640");
mobile_emulation.SetByDottedPath("deviceMetrics.pixelRatio", "3.0");
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(mobile_emulation));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, MobileEmulationBadDict) {
Capabilities capabilities;
base::Value::Dict mobile_emulation;
mobile_emulation.Set("deviceName", "Google Nexus 5");
mobile_emulation.SetByDottedPath("deviceMetrics.width", 360);
mobile_emulation.SetByDottedPath("deviceMetrics.height", 640);
mobile_emulation.SetByDottedPath("deviceMetrics.pixelRatio", 3.0);
base::Value::Dict caps;
caps.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(mobile_emulation));
Status status = capabilities.Parse(caps);
ASSERT_FALSE(status.IsOk());
}
TEST(ParseCapabilities, VirtualAuthenticatorsBool) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("webauthn:virtualAuthenticators", true);
EXPECT_TRUE(capabilities.Parse(caps).IsOk());
caps.Set("webauthn:virtualAuthenticators", false);
EXPECT_TRUE(capabilities.Parse(caps).IsOk());
}
TEST(ParseCapabilities, VirtualAuthenticatorsNotBool) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("webauthn:virtualAuthenticators", "not a bool");
EXPECT_FALSE(capabilities.Parse(caps).IsOk());
}
TEST(ParseCapabilities, VirtualAuthenticatorsLargeBlobBool) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("webauthn:extension:largeBlob", true);
EXPECT_TRUE(capabilities.Parse(caps).IsOk());
caps.Set("webauthn:extension:largeBlob", false);
EXPECT_TRUE(capabilities.Parse(caps).IsOk());
}
TEST(ParseCapabilities, VirtualAuthenticatorsLargeBlobNotBool) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("webauthn:extension:largeBlob", "not a bool");
EXPECT_FALSE(capabilities.Parse(caps).IsOk());
}
TEST(ParseCapabilities, FedcmAccountsBool) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("fedcm:accounts", true);
EXPECT_TRUE(capabilities.Parse(caps).IsOk());
caps.Set("fedcm:accounts", false);
EXPECT_TRUE(capabilities.Parse(caps).IsOk());
}
TEST(ParseCapabilities, FedcmAccountsNotBool) {
Capabilities capabilities;
base::Value::Dict caps;
caps.Set("fedcm:accounts", "not a bool");
EXPECT_FALSE(capabilities.Parse(caps).IsOk());
}
TEST(ParseCapabilities, MigrateChromeExtensionWindowType) {
Capabilities capabilities;
base::Value::Dict caps;
base::Value::List window_types;
window_types.Append("background_page");
caps.SetByDottedPath("goog:chromeOptions.windowTypes",
base::Value(std::move(window_types)));
EXPECT_EQ(kOk, capabilities.Parse(caps).code());
EXPECT_TRUE(capabilities.enable_extension_targets);
EXPECT_TRUE(capabilities.window_types.find(WebViewInfo::kBackgroundPage) ==
capabilities.window_types.end());
}
namespace {
base::Value::Dict CreateCapabilitiesDict(const std::string& mobile_emulation) {
base::Value::Dict result;
std::optional<base::Value> maybe_mobile_emulation = base::JSONReader::Read(
mobile_emulation, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
EXPECT_TRUE(maybe_mobile_emulation.has_value() &&
maybe_mobile_emulation->is_dict());
if (!maybe_mobile_emulation.has_value() ||
!maybe_mobile_emulation->is_dict()) {
return result;
}
result.SetByDottedPath("goog:chromeOptions.mobileEmulation",
std::move(maybe_mobile_emulation->GetDict()));
return result;
}
} // namespace
TEST(ParseClientHints, MinimalistMobileAndroid) {
Capabilities capabilities;
const std::string mobile_emulation =
"{\"deviceMetrics\": {}, \"clientHints\": {\"platform\": \"Android\", "
"\"mobile\": true}}";
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
ASSERT_FALSE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ("Android", client_hints.platform);
EXPECT_EQ(true, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(kUserAgentMobileChromeOnAndroid, reduced_user_agent);
}
TEST(ParseClientHints, MinimalistTabletAndroid) {
Capabilities capabilities;
const std::string mobile_emulation =
"{\"deviceMetrics\": {},"
"\"clientHints\": {\"platform\": \"Android\", \"mobile\": false}}";
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
ASSERT_FALSE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ("Android", client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(kUserAgentNonMobileChromeOnAndroid, reduced_user_agent);
}
class ParseClientHintsPerPlatform
: public testing::TestWithParam<std::pair<std::string, std::string>> {};
TEST_P(ParseClientHintsPerPlatform, MinimalistDesktop) {
const std::string expected_platform = GetParam().first;
const std::string expected_user_agent = GetParam().second;
Capabilities capabilities;
const std::string mobile_emulation = base::StringPrintf(
"{\"clientHints\": {\"platform\": \"%s\"}}", expected_platform.c_str());
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
ASSERT_FALSE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ(expected_platform, client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(expected_user_agent, reduced_user_agent);
}
TEST_P(ParseClientHintsPerPlatform, MobileDeviceMetrics) {
const std::string expected_platform = GetParam().first;
const std::string expected_user_agent = GetParam().second;
Capabilities capabilities;
const std::string mobile_emulation = base::StringPrintf(
"{\"deviceMetrics\": {},"
"\"clientHints\": {\"platform\": \"%s\", \"mobile\": true}}",
expected_platform.c_str());
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
ASSERT_FALSE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ(expected_platform, client_hints.platform);
EXPECT_EQ(true, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(expected_user_agent, reduced_user_agent);
}
TEST_P(ParseClientHintsPerPlatform, TabletDeviceMetrics) {
const std::string expected_platform = GetParam().first;
const std::string expected_user_agent = GetParam().second;
Capabilities capabilities;
const std::string mobile_emulation = base::StringPrintf(
"{\"deviceMetrics\": {},"
"\"clientHints\": {\"platform\": \"%s\", \"mobile\": false}}",
expected_platform.c_str());
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
ASSERT_FALSE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ(expected_platform, client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(expected_user_agent, reduced_user_agent);
}
INSTANTIATE_TEST_SUITE_P(
Parsing,
ParseClientHintsPerPlatform,
testing::Values(std::make_pair("Chrome OS", kUserAgentChromeOnChromeOS),
std::make_pair("Chromium OS", kUserAgentChromeOnChromeOS),
std::make_pair("Fuchsia", kUserAgentChromeOnFuchsia),
std::make_pair("Linux", kUserAgentChromeOnLinux),
std::make_pair("macOS", kUserAgentChromeOnMacOS),
std::make_pair("Windows", kUserAgentChromeOnWindows)));
TEST(ParseClientHints, MinimalistCustomMobile) {
Capabilities capabilities;
const std::string mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\", \"deviceMetrics\": {},"
"\"clientHints\": {\"platform\": \"Custom\", \"mobile\": true}}",
kUserAgentMobileChromeOnIOS);
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_EQ("Custom", client_hints.platform);
EXPECT_EQ(true, client_hints.mobile);
ASSERT_TRUE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ(kUserAgentMobileChromeOnIOS,
capabilities.mobile_device->user_agent.value());
std::string reduced_user_agent;
EXPECT_TRUE(capabilities.mobile_device
->GetReducedUserAgent("114", &reduced_user_agent)
.IsError());
EXPECT_TRUE(reduced_user_agent.empty());
}
TEST(ParseClientHints, MinimalistCustomTablet) {
Capabilities capabilities;
const std::string mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\", \"deviceMetrics\": {},"
"\"clientHints\": {\"platform\": \"Custom\", \"mobile\": false}}",
kUserAgentNonMobileChromeOnIOS);
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_EQ("Custom", client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
ASSERT_TRUE(capabilities.mobile_device->user_agent.has_value());
EXPECT_EQ(kUserAgentNonMobileChromeOnIOS,
capabilities.mobile_device->user_agent.value());
std::string reduced_user_agent;
EXPECT_TRUE(capabilities.mobile_device
->GetReducedUserAgent("114", &reduced_user_agent)
.IsError());
EXPECT_TRUE(reduced_user_agent.empty());
}
class InferClientHintsOnAndroid
: public testing::TestWithParam<std::pair<std::string, bool>> {};
TEST_P(InferClientHintsOnAndroid, NoDeviceMetrics) {
const std::string input_user_agent = GetParam().first;
const bool expected_is_mobile = GetParam().second;
const std::string mobile_emulation =
base::StringPrintf("{\"userAgent\": \"%s\"}", input_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(input_user_agent, capabilities.mobile_device->user_agent.value());
EXPECT_EQ("Android", client_hints.platform);
EXPECT_EQ(expected_is_mobile, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(input_user_agent, reduced_user_agent);
}
TEST_P(InferClientHintsOnAndroid, MobileDeviceMetrics) {
const std::string input_user_agent = GetParam().first;
const bool expected_is_mobile = GetParam().second;
const std::string mobile_emulation =
base::StringPrintf("{\"userAgent\": \"%s\", \"deviceMetrics\": {}}",
input_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(input_user_agent, capabilities.mobile_device->user_agent.value());
EXPECT_EQ("Android", client_hints.platform);
EXPECT_EQ(expected_is_mobile, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(input_user_agent, reduced_user_agent);
}
TEST_P(InferClientHintsOnAndroid, TabletDeviceMetrics) {
const std::string input_user_agent = GetParam().first;
const bool expected_is_mobile = GetParam().second;
const std::string mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\", \"deviceMetrics\": {\"mobile\": false}}",
input_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(input_user_agent, capabilities.mobile_device->user_agent.value());
EXPECT_EQ("Android", client_hints.platform);
// Deriverd from deviceMetrics.mobile
EXPECT_EQ(expected_is_mobile, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(input_user_agent, reduced_user_agent);
}
INSTANTIATE_TEST_SUITE_P(
Inference,
InferClientHintsOnAndroid,
testing::Values(std::make_pair(kUserAgentMobileChromeOnAndroid, true),
std::make_pair(kUserAgentNonMobileChromeOnAndroid, false)));
class InferClientHintsPerPlatform
: public testing::TestWithParam<std::pair<std::string, std::string>> {};
TEST_P(InferClientHintsPerPlatform, NoDeviceMetrics) {
const std::string expected_platform = GetParam().first;
const std::string expected_user_agent = GetParam().second;
const std::string mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\"}", expected_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(expected_user_agent,
capabilities.mobile_device->user_agent.value());
EXPECT_EQ(expected_platform, client_hints.platform);
// Inferred as non-mobile due to the lack of device metrics
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(expected_user_agent, reduced_user_agent);
}
TEST_P(InferClientHintsPerPlatform, MobileDeviceMetrics) {
const std::string expected_platform = GetParam().first;
const std::string expected_user_agent = GetParam().second;
const std::string mobile_emulation =
base::StringPrintf("{\"userAgent\": \"%s\", \"deviceMetrics\": {}}",
expected_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(expected_user_agent,
capabilities.mobile_device->user_agent.value());
EXPECT_EQ(expected_platform, client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(expected_user_agent, reduced_user_agent);
}
TEST_P(InferClientHintsPerPlatform, TabletDeviceMetrics) {
const std::string expected_platform = GetParam().first;
const std::string expected_user_agent = GetParam().second;
const std::string mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\", \"deviceMetrics\": {\"mobile\": false}}",
expected_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(expected_user_agent,
capabilities.mobile_device->user_agent.value());
EXPECT_EQ(expected_platform, client_hints.platform);
// Deriverd from deviceMetrics.mobile
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(StatusOk(capabilities.mobile_device->GetReducedUserAgent(
"114", &reduced_user_agent)));
EXPECT_EQ(expected_user_agent, reduced_user_agent);
}
INSTANTIATE_TEST_SUITE_P(
Legacy,
InferClientHintsPerPlatform,
testing::Values(std::make_pair("Chrome OS", kUserAgentChromeOnChromeOS),
std::make_pair("Fuchsia", kUserAgentChromeOnFuchsia),
std::make_pair("Linux", kUserAgentChromeOnLinux),
std::make_pair("macOS", kUserAgentChromeOnMacOS),
std::make_pair("Windows", kUserAgentChromeOnWindows)));
class InferClientHintsOnCustomPlatform
: public testing::TestWithParam<std::string> {};
TEST_P(InferClientHintsOnCustomPlatform, NoDeviceMetrics) {
const std::string input_user_agent = GetParam();
const std::string mobile_emulation =
base::StringPrintf("{\"userAgent\": \"%s\"}", input_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(input_user_agent, capabilities.mobile_device->user_agent.value());
EXPECT_EQ("", client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(capabilities.mobile_device
->GetReducedUserAgent("114", &reduced_user_agent)
.IsError());
EXPECT_EQ("", reduced_user_agent);
}
TEST_P(InferClientHintsOnCustomPlatform, MobileDeviceMetrics) {
const std::string input_user_agent = GetParam();
const std::string mobile_emulation =
base::StringPrintf("{\"userAgent\": \"%s\", \"deviceMetrics\": {}}",
input_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(input_user_agent, capabilities.mobile_device->user_agent.value());
EXPECT_EQ("", client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(capabilities.mobile_device
->GetReducedUserAgent("114", &reduced_user_agent)
.IsError());
EXPECT_EQ("", reduced_user_agent);
}
TEST_P(InferClientHintsOnCustomPlatform, TabletDeviceMetrics) {
const std::string input_user_agent = GetParam();
const std::string mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\", \"deviceMetrics\": {\"mobile\": false}}",
input_user_agent.c_str());
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
const ClientHints& client_hints =
capabilities.mobile_device->client_hints.value();
CheckDefaults(client_hints);
EXPECT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_EQ(input_user_agent, capabilities.mobile_device->user_agent.value());
EXPECT_EQ("", client_hints.platform);
EXPECT_EQ(false, client_hints.mobile);
std::string reduced_user_agent;
EXPECT_TRUE(capabilities.mobile_device
->GetReducedUserAgent("114", &reduced_user_agent)
.IsError());
EXPECT_EQ("", reduced_user_agent);
}
INSTANTIATE_TEST_SUITE_P(Inference,
InferClientHintsOnCustomPlatform,
testing::Values(kUserAgentMobileChromeOnIOS,
"Custom User Agent"));
TEST(ParseClientHints, NoUserAgentNoClientHints) {
Capabilities capabilities;
base::Value::Dict caps = CreateCapabilitiesDict("{\"deviceMetrics\": {}}");
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
EXPECT_FALSE(capabilities.mobile_device->client_hints.has_value());
}
TEST(ParseClientHints, EmptyClientHints) {
Capabilities capabilities;
base::Value::Dict caps;
std::string mobile_emulation;
caps = CreateCapabilitiesDict("{\"clientHints\": {}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
mobile_emulation =
base::StringPrintf("{\"userAgent\": \"%s\", \"clientHints\": {}}",
kUserAgentMobileChromeOnAndroid);
caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict("{\"deviceMetrics\": {}, \"clientHints\": {}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
mobile_emulation = base::StringPrintf(
"{\"userAgent\": \"%s\", \"deviceMetrics\": {}, \"clientHints\": {}}",
kUserAgentMobileChromeOnAndroid);
caps = CreateCapabilitiesDict(mobile_emulation);
EXPECT_TRUE(capabilities.Parse(caps).IsError());
}
TEST(ParseClientHints, RequireUserAgentForCustomPlatform) {
Capabilities capabilities;
base::Value::Dict caps;
caps =
CreateCapabilitiesDict("{\"clientHints\": {\"platform\": \"Custom\"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Custom\", \"mobile\": false}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Custom\", \"mobile\": true}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"deviceMetrics\": {}, \"clientHints\": {\"platform\": \"Custom\"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"deviceMetrics\": {\"mobile\": false}, \"clientHints\": "
"{\"platform\": \"Custom\"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"deviceMetrics\": {\"mobile\": true}, \"clientHints\": {\"platform\": "
"\"Custom\"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
}
TEST(ParseClientHints, WrongClientHintsType) {
Capabilities capabilities;
base::Value::Dict caps =
CreateCapabilitiesDict("{\"clientHints\": \"wrong\"}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
}
TEST(ParseClientHints, WrongClientHintsProperties) {
Capabilities capabilities;
base::Value::Dict caps;
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": 1, \"mobile\": true}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Linux\", \"mobile\": {}}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Linux\", \"mobile\": false, "
"\"architecture\": 3}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Linux\", \"mobile\": false, "
"\"platformVersion\": 3}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Linux\", \"mobile\": false, "
"\"bitness\": 3}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Linux\", \"mobile\": false, "
"\"wow64\": 3}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{\"clientHints\": {\"platform\": \"Linux\", \"mobile\": false, "
"\"model\": 3}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
}
TEST(ParseClientHints, CustomClientHints) {
Capabilities capabilities;
base::Value::Dict caps;
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"platformVersion\": \"11\","
"\"architecture\": \"Custom Architecture\","
"\"model\": \"Custom Model\","
"\"bitness\": \"14\","
"\"wow64\": true,"
"\"brands\": ["
"{\"brand\": \"Ax\", \"version\": \"33\"},"
"{\"brand\": \"Bx\", \"version\": \"41\"}"
"],"
"\"fullVersionList\": ["
"{\"brand\": \"Aex\", \"version\": \"33.3.1\"},"
"{\"brand\": \"Bex\", \"version\": \"41.2.1\"}"
"]"
"}}");
EXPECT_TRUE(StatusOk(capabilities.Parse(caps)));
ASSERT_TRUE(capabilities.mobile_device.has_value());
ASSERT_TRUE(capabilities.mobile_device->user_agent.has_value());
ASSERT_TRUE(capabilities.mobile_device->client_hints.has_value());
EXPECT_EQ("Custom Mobile User Agent",
capabilities.mobile_device->user_agent.value());
EXPECT_EQ("Custom Platform",
capabilities.mobile_device->client_hints->platform);
EXPECT_EQ(true, capabilities.mobile_device->client_hints->mobile);
EXPECT_EQ("11", capabilities.mobile_device->client_hints->platform_version);
EXPECT_EQ("Custom Architecture",
capabilities.mobile_device->client_hints->architecture);
EXPECT_EQ("Custom Model", capabilities.mobile_device->client_hints->model);
EXPECT_EQ("14", capabilities.mobile_device->client_hints->bitness);
EXPECT_EQ(true, capabilities.mobile_device->client_hints->wow64);
ASSERT_TRUE(capabilities.mobile_device->client_hints->brands.has_value());
ASSERT_TRUE(
capabilities.mobile_device->client_hints->full_version_list.has_value());
auto brands = capabilities.mobile_device->client_hints->brands.value();
auto full_version_list =
capabilities.mobile_device->client_hints->full_version_list.value();
ASSERT_EQ(2u, brands.size());
ASSERT_EQ(2u, full_version_list.size());
EXPECT_EQ("Ax", brands[0].brand);
EXPECT_EQ("33", brands[0].version);
EXPECT_EQ("Bx", brands[1].brand);
EXPECT_EQ("41", brands[1].version);
EXPECT_EQ("Aex", full_version_list[0].brand);
EXPECT_EQ("33.3.1", full_version_list[0].version);
EXPECT_EQ("Bex", full_version_list[1].brand);
EXPECT_EQ("41.2.1", full_version_list[1].version);
}
TEST(ParseClientHints, MalformedBrands) {
Capabilities capabilities;
base::Value::Dict caps;
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"brands\": ["
"{\"brand\": 3, \"version\": \"33\"}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"brands\": ["
"{\"brand\": \"3\", \"version\": 33}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"brands\": ["
"{\"brand\": \"3\"}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"brands\": ["
"{\"version\": \"3\"}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
}
TEST(ParseClientHints, MalformedFullVersionList) {
Capabilities capabilities;
base::Value::Dict caps;
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"fullVersionList\": ["
"{\"brand\": 3, \"version\": \"33\"}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"fullVersionList\": ["
"{\"brand\": \"3\", \"version\": 33}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"fullVersionList\": ["
"{\"brand\": \"3\"}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
caps = CreateCapabilitiesDict(
"{"
"\"userAgent\": \"Custom Mobile User Agent\","
"\"deviceMetrics\": {},"
"\"clientHints\":{"
"\"platform\": \"Custom Platform\","
"\"mobile\": true,"
"\"fullVersionList\": ["
"{\"version\": \"3\"}"
"]"
"}}");
EXPECT_TRUE(capabilities.Parse(caps).IsError());
}