Open source {workspace_layout,startup_options,option_processor}_test.

Additionally rewrite the option_processor_test to make it more flexible.

PiperOrigin-RevId: 160958736
diff --git a/src/test/cpp/option_processor_test.cc b/src/test/cpp/option_processor_test.cc
new file mode 100644
index 0000000..da7c743
--- /dev/null
+++ b/src/test/cpp/option_processor_test.cc
@@ -0,0 +1,374 @@
+// 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/blaze_util.h"
+#include "src/main/cpp/blaze_util_platform.h"
+#include "src/main/cpp/option_processor-internal.h"
+#include "src/main/cpp/workspace_layout.h"
+#include "src/main/cpp/util/file.h"
+#include "src/main/cpp/util/file_platform.h"
+#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 StartupOptions(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(nullptr, result);
+    ASSERT_EQ(expected_error, error);
+  }
+
+  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);
+  }
+
+  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));
+
+  ASSERT_EQ("", error);
+  ASSERT_EQ(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, CanParseHelpArgs) {
+  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));
+
+  ASSERT_EQ(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, CanParseEmptyArgs) {
+  const std::vector<std::string> args = {"bazel"};
+  std::string error;
+  ASSERT_EQ(blaze_exit_code::SUCCESS,
+            option_processor_->ParseOptions(args, workspace_, cwd_, &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));
+
+  ASSERT_EQ(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, CommandLineBlazercTest) {
+  const std::string cmdline_rc_path =
+      blaze_util::JoinPath(workspace_, "mybazelrc");
+  ASSERT_TRUE(blaze_util::MakeDirectories(
+      blaze_util::Dirname(cmdline_rc_path), 0755));
+  ASSERT_TRUE(blaze_util::WriteFile("startup --foo", cmdline_rc_path, 0755));
+
+  const std::vector<std::string> args =
+      {"bazel", "--bazelrc=" + cmdline_rc_path, "build"};
+  const std::string expected_error =
+      "Unknown startup option: '--foo'.\n"
+          "  For more info, run 'bazel help startup_options'.";
+  std::string error;
+  ASSERT_NE(blaze_exit_code::SUCCESS,
+            option_processor_->ParseOptions(args, workspace_, cwd_, &error));
+  ASSERT_EQ(expected_error, error);
+}
+
+TEST_F(OptionProcessorTest, NoMasterBlazercAndBlazercWorkTogetherCorrectly) {
+  const std::string cmdline_rc_path =
+      blaze_util::JoinPath(workspace_, "mybazelrc");
+  ASSERT_TRUE(blaze_util::MakeDirectories(
+      blaze_util::Dirname(cmdline_rc_path), 0755));
+  ASSERT_TRUE(blaze_util::WriteFile("startup --max_idle_secs=123",
+                                    cmdline_rc_path, 0755));
+
+  const std::string master_rc_path =
+      blaze_util::JoinPath(workspace_, "tools/bazel.rc");
+  ASSERT_TRUE(blaze_util::MakeDirectories(
+      blaze_util::Dirname(master_rc_path), 0755));
+  ASSERT_TRUE(blaze_util::WriteFile("startup --foo", master_rc_path, 0755));
+
+  const std::vector<std::string> args =
+      {"bazel",
+       "--bazelrc=" + cmdline_rc_path, "--nomaster_bazelrc",
+       "build"};
+  std::string error;
+  ASSERT_EQ(blaze_exit_code::SUCCESS,
+            option_processor_->ParseOptions(args, workspace_, cwd_, &error));
+
+  EXPECT_EQ(123, option_processor_->GetParsedStartupOptions()->max_idle_secs);
+}
+
+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, 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(COMPILER_MSVC) && !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(COMPILER_MSVC) && !defined(__CYGWIN__)
+
+}  // namespace blaze