| // Copyright 2014 The Chromium OS 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 "chromeos/ui/chromium_command_builder.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/files/file_util.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/macros.h> |
| #include <base/strings/stringprintf.h> |
| #include <gtest/gtest.h> |
| |
| #include "chromeos/ui/util.h" |
| |
| namespace chromeos { |
| namespace ui { |
| |
| class ChromiumCommandBuilderTest : public testing::Test { |
| public: |
| ChromiumCommandBuilderTest() |
| : write_use_flags_file_(true), write_lsb_release_file_(true) { |
| PCHECK(temp_dir_.CreateUniqueTempDir()); |
| base_path_ = temp_dir_.GetPath(); |
| builder_.set_base_path_for_testing(base_path_); |
| |
| pepper_dir_ = util::GetReparentedPath( |
| ChromiumCommandBuilder::kPepperPluginsPath, base_path_); |
| PCHECK(base::CreateDirectory(pepper_dir_)); |
| } |
| virtual ~ChromiumCommandBuilderTest() {} |
| |
| // Does testing-related initialization and returns the result of |builder_|'s |
| // Init() method. |
| bool Init() { |
| if (write_use_flags_file_) { |
| WriteFileUnderBasePath(ChromiumCommandBuilder::kUseFlagsPath, |
| use_flags_data_); |
| } |
| if (write_lsb_release_file_) { |
| WriteFileUnderBasePath(ChromiumCommandBuilder::kLsbReleasePath, |
| lsb_release_data_); |
| } |
| return builder_.Init(); |
| } |
| |
| // Writes |data| to |path| underneath |base_path_|. |
| void WriteFileUnderBasePath(const std::string& path, |
| const std::string& data) { |
| base::FilePath reparented_path(util::GetReparentedPath(path, base_path_)); |
| if (!base::DirectoryExists(reparented_path.DirName())) |
| PCHECK(base::CreateDirectory(reparented_path.DirName())); |
| PCHECK(base::WriteFile(reparented_path, data.data(), data.size()) == |
| static_cast<int>(data.size())); |
| } |
| |
| // Looks up |name| in |builder_|'s list of environment variables, returning |
| // its value if present or an empty string otherwise. |
| std::string ReadEnvVar(const std::string& name) { |
| const ChromiumCommandBuilder::StringMap& vars = |
| builder_.environment_variables(); |
| ChromiumCommandBuilder::StringMap::const_iterator it = vars.find(name); |
| return it != vars.end() ? it->second : std::string(); |
| } |
| |
| // Returns the first argument in |builder_| that starts with |prefix|, or an |
| // empty string if no matching argument is found. |
| std::string GetFirstArgWithPrefix(const std::string& prefix) { |
| const ChromiumCommandBuilder::StringVector& args = builder_.arguments(); |
| for (size_t i = 0; i < args.size(); ++i) { |
| if (args[i].find(prefix) == 0) |
| return args[i]; |
| } |
| return std::string(); |
| } |
| |
| protected: |
| base::ScopedTempDir temp_dir_; |
| base::FilePath base_path_; |
| |
| bool write_use_flags_file_; |
| std::string use_flags_data_; |
| |
| bool write_lsb_release_file_; |
| std::string lsb_release_data_; |
| |
| base::FilePath pepper_dir_; |
| |
| ChromiumCommandBuilder builder_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ChromiumCommandBuilderTest); |
| }; |
| |
| TEST_F(ChromiumCommandBuilderTest, MissingUseFlagsFile) { |
| write_use_flags_file_ = false; |
| EXPECT_FALSE(Init()); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, UseFlags) { |
| use_flags_data_ = "# Here's a comment.\nfoo\nbar\n"; |
| ASSERT_TRUE(Init()); |
| |
| EXPECT_TRUE(builder_.UseFlagIsSet("foo")); |
| EXPECT_TRUE(builder_.UseFlagIsSet("bar")); |
| EXPECT_FALSE(builder_.UseFlagIsSet("food")); |
| EXPECT_FALSE(builder_.UseFlagIsSet("# Here's a comment.")); |
| EXPECT_FALSE(builder_.UseFlagIsSet("#")); |
| EXPECT_FALSE(builder_.UseFlagIsSet("a")); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, MissingLsbReleaseFile) { |
| write_lsb_release_file_ = false; |
| EXPECT_FALSE(Init()); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, LsbRelease) { |
| lsb_release_data_ = "abc\ndef"; |
| ASSERT_TRUE(Init()); |
| ASSERT_TRUE(builder_.SetUpChromium()); |
| |
| EXPECT_EQ(lsb_release_data_, ReadEnvVar("LSB_RELEASE")); |
| EXPECT_FALSE(ReadEnvVar("LSB_RELEASE_TIME").empty()); |
| EXPECT_FALSE(builder_.is_test_build()); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, IsTestBuild) { |
| lsb_release_data_ = "abc\nCHROMEOS_RELEASE_TRACK=testabc\ndef"; |
| ASSERT_TRUE(Init()); |
| EXPECT_TRUE(builder_.is_test_build()); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, TimeZone) { |
| // Test that the builder creates a symlink for the time zone. |
| ASSERT_TRUE(Init()); |
| ASSERT_TRUE(builder_.SetUpChromium()); |
| const base::FilePath kSymlink(util::GetReparentedPath( |
| ChromiumCommandBuilder::kTimeZonePath, base_path_)); |
| base::FilePath target; |
| ASSERT_TRUE(base::ReadSymbolicLink(kSymlink, &target)); |
| EXPECT_EQ(ChromiumCommandBuilder::kDefaultZoneinfoPath, target.value()); |
| |
| // Delete the old symlink and create a new one with a different target. |
| // Arbitrarily use |base_path_| (we need a path that exists). |
| ASSERT_TRUE(base::DeleteFile(kSymlink, false)); |
| const base::FilePath kNewTarget(base_path_); |
| ASSERT_TRUE(base::CreateSymbolicLink(kNewTarget, kSymlink)); |
| |
| // Initialize a second builder and check that it leaves the existing symlink |
| // alone. |
| ChromiumCommandBuilder second_builder; |
| second_builder.set_base_path_for_testing(base_path_); |
| ASSERT_TRUE(second_builder.Init()); |
| ASSERT_TRUE(second_builder.SetUpChromium()); |
| ASSERT_TRUE(base::ReadSymbolicLink(kSymlink, &target)); |
| EXPECT_EQ(kNewTarget.value(), target.value()); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, BasicEnvironment) { |
| ASSERT_TRUE(Init()); |
| ASSERT_TRUE(builder_.SetUpChromium()); |
| |
| EXPECT_EQ("chronos", ReadEnvVar("USER")); |
| EXPECT_EQ("chronos", ReadEnvVar("LOGNAME")); |
| EXPECT_EQ("/bin/sh", ReadEnvVar("SHELL")); |
| EXPECT_FALSE(ReadEnvVar("PATH").empty()); |
| EXPECT_EQ("en_US.utf8", ReadEnvVar("LC_ALL")); |
| base::FilePath data_dir(util::GetReparentedPath("/home/chronos", base_path_)); |
| EXPECT_EQ(data_dir.value(), ReadEnvVar("DATA_DIR")); |
| EXPECT_TRUE(base::DirectoryExists(data_dir)); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, ValueListFlags) { |
| ASSERT_TRUE(Init()); |
| ASSERT_TRUE(builder_.SetUpChromium()); |
| |
| // All of these methods do essentially the same thing. |
| typedef void (ChromiumCommandBuilder::*AddMethod)(const std::string&); |
| struct TestCase { |
| const char* flag; |
| AddMethod method; |
| bool append; |
| }; |
| const std::vector<TestCase> kTestCases = { |
| {ChromiumCommandBuilder::kVmoduleFlag, |
| &ChromiumCommandBuilder::AddVmodulePattern, false}, |
| {ChromiumCommandBuilder::kEnableFeaturesFlag, |
| &ChromiumCommandBuilder::AddFeatureEnableOverride, true}, |
| {ChromiumCommandBuilder::kDisableFeaturesFlag, |
| &ChromiumCommandBuilder::AddFeatureDisableOverride, true}, |
| {ChromiumCommandBuilder::kEnableBlinkFeaturesFlag, |
| &ChromiumCommandBuilder::AddBlinkFeatureEnableOverride, true}, |
| {ChromiumCommandBuilder::kDisableBlinkFeaturesFlag, |
| &ChromiumCommandBuilder::AddBlinkFeatureDisableOverride, true}, |
| }; |
| |
| for (const auto& tc : kTestCases) { |
| SCOPED_TRACE(tc.flag); |
| const std::string kPrefix = base::StringPrintf("--%s=", tc.flag); |
| EXPECT_EQ("", GetFirstArgWithPrefix(kPrefix)); |
| (builder_.*(tc.method))("foo"); |
| EXPECT_EQ(kPrefix + "foo", GetFirstArgWithPrefix(kPrefix)); |
| (builder_.*(tc.method))("bar"); |
| EXPECT_EQ(kPrefix + (tc.append ? "foo,bar" : "bar,foo"), |
| GetFirstArgWithPrefix(kPrefix)); |
| |
| // Add another argument and check that the flag still gets updated. |
| builder_.AddArg("--blah"); |
| (builder_.*(tc.method))("baz"); |
| ASSERT_EQ(kPrefix + (tc.append ? "foo,bar,baz" : "baz,bar,foo"), |
| GetFirstArgWithPrefix(kPrefix)); |
| } |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, UserConfig) { |
| ASSERT_TRUE(Init()); |
| builder_.AddArg("--baz=4"); |
| builder_.AddArg("--blah-a"); |
| builder_.AddArg("--blah-b"); |
| |
| const char kConfig[] = |
| "# Here's a comment followed by a blank line and some whitespace.\n" |
| "\n" |
| " \n" |
| "--foo=1\n" |
| "--bar=2\n" |
| "FOO=3\n" |
| "BAR=4\n" |
| "!--bar\n" |
| "!--baz\n" |
| "--bar=3\n" |
| "!--blah\n"; |
| base::FilePath path(util::GetReparentedPath("/config.txt", base_path_)); |
| ASSERT_EQ(strlen(kConfig), base::WriteFile(path, kConfig, strlen(kConfig))); |
| std::set<std::string> disallowed_prefixes; |
| |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| ASSERT_EQ(2U, builder_.arguments().size()); |
| EXPECT_EQ("--foo=1", builder_.arguments()[0]); |
| EXPECT_EQ("--bar=3", builder_.arguments()[1]); |
| EXPECT_EQ("3", ReadEnvVar("FOO")); |
| EXPECT_EQ("4", ReadEnvVar("BAR")); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, UserConfigWithDisallowedPrefixes) { |
| ASSERT_TRUE(Init()); |
| ASSERT_TRUE(builder_.SetUpChromium()); |
| |
| size_t default_size = builder_.arguments().size(); |
| |
| const char kConfig[] = |
| "# Here's a comment followed by 3 lines with disallowed prefixes and\n" |
| "# 2 lines without disallowed prefixes.\n" |
| "--disallowed-prefix1=bar\n" |
| " --disallowed-prefix2=bar\n" |
| "--notallowed-prefix3=bar\n" |
| "--Disallowed-prefix=foo\n" |
| "--allowed-prefix=foo"; |
| |
| base::FilePath path(util::GetReparentedPath("/config.txt", base_path_)); |
| ASSERT_EQ(strlen(kConfig), base::WriteFile(path, kConfig, strlen(kConfig))); |
| std::set<std::string> disallowed_prefixes; |
| |
| disallowed_prefixes.insert("--disallowed-prefix"); |
| disallowed_prefixes.insert("--notallowed-prefix"); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| |
| ASSERT_EQ(default_size + 2, builder_.arguments().size()); |
| EXPECT_EQ("--Disallowed-prefix=foo", builder_.arguments()[default_size]); |
| EXPECT_EQ("--allowed-prefix=foo", builder_.arguments()[default_size + 1]); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, UserConfigVmodule) { |
| const char kPrefix[] = "--vmodule="; |
| |
| ASSERT_TRUE(Init()); |
| builder_.AddArg("--foo"); |
| builder_.AddVmodulePattern("a=2"); |
| builder_.AddArg("--bar"); |
| |
| // Check that we don't get confused when deleting flags surrounding the |
| // vmodule flag. |
| const char kConfig[] = "!--foo\n!--bar"; |
| base::FilePath path(util::GetReparentedPath("/config.txt", base_path_)); |
| ASSERT_EQ(strlen(kConfig), base::WriteFile(path, kConfig, strlen(kConfig))); |
| std::set<std::string> disallowed_prefixes; |
| |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| builder_.AddVmodulePattern("b=1"); |
| ASSERT_EQ("--vmodule=b=1,a=2", GetFirstArgWithPrefix(kPrefix)); |
| |
| // Delete the --vmodule flag. |
| const char kConfig2[] = "!--vmodule="; |
| ASSERT_EQ(strlen(kConfig2), |
| base::WriteFile(path, kConfig2, strlen(kConfig2))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| EXPECT_TRUE(builder_.arguments().empty()); |
| |
| // Now add another vmodule pattern and check that the flag is re-added. |
| builder_.AddVmodulePattern("c=1"); |
| ASSERT_EQ("--vmodule=c=1", GetFirstArgWithPrefix(kPrefix)); |
| |
| // Check that vmodule directives in config files are handled. |
| const char kConfig3[] = "vmodule=a=1\nvmodule=b=2"; |
| ASSERT_EQ(strlen(kConfig3), |
| base::WriteFile(path, kConfig3, strlen(kConfig3))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| ASSERT_EQ("--vmodule=b=2,a=1,c=1", GetFirstArgWithPrefix(kPrefix)); |
| |
| // Also check that literal "vmodule=..." arguments don't get added. |
| ASSERT_EQ("", GetFirstArgWithPrefix("vmodule=")); |
| |
| // "--vmodule=" lines in config files should be permitted too. Each pattern is |
| // prepended to the existing list because Chrome uses the first matching |
| // pattern that it sees; we want patterns specified via the developer's config |
| // file to override hardcoded patterns. |
| const char kConfig4[] = "--vmodule=d=1,e=2"; |
| ASSERT_EQ(strlen(kConfig4), |
| base::WriteFile(path, kConfig4, strlen(kConfig4))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| ASSERT_EQ("--vmodule=e=2,d=1,b=2,a=1,c=1", GetFirstArgWithPrefix(kPrefix)); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, UserConfigEnableFeatures) { |
| const char kPrefix[] = "--enable-features="; |
| |
| ASSERT_TRUE(Init()); |
| builder_.AddArg("--foo"); |
| builder_.AddFeatureEnableOverride("a"); |
| builder_.AddArg("--bar"); |
| |
| // Check that we don't get confused when deleting flags surrounding the |
| // feature flag. |
| const char kConfig[] = "!--foo\n!--bar"; |
| std::set<std::string> disallowed_prefixes; |
| |
| base::FilePath path(util::GetReparentedPath("/config.txt", base_path_)); |
| ASSERT_EQ(strlen(kConfig), base::WriteFile(path, kConfig, strlen(kConfig))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| builder_.AddFeatureEnableOverride("b"); |
| ASSERT_EQ("--enable-features=a,b", GetFirstArgWithPrefix(kPrefix)); |
| |
| // Delete the --enable-features flag. |
| const char kConfig2[] = "!--enable-features="; |
| ASSERT_EQ(strlen(kConfig2), |
| base::WriteFile(path, kConfig2, strlen(kConfig2))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| EXPECT_TRUE(builder_.arguments().empty()); |
| |
| // Now add another feature and check that the flag is re-added. |
| builder_.AddFeatureEnableOverride("c"); |
| ASSERT_EQ("--enable-features=c", GetFirstArgWithPrefix(kPrefix)); |
| |
| // Check that enable-features directives in config files are handled. |
| const char kConfig3[] = "enable-features=d\nenable-features=e"; |
| ASSERT_EQ(strlen(kConfig3), |
| base::WriteFile(path, kConfig3, strlen(kConfig3))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| ASSERT_EQ("--enable-features=c,d,e", GetFirstArgWithPrefix(kPrefix)); |
| |
| // Also check that literal "enable-features=..." arguments don't get added. |
| ASSERT_EQ("", GetFirstArgWithPrefix("enable-features=")); |
| |
| // "--enable-features=" lines in config files should be permitted too. |
| const char kConfig4[] = "--enable-features=f,g"; |
| ASSERT_EQ(strlen(kConfig4), |
| base::WriteFile(path, kConfig4, strlen(kConfig4))); |
| ASSERT_TRUE(builder_.ApplyUserConfig(path, disallowed_prefixes)); |
| ASSERT_EQ("--enable-features=c,d,e,f,g", GetFirstArgWithPrefix(kPrefix)); |
| } |
| |
| TEST_F(ChromiumCommandBuilderTest, PepperPlugins) { |
| const char kFlash[] = |
| "# Here's a comment.\n" |
| "FILE_NAME=/opt/google/chrome/pepper/flash.so\n" |
| "PLUGIN_NAME=\"Shockwave Flash\"\n" |
| "VERSION=1.2.3.4\n"; |
| ASSERT_EQ(strlen(kFlash), base::WriteFile(pepper_dir_.Append("flash.info"), |
| kFlash, strlen(kFlash))); |
| |
| const char kNetflix[] = |
| "FILE_NAME=/opt/google/chrome/pepper/netflix.so\n" |
| "PLUGIN_NAME=\"Netflix\"\n" |
| "VERSION=2.0.0\n" |
| "DESCRIPTION=Helper for the Netflix application\n" |
| "MIME_TYPES=\"application/netflix\"\n"; |
| ASSERT_EQ(strlen(kNetflix), |
| base::WriteFile(pepper_dir_.Append("netflix.info"), kNetflix, |
| strlen(kNetflix))); |
| |
| const char kOther[] = |
| "PLUGIN_NAME=Some other plugin\n" |
| "FILE_NAME=/opt/google/chrome/pepper/other.so\n"; |
| ASSERT_EQ(strlen(kOther), base::WriteFile(pepper_dir_.Append("other.info"), |
| kOther, strlen(kOther))); |
| |
| const char kMissingFileName[] = |
| "PLUGIN_NAME=Foo\n" |
| "VERSION=2.3\n"; |
| ASSERT_EQ(strlen(kMissingFileName), |
| base::WriteFile(pepper_dir_.Append("broken.info"), kMissingFileName, |
| strlen(kMissingFileName))); |
| |
| ASSERT_TRUE(Init()); |
| ASSERT_TRUE(builder_.SetUpChromium()); |
| |
| EXPECT_EQ("--ppapi-flash-path=/opt/google/chrome/pepper/flash.so", |
| GetFirstArgWithPrefix("--ppapi-flash-path")); |
| EXPECT_EQ("--ppapi-flash-version=1.2.3.4", |
| GetFirstArgWithPrefix("--ppapi-flash-version")); |
| |
| // Plugins are ordered alphabetically by registration info. |
| const char kExpected[] = |
| "--register-pepper-plugins=" |
| "/opt/google/chrome/pepper/netflix.so#Netflix#" |
| "Helper for the Netflix application#2.0.0;application/netflix," |
| "/opt/google/chrome/pepper/other.so#Some other plugin;"; |
| EXPECT_EQ(kExpected, GetFirstArgWithPrefix("--register-pepper-plugins")); |
| } |
| |
| } // namespace ui |
| } // namespace chromeos |