You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by to...@apache.org on 2016/08/25 21:51:05 UTC

[4/5] kudu git commit: tool: basic integration test

tool: basic integration test

So far all it does is spot check some help pages, but in the future we
should augment it to test functionality too. For now that's not a big deal
because every tool function is covered in either master_migration-itest or
master_failover-itest.

Change-Id: Ib386882c1874e987d5824cfe742cc86627cd9eaa
Reviewed-on: http://gerrit.cloudera.org:8080/4058
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <to...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/7321b38b
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/7321b38b
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/7321b38b

Branch: refs/heads/master
Commit: 7321b38be0303876fcc71b5c73e3e6eddbbdf012
Parents: a1e6b88
Author: Adar Dembo <ad...@cloudera.com>
Authored: Thu Aug 18 20:50:39 2016 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Thu Aug 25 21:46:35 2016 +0000

----------------------------------------------------------------------
 build-support/dist_test.py       |  13 ++-
 src/kudu/tools/CMakeLists.txt    |   3 +
 src/kudu/tools/kudu-tool-test.cc | 154 ++++++++++++++++++++++++++++++++++
 src/kudu/util/test_macros.h      |  21 ++++-
 4 files changed, 188 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kudu/blob/7321b38b/build-support/dist_test.py
----------------------------------------------------------------------
diff --git a/build-support/dist_test.py b/build-support/dist_test.py
index f56513f..d391cf4 100755
--- a/build-support/dist_test.py
+++ b/build-support/dist_test.py
@@ -182,7 +182,10 @@ def ldd_deps(exe):
   If the provided 'exe' is not a binary executable, returns
   an empty list.
   """
-  if exe.endswith(".sh"):
+  if (exe.endswith(".pl") or
+      exe.endswith(".py") or
+      exe.endswith(".sh") or
+      exe.endswith(".txt")):
     return []
   p = subprocess.Popen(["ldd", exe], stdout=subprocess.PIPE)
   out, err = p.communicate()
@@ -243,7 +246,13 @@ def create_archive_input(staging, argv,
     if os.path.isdir(d):
       d += "/"
     deps.append(d)
-  for d in deps:
+    # DEPS_FOR_ALL may include binaries whose dependencies are not dependencies
+    # of the test executable. We must include those dependencies in the archive
+    # for the binaries to be usable.
+    deps.extend(ldd_deps(d))
+
+  # Deduplicate dependencies included via DEPS_FOR_ALL.
+  for d in set(deps):
     # System libraries will end up being relative paths out
     # of the build tree. We need to copy those into the build
     # tree somewhere.

http://git-wip-us.apache.org/repos/asf/kudu/blob/7321b38b/src/kudu/tools/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/kudu/tools/CMakeLists.txt b/src/kudu/tools/CMakeLists.txt
index c66b314..ce361f0 100644
--- a/src/kudu/tools/CMakeLists.txt
+++ b/src/kudu/tools/CMakeLists.txt
@@ -115,6 +115,9 @@ ADD_KUDU_TEST(ksck_remote-test)
 ADD_KUDU_TEST(kudu-admin-test)
 ADD_KUDU_TEST_DEPENDENCIES(kudu-admin-test
   kudu-admin)
+ADD_KUDU_TEST(kudu-tool-test)
+ADD_KUDU_TEST_DEPENDENCIES(kudu-tool-test
+  kudu)
 ADD_KUDU_TEST(kudu-ts-cli-test)
 ADD_KUDU_TEST_DEPENDENCIES(kudu-ts-cli-test
   kudu-ts-cli)

http://git-wip-us.apache.org/repos/asf/kudu/blob/7321b38b/src/kudu/tools/kudu-tool-test.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc
new file mode 100644
index 0000000..2700fb1
--- /dev/null
+++ b/src/kudu/tools/kudu-tool-test.cc
@@ -0,0 +1,154 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <glog/stl_logging.h>
+
+#include "kudu/gutil/strings/split.h"
+#include "kudu/gutil/strings/substitute.h"
+#include "kudu/util/env.h"
+#include "kudu/util/path_util.h"
+#include "kudu/util/subprocess.h"
+#include "kudu/util/test_macros.h"
+#include "kudu/util/test_util.h"
+
+namespace kudu {
+namespace tools {
+
+using std::string;
+using std::vector;
+
+class ToolTest : public KuduTest {
+ public:
+  ToolTest() {
+    string exe;
+    CHECK_OK(env_->GetExecutablePath(&exe));
+    string bin_root = DirName(exe);
+    string tool_path = JoinPathSegments(bin_root, "kudu");
+    CHECK(env_->FileExists(tool_path)) << "kudu tool not found at " << tool_path;
+    tool_path_ = tool_path;
+  }
+
+  Status RunTool(const string& arg_str,
+                 vector<string>* stdout_lines,
+                 vector<string>* stderr_lines) const {
+    vector<string> args = { tool_path_ };
+    vector<string> more_args = strings::Split(arg_str, " ",
+                                              strings::SkipEmpty());
+    args.insert(args.end(), more_args.begin(), more_args.end());
+
+    string stdout;
+    string stderr;
+    Status s = Subprocess::Call(args, &stdout, &stderr);
+    StripWhiteSpace(&stdout);
+    StripWhiteSpace(&stderr);
+    *stdout_lines = strings::Split(stdout, "\n", strings::SkipEmpty());
+    *stderr_lines = strings::Split(stderr, "\n", strings::SkipEmpty());
+    return s;
+
+  }
+
+  void RunTestHelp(const string& arg_str,
+                   const vector<string>& regexes,
+                   const Status& expected_status = Status::OK()) const {
+    vector<string> stdout;
+    vector<string> stderr;
+    Status s = RunTool(arg_str, &stdout, &stderr);
+    SCOPED_TRACE(stdout);
+    SCOPED_TRACE(stderr);
+
+    // These are always true for showing help.
+    ASSERT_TRUE(s.IsRuntimeError());
+    ASSERT_TRUE(stdout.empty());
+    ASSERT_FALSE(stderr.empty());
+
+    // If it was an invalid command, the usage string is on the second line.
+    int usage_idx = 0;
+    if (!expected_status.ok()) {
+      ASSERT_EQ(expected_status.ToString(), stderr[0]);
+      usage_idx = 1;
+    }
+    ASSERT_EQ(0, stderr[usage_idx].find("Usage: "));
+
+    // Strip away everything up to the usage string to test for regexes.
+    vector<string> remaining_lines;
+    for (int i = usage_idx + 1; i < stderr.size(); i++) {
+      remaining_lines.push_back(stderr[i]);
+    }
+    for (const auto& r : regexes) {
+      ASSERT_STRINGS_ANY_MATCH(remaining_lines, r);
+    }
+  }
+
+ private:
+  string tool_path_;
+};
+
+TEST_F(ToolTest, TestTopLevelHelp) {
+  const vector<string> kTopLevelRegexes = {
+      "fs.*Kudu filesystem",
+      "pbc.*protobuf container",
+      "tablet.*Kudu replica"
+  };
+  NO_FATALS(RunTestHelp("", kTopLevelRegexes));
+  NO_FATALS(RunTestHelp("--help", kTopLevelRegexes));
+  NO_FATALS(RunTestHelp("not_a_mode", kTopLevelRegexes,
+                        Status::InvalidArgument("unknown command 'not_a_mode'")));
+}
+
+TEST_F(ToolTest, TestModeHelp) {
+  {
+    const vector<string> kFsModeRegexes = {
+        "format.*new Kudu filesystem",
+        "print_uuid.*UUID of a Kudu filesystem"
+    };
+    NO_FATALS(RunTestHelp("fs", kFsModeRegexes));
+    NO_FATALS(RunTestHelp("fs not_a_mode", kFsModeRegexes,
+                          Status::InvalidArgument("unknown command 'not_a_mode'")));
+  }
+  {
+    const vector<string> kTabletModeRegexes = {
+        "cmeta.*consensus metadata file",
+        "copy.*Copy a replica"
+    };
+    NO_FATALS(RunTestHelp("tablet", kTabletModeRegexes));
+  }
+  {
+    const vector<string> kCmetaModeRegexes = {
+        "print_replica_uuids.*Print all replica UUIDs",
+        "rewrite_raft_config.*Rewrite a replica"
+    };
+    NO_FATALS(RunTestHelp("tablet cmeta", kCmetaModeRegexes));
+  }
+}
+
+TEST_F(ToolTest, TestActionHelp) {
+  const vector<string> kFormatActionRegexes = {
+      "-fs_wal_dir \\(Directory",
+      "-fs_data_dirs \\(Comma-separated list",
+      "-uuid \\(The uuid"
+  };
+  NO_FATALS(RunTestHelp("fs format --help", kFormatActionRegexes));
+  NO_FATALS(RunTestHelp("fs format extra_arg", kFormatActionRegexes,
+      Status::InvalidArgument("too many arguments: 'extra_arg'")));
+}
+
+} // namespace tools
+} // namespace kudu

http://git-wip-us.apache.org/repos/asf/kudu/blob/7321b38b/src/kudu/util/test_macros.h
----------------------------------------------------------------------
diff --git a/src/kudu/util/test_macros.h b/src/kudu/util/test_macros.h
index e7950a9..a5f151f 100644
--- a/src/kudu/util/test_macros.h
+++ b/src/kudu/util/test_macros.h
@@ -63,7 +63,9 @@
   ASSERT_THAT(str, testing::ContainsRegex(pattern))
 
 // Batched substring regular expressions in extended regex (POSIX) syntax.
-#define ASSERT_STRINGS_MATCH(strings, pattern) do { \
+//
+// All strings must match the pattern.
+#define ASSERT_STRINGS_ALL_MATCH(strings, pattern) do { \
   const auto& _strings = (strings); \
   const auto& _pattern = (pattern); \
   int _str_idx = 0; \
@@ -75,6 +77,23 @@
   } \
 } while (0)
 
+// Batched substring regular expressions in extended regex (POSIX) syntax.
+//
+// At least one string must match the pattern.
+#define ASSERT_STRINGS_ANY_MATCH(strings, pattern) do { \
+  const auto& _strings = (strings); \
+  const auto& _pattern = (pattern); \
+  bool matched = false; \
+  for (const auto& str : _strings) { \
+    if (testing::internal::RE::PartialMatch(str, testing::internal::RE(_pattern))) { \
+      matched = true; \
+      break; \
+    } \
+  } \
+  ASSERT_TRUE(matched) \
+      << "not one string matched pattern " << _pattern; \
+} while (0)
+
 #define ASSERT_FILE_EXISTS(env, path) do { \
   std::string _s = path; \
   ASSERT_TRUE(env->FileExists(_s)) \