// Copyright 2017 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/main/cpp/option_processor.h" #include "src/main/cpp/bazel_startup_options.h" #include "src/main/cpp/blaze_util.h" #include "src/main/cpp/blaze_util_platform.h" #include "src/main/cpp/option_processor-internal.h" #include "src/main/cpp/util/file.h" #include "src/main/cpp/util/file_platform.h" #include "src/main/cpp/util/path.h" #include "src/main/cpp/workspace_layout.h" #include "googletest/include/gtest/gtest.h" namespace blaze { class OptionProcessorTest : public ::testing::Test { protected: OptionProcessorTest() : workspace_( blaze_util::JoinPath(blaze::GetEnv("TEST_TMPDIR"), "testdir")), cwd_("cwd"), workspace_layout_(new WorkspaceLayout()) {} ~OptionProcessorTest() override {} void SetUp() override { ASSERT_TRUE(blaze_util::MakeDirectories(workspace_, 0755)); option_processor_.reset(new OptionProcessor( workspace_layout_.get(), std::unique_ptr( new BazelStartupOptions(workspace_layout_.get())))); } void TearDown() override { // TODO(bazel-team): The code below deletes all the files in the workspace // but it intentionally skips directories. As a consequence, there may be // empty directories from test to test. Remove this once // blaze_util::DeleteDirectories(path) exists. std::vector files_in_workspace; blaze_util::GetAllFilesUnder(workspace_, &files_in_workspace); for (const std::string& file : files_in_workspace) { blaze_util::UnlinkPath(file); } } void FailedSplitStartupOptionsTest(const std::vector& args, const std::string& expected_error) const { std::string error; const std::unique_ptr result = option_processor_->SplitCommandLine(args, &error); ASSERT_EQ(expected_error, error); ASSERT_EQ(nullptr, result); } void SuccessfulSplitStartupOptionsTest(const std::vector& args, const CommandLine& expected) const { std::string error; const std::unique_ptr result = option_processor_->SplitCommandLine(args, &error); ASSERT_EQ("", error); EXPECT_EQ(expected.path_to_binary, result->path_to_binary); EXPECT_EQ(expected.startup_args, result->startup_args); EXPECT_EQ(expected.command, result->command); EXPECT_EQ(expected.command_args, result->command_args); } void HelpArgIsInterpretedAsACommand(const std::string& arg) { const std::vector args = {"bazel", arg}; std::string error; ASSERT_EQ(blaze_exit_code::SUCCESS, option_processor_->ParseOptions(args, workspace_, cwd_, &error)) << error; ASSERT_EQ("", error); EXPECT_EQ(arg, option_processor_->GetCommand()); EXPECT_EQ(std::vector({}), option_processor_->GetExplicitCommandArguments()); } const std::string workspace_; const std::string cwd_; const std::unique_ptr workspace_layout_; std::unique_ptr option_processor_; }; TEST_F(OptionProcessorTest, CanParseOptions) { const std::vector args = {"bazel", "--host_jvm_args=MyParam", "--nobatch", "command", "--flag", "//my:target", "--flag2=42"}; std::string error; ASSERT_EQ(blaze_exit_code::SUCCESS, option_processor_->ParseOptions(args, workspace_, cwd_, &error)) << error; ASSERT_EQ("", error); ASSERT_EQ(size_t(1), option_processor_->GetParsedStartupOptions()->host_jvm_args.size()); EXPECT_EQ("MyParam", option_processor_->GetParsedStartupOptions()->host_jvm_args[0]); EXPECT_FALSE(option_processor_->GetParsedStartupOptions()->batch); EXPECT_EQ("command", option_processor_->GetCommand()); EXPECT_EQ(std::vector({"--flag", "//my:target", "--flag2=42"}), option_processor_->GetExplicitCommandArguments()); } TEST_F(OptionProcessorTest, CanParseHelpCommandSurroundedByOtherArgs) { const std::vector args = {"bazel", "--host_jvm_args=MyParam", "--nobatch", "help", "--flag", "//my:target", "--flag2=42"}; std::string error; ASSERT_EQ(blaze_exit_code::SUCCESS, option_processor_->ParseOptions(args, workspace_, cwd_, &error)) << error; ASSERT_EQ("", error); ASSERT_EQ(size_t(1), option_processor_->GetParsedStartupOptions()->host_jvm_args.size()); EXPECT_EQ("MyParam", option_processor_->GetParsedStartupOptions()->host_jvm_args[0]); EXPECT_FALSE(option_processor_->GetParsedStartupOptions()->batch); EXPECT_EQ("help", option_processor_->GetCommand()); EXPECT_EQ(std::vector({"--flag", "//my:target", "--flag2=42"}), option_processor_->GetExplicitCommandArguments()); } TEST_F(OptionProcessorTest, CanParseHelpCommand) { HelpArgIsInterpretedAsACommand("help"); } TEST_F(OptionProcessorTest, CanParseHelpShortFlag) { HelpArgIsInterpretedAsACommand("-h"); } TEST_F(OptionProcessorTest, CanParseHelpFlag) { HelpArgIsInterpretedAsACommand("-help"); } TEST_F(OptionProcessorTest, CanParseEmptyArgs) { const std::vector args = {"bazel"}; std::string error; ASSERT_EQ(blaze_exit_code::SUCCESS, option_processor_->ParseOptions(args, workspace_, cwd_, &error)) << error; ASSERT_EQ("", error); EXPECT_EQ("", option_processor_->GetCommand()); EXPECT_EQ(std::vector({}), option_processor_->GetExplicitCommandArguments()); } TEST_F(OptionProcessorTest, CanParseDifferentStartupArgs) { const std::vector args = {"bazel", "--nobatch", "--host_jvm_args=MyParam", "--host_jvm_args", "42"}; std::string error; ASSERT_EQ(blaze_exit_code::SUCCESS, option_processor_->ParseOptions(args, workspace_, cwd_, &error)) << error; ASSERT_EQ("", error); ASSERT_EQ(size_t(2), option_processor_->GetParsedStartupOptions()->host_jvm_args.size()); EXPECT_EQ("MyParam", option_processor_->GetParsedStartupOptions()->host_jvm_args[0]); EXPECT_EQ("42", option_processor_->GetParsedStartupOptions()->host_jvm_args[1]); EXPECT_EQ("", option_processor_->GetCommand()); EXPECT_EQ(std::vector({}), option_processor_->GetExplicitCommandArguments()); } TEST_F(OptionProcessorTest, SplitCommandLineWithEmptyArgs) { FailedSplitStartupOptionsTest( {}, "Unable to split command line, args is empty"); } TEST_F(OptionProcessorTest, SplitCommandLineWithAllParams) { SuccessfulSplitStartupOptionsTest( {"bazel", "--nomaster_bazelrc", "build", "--bar", ":mytarget"}, CommandLine("bazel", {"--nomaster_bazelrc"}, "build", {"--bar", ":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineWithAbsolutePathToBinary) { SuccessfulSplitStartupOptionsTest( {"mybazel", "build", ":mytarget"}, CommandLine("mybazel", {}, "build", {":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineWithUnaryStartupWithEquals) { SuccessfulSplitStartupOptionsTest( {"bazel", "--bazelrc=foo", "build", ":mytarget"}, CommandLine("bazel", {"--bazelrc=foo"}, "build", {":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineWithUnaryStartupWithoutEquals) { SuccessfulSplitStartupOptionsTest( {"bazel", "--bazelrc", "foo", "build", ":mytarget"}, CommandLine("bazel", {"--bazelrc=foo"}, "build", {":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineWithIncompleteUnaryOption) { FailedSplitStartupOptionsTest( {"bazel", "--bazelrc"}, "Startup option '--bazelrc' expects a value.\n" "Usage: '--bazelrc=somevalue' or '--bazelrc somevalue'.\n" " For more info, run 'bazel help startup_options'."); } TEST_F(OptionProcessorTest, SplitCommandLineWithMultipleStartup) { SuccessfulSplitStartupOptionsTest( {"bazel", "--bazelrc", "foo", "--nomaster_bazelrc", "build", ":mytarget"}, CommandLine("bazel", {"--bazelrc=foo", "--nomaster_bazelrc"}, "build", {":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineWithNoStartupArgs) { SuccessfulSplitStartupOptionsTest( {"bazel", "build", ":mytarget"}, CommandLine("bazel", {}, "build", {":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineWithNoCommandArgs) { SuccessfulSplitStartupOptionsTest( {"bazel", "build"}, CommandLine("bazel", {}, "build", {})); } TEST_F(OptionProcessorTest, SplitCommandLineWithBlazeHelp) { SuccessfulSplitStartupOptionsTest( {"bazel", "help"}, CommandLine("bazel", {}, "help", {})); SuccessfulSplitStartupOptionsTest( {"bazel", "-h"}, CommandLine("bazel", {}, "-h", {})); SuccessfulSplitStartupOptionsTest( {"bazel", "-help"}, CommandLine("bazel", {}, "-help", {})); SuccessfulSplitStartupOptionsTest( {"bazel", "--help"}, CommandLine("bazel", {}, "--help", {})); } TEST_F(OptionProcessorTest, SplitCommandLineWithBlazeVersion) { SuccessfulSplitStartupOptionsTest( {"bazel", "version"}, CommandLine("bazel", {}, "version", {})); } TEST_F(OptionProcessorTest, SplitCommandLineWithMultipleCommandArgs) { SuccessfulSplitStartupOptionsTest( {"bazel", "build", "--foo", "-s", ":mytarget"}, CommandLine("bazel", {}, "build", {"--foo", "-s", ":mytarget"})); } TEST_F(OptionProcessorTest, SplitCommandLineFailsWithDashDashInStartupArgs) { FailedSplitStartupOptionsTest( {"bazel", "--"}, "Unknown startup option: '--'.\n" " For more info, run 'bazel help startup_options'."); } TEST_F(OptionProcessorTest, SplitCommandLineWithDashDash) { SuccessfulSplitStartupOptionsTest( {"bazel", "--nomaster_bazelrc", "build", "--b", "--", ":mytarget"}, CommandLine("bazel", {"--nomaster_bazelrc"}, "build", {"--b", "--", ":mytarget"})); } TEST_F(OptionProcessorTest, TestDedupePathsOmitsInvalidPath) { std::vector input = {"foo"}; std::vector expected = {}; ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); } TEST_F(OptionProcessorTest, TestDedupePathsOmitsEmptyPath) { std::vector input = {""}; std::vector expected = {}; ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); } TEST_F(OptionProcessorTest, TestDedupePathsWithDifferentFiles) { std::string foo_path = blaze_util::JoinPath(workspace_, "foo"); std::string bar_path = blaze_util::JoinPath(workspace_, "bar"); ASSERT_TRUE(blaze_util::WriteFile("foo", foo_path)); ASSERT_TRUE(blaze_util::WriteFile("bar", bar_path)); std::vector input = {foo_path, bar_path}; ASSERT_EQ(input, internal::DedupeBlazercPaths(input)); } TEST_F(OptionProcessorTest, TestDedupePathsWithSameFile) { std::string foo_path = blaze_util::JoinPath(workspace_, "foo"); ASSERT_TRUE(blaze_util::WriteFile("foo", foo_path)); std::vector input = {foo_path, foo_path}; std::vector expected = {foo_path}; ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); } TEST_F(OptionProcessorTest, TestDedupePathsWithRelativePath) { std::string dir(blaze_util::JoinPath(workspace_, "dir")); std::string foo_path(blaze_util::JoinPath(dir, "foo")); std::string relative_foo_path(blaze_util::JoinPath(dir, "../dir/foo")); ASSERT_TRUE(blaze_util::MakeDirectories(dir, 0755)); ASSERT_TRUE(blaze_util::WriteFile("foo", foo_path)); std::vector input = {foo_path, relative_foo_path}; std::vector expected = {foo_path}; ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); } #if !defined(_WIN32) && !defined(__CYGWIN__) static bool Symlink(const std::string& old_path, const std::string& new_path) { return symlink(old_path.c_str(), new_path.c_str()) == 0; } TEST_F(OptionProcessorTest, TestDedupePathsWithSymbolicLink) { std::string foo_path = blaze_util::JoinPath(workspace_, "foo"); std::string sym_foo_path = blaze_util::JoinPath(workspace_, "sym_foo"); ASSERT_TRUE(blaze_util::WriteFile("foo", foo_path)); ASSERT_TRUE(Symlink(foo_path, sym_foo_path)); std::vector input = {foo_path, sym_foo_path}; std::vector expected = {foo_path}; ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); } #endif // !defined(_WIN32) && !defined(__CYGWIN__) } // namespace blaze