| // 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<StartupOptions>( | 
 |             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<std::string> 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<std::string>& args, | 
 |                                      const std::string& expected_error) const { | 
 |     std::string error; | 
 |     const std::unique_ptr<CommandLine> result = | 
 |         option_processor_->SplitCommandLine(args, &error); | 
 |     ASSERT_EQ(expected_error, error); | 
 |     ASSERT_EQ(nullptr, result); | 
 |   } | 
 |  | 
 |   void SuccessfulSplitStartupOptionsTest(const std::vector<std::string>& args, | 
 |                                          const CommandLine& expected) const { | 
 |     std::string error; | 
 |     const std::unique_ptr<CommandLine> 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<std::string> 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<std::string>({}), | 
 |               option_processor_->GetExplicitCommandArguments()); | 
 |   } | 
 |  | 
 |   const std::string workspace_; | 
 |   const std::string cwd_; | 
 |   const std::unique_ptr<WorkspaceLayout> workspace_layout_; | 
 |   std::unique_ptr<OptionProcessor> option_processor_; | 
 | }; | 
 |  | 
 | TEST_F(OptionProcessorTest, CanParseOptions) { | 
 |   const std::vector<std::string> 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<std::string>({"--flag", "//my:target", "--flag2=42"}), | 
 |             option_processor_->GetExplicitCommandArguments()); | 
 | } | 
 |  | 
 | TEST_F(OptionProcessorTest, CanParseHelpCommandSurroundedByOtherArgs) { | 
 |   const std::vector<std::string> 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<std::string>({"--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<std::string> 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<std::string>({}), | 
 |             option_processor_->GetExplicitCommandArguments()); | 
 | } | 
 |  | 
 | TEST_F(OptionProcessorTest, CanParseDifferentStartupArgs) { | 
 |   const std::vector<std::string> 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<std::string>({}), | 
 |             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<std::string> input = {"foo"}; | 
 |   std::vector<std::string> expected = {}; | 
 |   ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); | 
 | } | 
 |  | 
 | TEST_F(OptionProcessorTest, TestDedupePathsOmitsEmptyPath) { | 
 |   std::vector<std::string> input = {""}; | 
 |   std::vector<std::string> 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<std::string> 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<std::string> input = {foo_path, foo_path}; | 
 |   std::vector<std::string> 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<std::string> input = {foo_path, relative_foo_path}; | 
 |   std::vector<std::string> 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<std::string> input = {foo_path, sym_foo_path}; | 
 |   std::vector<std::string> expected = {foo_path}; | 
 |   ASSERT_EQ(expected, internal::DedupeBlazercPaths(input)); | 
 | } | 
 | #endif  // !defined(_WIN32) && !defined(__CYGWIN__) | 
 |  | 
 | }  // namespace blaze |