aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/core/util
diff options
context:
space:
mode:
authorGravatar Vijay Pai <vpai@google.com>2018-02-08 12:18:09 -0800
committerGravatar Vijay Pai <vpai@google.com>2018-02-08 12:18:09 -0800
commit18b9133f31e394ea81c6ae5a44efe802904b3140 (patch)
tree14cd93ac3ee2964f3b185a5c02b456283e2fd515 /test/core/util
parent804f339338695879b7529c6d7f1d46c6bfdec19d (diff)
parentd3fb421ffacfef92af692c9753a97344f9812559 (diff)
Merge branch 'master' into gpr_review
Diffstat (limited to 'test/core/util')
-rw-r--r--test/core/util/BUILD15
-rw-r--r--test/core/util/cmdline.cc330
-rw-r--r--test/core/util/cmdline.h80
-rw-r--r--test/core/util/cmdline_test.cc496
-rw-r--r--test/core/util/subprocess.h36
-rw-r--r--test/core/util/subprocess_posix.cc99
-rw-r--r--test/core/util/subprocess_windows.cc126
7 files changed, 1182 insertions, 0 deletions
diff --git a/test/core/util/BUILD b/test/core/util/BUILD
index 2237cfc173..886cfddf86 100644
--- a/test/core/util/BUILD
+++ b/test/core/util/BUILD
@@ -51,6 +51,7 @@ grpc_cc_library(
grpc_cc_library(
name = "grpc_test_util_base",
srcs = [
+ "cmdline.cc",
"grpc_profiler.cc",
"histogram.cc",
"mock_endpoint.cc",
@@ -61,11 +62,14 @@ grpc_cc_library(
"port_server_client.cc",
"reconnect_server.cc",
"slice_splitter.cc",
+ "subprocess_posix.cc",
+ "subprocess_windows.cc",
"test_tcp_server.cc",
"tracer_util.cc",
"trickle_endpoint.cc",
],
hdrs = [
+ "cmdline.h",
"grpc_profiler.h",
"histogram.h",
"mock_endpoint.h",
@@ -74,6 +78,7 @@ grpc_cc_library(
"port.h",
"port_server_client.h",
"reconnect_server.h",
+ "subprocess.h",
"slice_splitter.h",
"test_tcp_server.h",
"tracer_util.h",
@@ -109,6 +114,16 @@ grpc_cc_library(
],
)
+grpc_cc_test(
+ name = "cmdline_test",
+ srcs = ["cmdline_test.cc"],
+ language = "C++",
+ deps = [
+ ":grpc_test_util",
+ "//:gpr",
+ ],
+)
+
grpc_cc_library(
name = "fuzzer_corpus_test",
testonly = 1,
diff --git a/test/core/util/cmdline.cc b/test/core/util/cmdline.cc
new file mode 100644
index 0000000000..20bce273cd
--- /dev/null
+++ b/test/core/util/cmdline.cc
@@ -0,0 +1,330 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 "test/core/util/cmdline.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/gpr/string.h"
+
+typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype;
+
+typedef struct arg {
+ const char* name;
+ const char* help;
+ argtype type;
+ void* value;
+ struct arg* next;
+} arg;
+
+struct gpr_cmdline {
+ const char* description;
+ arg* args;
+ const char* argv0;
+
+ const char* extra_arg_name;
+ const char* extra_arg_help;
+ void (*extra_arg)(void* user_data, const char* arg);
+ void* extra_arg_user_data;
+
+ int (*state)(gpr_cmdline* cl, char* arg);
+ arg* cur_arg;
+
+ int survive_failure;
+};
+
+static int normal_state(gpr_cmdline* cl, char* arg);
+
+gpr_cmdline* gpr_cmdline_create(const char* description) {
+ gpr_cmdline* cl = (gpr_cmdline*)gpr_zalloc(sizeof(gpr_cmdline));
+
+ cl->description = description;
+ cl->state = normal_state;
+
+ return cl;
+}
+
+void gpr_cmdline_set_survive_failure(gpr_cmdline* cl) {
+ cl->survive_failure = 1;
+}
+
+void gpr_cmdline_destroy(gpr_cmdline* cl) {
+ while (cl->args) {
+ arg* a = cl->args;
+ cl->args = a->next;
+ gpr_free(a);
+ }
+ gpr_free(cl);
+}
+
+static void add_arg(gpr_cmdline* cl, const char* name, const char* help,
+ argtype type, void* value) {
+ arg* a;
+
+ for (a = cl->args; a; a = a->next) {
+ GPR_ASSERT(0 != strcmp(a->name, name));
+ }
+
+ a = (arg*)gpr_zalloc(sizeof(arg));
+ a->name = name;
+ a->help = help;
+ a->type = type;
+ a->value = value;
+ a->next = cl->args;
+ cl->args = a;
+}
+
+void gpr_cmdline_add_int(gpr_cmdline* cl, const char* name, const char* help,
+ int* value) {
+ add_arg(cl, name, help, ARGTYPE_INT, value);
+}
+
+void gpr_cmdline_add_flag(gpr_cmdline* cl, const char* name, const char* help,
+ int* value) {
+ add_arg(cl, name, help, ARGTYPE_BOOL, value);
+}
+
+void gpr_cmdline_add_string(gpr_cmdline* cl, const char* name, const char* help,
+ const char** value) {
+ add_arg(cl, name, help, ARGTYPE_STRING, value);
+}
+
+void gpr_cmdline_on_extra_arg(
+ gpr_cmdline* cl, const char* name, const char* help,
+ void (*on_extra_arg)(void* user_data, const char* arg), void* user_data) {
+ GPR_ASSERT(!cl->extra_arg);
+ GPR_ASSERT(on_extra_arg);
+
+ cl->extra_arg = on_extra_arg;
+ cl->extra_arg_user_data = user_data;
+ cl->extra_arg_name = name;
+ cl->extra_arg_help = help;
+}
+
+/* recursively descend argument list, adding the last element
+ to s first - so that arguments are added in the order they were
+ added to the list by api calls */
+static void add_args_to_usage(gpr_strvec* s, arg* a) {
+ char* tmp;
+
+ if (!a) return;
+ add_args_to_usage(s, a->next);
+
+ switch (a->type) {
+ case ARGTYPE_BOOL:
+ gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name);
+ gpr_strvec_add(s, tmp);
+ break;
+ case ARGTYPE_STRING:
+ gpr_asprintf(&tmp, " [--%s=string]", a->name);
+ gpr_strvec_add(s, tmp);
+ break;
+ case ARGTYPE_INT:
+ gpr_asprintf(&tmp, " [--%s=int]", a->name);
+ gpr_strvec_add(s, tmp);
+ break;
+ }
+}
+
+char* gpr_cmdline_usage_string(gpr_cmdline* cl, const char* argv0) {
+ /* TODO(ctiller): make this prettier */
+ gpr_strvec s;
+ char* tmp;
+ const char* name = strrchr(argv0, '/');
+
+ if (name) {
+ name++;
+ } else {
+ name = argv0;
+ }
+
+ gpr_strvec_init(&s);
+
+ gpr_asprintf(&tmp, "Usage: %s", name);
+ gpr_strvec_add(&s, tmp);
+ add_args_to_usage(&s, cl->args);
+ if (cl->extra_arg) {
+ gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name);
+ gpr_strvec_add(&s, tmp);
+ }
+ gpr_strvec_add(&s, gpr_strdup("\n"));
+
+ tmp = gpr_strvec_flatten(&s, nullptr);
+ gpr_strvec_destroy(&s);
+ return tmp;
+}
+
+static int print_usage_and_die(gpr_cmdline* cl) {
+ char* usage = gpr_cmdline_usage_string(cl, cl->argv0);
+ fprintf(stderr, "%s", usage);
+ gpr_free(usage);
+ if (!cl->survive_failure) {
+ exit(1);
+ }
+ return 0;
+}
+
+static int extra_state(gpr_cmdline* cl, char* str) {
+ if (!cl->extra_arg) {
+ return print_usage_and_die(cl);
+ }
+ cl->extra_arg(cl->extra_arg_user_data, str);
+ return 1;
+}
+
+static arg* find_arg(gpr_cmdline* cl, char* name) {
+ arg* a;
+
+ for (a = cl->args; a; a = a->next) {
+ if (0 == strcmp(a->name, name)) {
+ break;
+ }
+ }
+
+ if (!a) {
+ fprintf(stderr, "Unknown argument: %s\n", name);
+ return nullptr;
+ }
+
+ return a;
+}
+
+static int value_state(gpr_cmdline* cl, char* str) {
+ long intval;
+ char* end;
+
+ GPR_ASSERT(cl->cur_arg);
+
+ switch (cl->cur_arg->type) {
+ case ARGTYPE_INT:
+ intval = strtol(str, &end, 0);
+ if (*end || intval < INT_MIN || intval > INT_MAX) {
+ fprintf(stderr, "expected integer, got '%s' for %s\n", str,
+ cl->cur_arg->name);
+ return print_usage_and_die(cl);
+ }
+ *(int*)cl->cur_arg->value = (int)intval;
+ break;
+ case ARGTYPE_BOOL:
+ if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) {
+ *(int*)cl->cur_arg->value = 1;
+ } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) {
+ *(int*)cl->cur_arg->value = 0;
+ } else {
+ fprintf(stderr, "expected boolean, got '%s' for %s\n", str,
+ cl->cur_arg->name);
+ return print_usage_and_die(cl);
+ }
+ break;
+ case ARGTYPE_STRING:
+ *(char**)cl->cur_arg->value = str;
+ break;
+ }
+
+ cl->state = normal_state;
+ return 1;
+}
+
+static int normal_state(gpr_cmdline* cl, char* str) {
+ char* eq = nullptr;
+ char* tmp = nullptr;
+ char* arg_name = nullptr;
+ int r = 1;
+
+ if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") ||
+ 0 == strcmp(str, "-h")) {
+ return print_usage_and_die(cl);
+ }
+
+ cl->cur_arg = nullptr;
+
+ if (str[0] == '-') {
+ if (str[1] == '-') {
+ if (str[2] == 0) {
+ /* handle '--' to move to just extra args */
+ cl->state = extra_state;
+ return 1;
+ }
+ str += 2;
+ } else {
+ str += 1;
+ }
+ /* first byte of str is now past the leading '-' or '--' */
+ if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') {
+ /* str is of the form '--no-foo' - it's a flag disable */
+ str += 3;
+ cl->cur_arg = find_arg(cl, str);
+ if (cl->cur_arg == nullptr) {
+ return print_usage_and_die(cl);
+ }
+ if (cl->cur_arg->type != ARGTYPE_BOOL) {
+ fprintf(stderr, "%s is not a flag argument\n", str);
+ return print_usage_and_die(cl);
+ }
+ *(int*)cl->cur_arg->value = 0;
+ return 1; /* early out */
+ }
+ eq = strchr(str, '=');
+ if (eq != nullptr) {
+ /* copy the string into a temp buffer and extract the name */
+ tmp = arg_name = (char*)gpr_malloc((size_t)(eq - str + 1));
+ memcpy(arg_name, str, (size_t)(eq - str));
+ arg_name[eq - str] = 0;
+ } else {
+ arg_name = str;
+ }
+ cl->cur_arg = find_arg(cl, arg_name);
+ if (cl->cur_arg == nullptr) {
+ return print_usage_and_die(cl);
+ }
+ if (eq != nullptr) {
+ /* str was of the type --foo=value, parse the value */
+ r = value_state(cl, eq + 1);
+ } else if (cl->cur_arg->type != ARGTYPE_BOOL) {
+ /* flag types don't have a '--foo value' variant, other types do */
+ cl->state = value_state;
+ } else {
+ /* flag parameter: just set the value */
+ *(int*)cl->cur_arg->value = 1;
+ }
+ } else {
+ r = extra_state(cl, str);
+ }
+
+ gpr_free(tmp);
+ return r;
+}
+
+int gpr_cmdline_parse(gpr_cmdline* cl, int argc, char** argv) {
+ int i;
+
+ GPR_ASSERT(argc >= 1);
+ cl->argv0 = argv[0];
+
+ for (i = 1; i < argc; i++) {
+ if (!cl->state(cl, argv[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/test/core/util/cmdline.h b/test/core/util/cmdline.h
new file mode 100644
index 0000000000..3ae35d6e6a
--- /dev/null
+++ b/test/core/util/cmdline.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_TEST_CORE_UTIL_CMDLINE_H
+#define GRPC_TEST_CORE_UTIL_CMDLINE_H
+
+#include <grpc/support/port_platform.h>
+
+/** Simple command line parser.
+
+ Supports flags that can be specified as -foo, --foo, --no-foo, -no-foo, etc
+ And integers, strings that can be specified as -foo=4, -foo blah, etc
+
+ No support for short command line options (but we may get that in the
+ future.)
+
+ Usage (for a program with a single flag argument 'foo'):
+
+ int main(int argc, char **argv) {
+ gpr_cmdline *cl;
+ int verbose = 0;
+
+ cl = gpr_cmdline_create("My cool tool");
+ gpr_cmdline_add_int(cl, "verbose", "Produce verbose output?", &verbose);
+ gpr_cmdline_parse(cl, argc, argv);
+ gpr_cmdline_destroy(cl);
+
+ if (verbose) {
+ gpr_log(GPR_INFO, "Goodbye cruel world!");
+ }
+
+ return 0;
+ } */
+
+typedef struct gpr_cmdline gpr_cmdline;
+
+/** Construct a command line parser: takes a short description of the tool
+ doing the parsing */
+gpr_cmdline* gpr_cmdline_create(const char* description);
+/** Add an integer parameter, with a name (used on the command line) and some
+ helpful text (used in the command usage) */
+void gpr_cmdline_add_int(gpr_cmdline* cl, const char* name, const char* help,
+ int* value);
+/** The same, for a boolean flag */
+void gpr_cmdline_add_flag(gpr_cmdline* cl, const char* name, const char* help,
+ int* value);
+/** And for a string */
+void gpr_cmdline_add_string(gpr_cmdline* cl, const char* name, const char* help,
+ const char** value);
+/** Set a callback for non-named arguments */
+void gpr_cmdline_on_extra_arg(
+ gpr_cmdline* cl, const char* name, const char* help,
+ void (*on_extra_arg)(void* user_data, const char* arg), void* user_data);
+/** Enable surviving failure: default behavior is to exit the process */
+void gpr_cmdline_set_survive_failure(gpr_cmdline* cl);
+/** Parse the command line; returns 1 on success, on failure either dies
+ (by default) or returns 0 if gpr_cmdline_set_survive_failure() has been
+ called */
+int gpr_cmdline_parse(gpr_cmdline* cl, int argc, char** argv);
+/** Destroy the parser */
+void gpr_cmdline_destroy(gpr_cmdline* cl);
+/** Get a string describing usage */
+char* gpr_cmdline_usage_string(gpr_cmdline* cl, const char* argv0);
+
+#endif /* GRPC_TEST_CORE_UTIL_CMDLINE_H */
diff --git a/test/core/util/cmdline_test.cc b/test/core/util/cmdline_test.cc
new file mode 100644
index 0000000000..069e1ceccf
--- /dev/null
+++ b/test/core/util/cmdline_test.cc
@@ -0,0 +1,496 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+<<<<<<< HEAD:test/core/gpr/cmdline_test.cc
+
+#include "src/core/lib/gpr/useful.h"
+=======
+#include <grpc/support/useful.h>
+
+#include "test/core/util/cmdline.h"
+>>>>>>> master:test/core/util/cmdline_test.cc
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "test at %s:%d", __FILE__, __LINE__)
+
+static void test_simple_int(void) {
+ int x = 1;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("-foo"),
+ const_cast<char*>("3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_int(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_eq_int(void) {
+ int x = 1;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("-foo=3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_int(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_int(void) {
+ int x = 1;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo"),
+ const_cast<char*>("3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_int(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_eq_int(void) {
+ int x = 1;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo=3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_int(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_simple_string(void) {
+ const char* x = nullptr;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("-foo"),
+ const_cast<char*>("3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_string(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == nullptr);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_eq_string(void) {
+ const char* x = nullptr;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("-foo=3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_string(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == nullptr);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_string(void) {
+ const char* x = nullptr;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo"),
+ const_cast<char*>("3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_string(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == nullptr);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_2dash_eq_string(void) {
+ const char* x = nullptr;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo=3")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_string(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == nullptr);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(0 == strcmp(x, "3"));
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_on(void) {
+ int x = 2;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_flag(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_no(void) {
+ int x = 2;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--no-foo")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_flag(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_1(void) {
+ int x = 2;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo=1")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_flag(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_0(void) {
+ int x = 2;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo=0")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_flag(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_true(void) {
+ int x = 2;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo=true")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_flag(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 1);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_flag_val_false(void) {
+ int x = 2;
+ gpr_cmdline* cl;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--foo=false")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_flag(cl, "foo", nullptr, &x);
+ GPR_ASSERT(x == 2);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_many(void) {
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+ gpr_cmdline* cl;
+
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--str"),
+ const_cast<char*>("hello"), const_cast<char*>("-x=4"),
+ const_cast<char*>("-no-flag")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(x == 4);
+ GPR_ASSERT(0 == strcmp(str, "hello"));
+ GPR_ASSERT(flag == 0);
+ gpr_cmdline_destroy(cl);
+}
+
+static void extra_arg_cb(void* user_data, const char* arg) {
+ int* count = static_cast<int*>(user_data);
+ GPR_ASSERT(arg != nullptr);
+ GPR_ASSERT(strlen(arg) == 1);
+ GPR_ASSERT(arg[0] == 'a' + *count);
+ ++*count;
+}
+
+static void test_extra(void) {
+ gpr_cmdline* cl;
+ int count = 0;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("a"),
+ const_cast<char*>("b"), const_cast<char*>("c")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ &count);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(count == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_extra_dashdash(void) {
+ gpr_cmdline* cl;
+ int count = 0;
+ char* args[] = {(char*)__FILE__, const_cast<char*>("--"),
+ const_cast<char*>("a"), const_cast<char*>("b"),
+ const_cast<char*>("c")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ &count);
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(args), args);
+ GPR_ASSERT(count == 3);
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_usage(void) {
+ gpr_cmdline* cl;
+ char* usage;
+
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ nullptr);
+
+ usage = gpr_cmdline_usage_string(cl, "test");
+ GPR_ASSERT(0 == strcmp(usage,
+ "Usage: test [--str=string] [--x=int] "
+ "[--flag|--no-flag] [file...]\n"));
+ gpr_free(usage);
+
+ usage = gpr_cmdline_usage_string(cl, "/foo/test");
+ GPR_ASSERT(0 == strcmp(usage,
+ "Usage: test [--str=string] [--x=int] "
+ "[--flag|--no-flag] [file...]\n"));
+ gpr_free(usage);
+
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_help(void) {
+ gpr_cmdline* cl;
+
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+
+ char* help[] = {(char*)__FILE__, const_cast<char*>("-h")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_set_survive_failure(cl);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ nullptr);
+
+ GPR_ASSERT(0 == gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(help), help));
+
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_badargs1(void) {
+ gpr_cmdline* cl;
+
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+
+ char* bad_arg_name[] = {(char*)__FILE__, const_cast<char*>("--y")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_set_survive_failure(cl);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ nullptr);
+
+ GPR_ASSERT(0 ==
+ gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(bad_arg_name), bad_arg_name));
+
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_badargs2(void) {
+ gpr_cmdline* cl;
+
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+
+ char* bad_int_value[] = {(char*)__FILE__, const_cast<char*>("--x"),
+ const_cast<char*>("henry")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_set_survive_failure(cl);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ nullptr);
+
+ GPR_ASSERT(
+ 0 == gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(bad_int_value), bad_int_value));
+
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_badargs3(void) {
+ gpr_cmdline* cl;
+
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+
+ char* bad_bool_value[] = {(char*)__FILE__, const_cast<char*>("--flag=henry")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_set_survive_failure(cl);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ nullptr);
+
+ GPR_ASSERT(0 == gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(bad_bool_value),
+ bad_bool_value));
+
+ gpr_cmdline_destroy(cl);
+}
+
+static void test_badargs4(void) {
+ gpr_cmdline* cl;
+
+ const char* str = nullptr;
+ int x = 0;
+ int flag = 2;
+
+ char* bad_bool_value[] = {(char*)__FILE__, const_cast<char*>("--no-str")};
+
+ LOG_TEST();
+
+ cl = gpr_cmdline_create(nullptr);
+ gpr_cmdline_set_survive_failure(cl);
+ gpr_cmdline_add_string(cl, "str", nullptr, &str);
+ gpr_cmdline_add_int(cl, "x", nullptr, &x);
+ gpr_cmdline_add_flag(cl, "flag", nullptr, &flag);
+ gpr_cmdline_on_extra_arg(cl, "file", "filenames to process", extra_arg_cb,
+ nullptr);
+
+ GPR_ASSERT(0 == gpr_cmdline_parse(cl, GPR_ARRAY_SIZE(bad_bool_value),
+ bad_bool_value));
+
+ gpr_cmdline_destroy(cl);
+}
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ test_simple_int();
+ test_eq_int();
+ test_2dash_int();
+ test_2dash_eq_int();
+ test_simple_string();
+ test_eq_string();
+ test_2dash_string();
+ test_2dash_eq_string();
+ test_flag_on();
+ test_flag_no();
+ test_flag_val_1();
+ test_flag_val_0();
+ test_flag_val_true();
+ test_flag_val_false();
+ test_many();
+ test_extra();
+ test_extra_dashdash();
+ test_usage();
+ test_help();
+ test_badargs1();
+ test_badargs2();
+ test_badargs3();
+ test_badargs4();
+ return 0;
+}
diff --git a/test/core/util/subprocess.h b/test/core/util/subprocess.h
new file mode 100644
index 0000000000..c7fe9af435
--- /dev/null
+++ b/test/core/util/subprocess.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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.
+ *
+ */
+
+#ifndef GRPC_TEST_CORE_UTIL_SUBPROCESS_H
+#define GRPC_TEST_CORE_UTIL_SUBPROCESS_H
+
+#include <grpc/support/port_platform.h>
+
+typedef struct gpr_subprocess gpr_subprocess;
+
+/** .exe on windows, empty on unices */
+const char* gpr_subprocess_binary_extension();
+
+gpr_subprocess* gpr_subprocess_create(int argc, const char** argv);
+/** if subprocess has not been joined, kill it */
+void gpr_subprocess_destroy(gpr_subprocess* p);
+/** returns exit status; can be called at most once */
+int gpr_subprocess_join(gpr_subprocess* p);
+void gpr_subprocess_interrupt(gpr_subprocess* p);
+
+#endif /* GRPC_TEST_CORE_UTIL_SUBPROCESS_H */
diff --git a/test/core/util/subprocess_posix.cc b/test/core/util/subprocess_posix.cc
new file mode 100644
index 0000000000..0f6c99731f
--- /dev/null
+++ b/test/core/util/subprocess_posix.cc
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright 2015 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SUBPROCESS
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "test/core/util/subprocess.h"
+
+struct gpr_subprocess {
+ int pid;
+ bool joined;
+};
+
+const char* gpr_subprocess_binary_extension() { return ""; }
+
+gpr_subprocess* gpr_subprocess_create(int argc, const char** argv) {
+ gpr_subprocess* r;
+ int pid;
+ char** exec_args;
+
+ pid = fork();
+ if (pid == -1) {
+ return nullptr;
+ } else if (pid == 0) {
+ exec_args = (char**)gpr_malloc(((size_t)argc + 1) * sizeof(char*));
+ memcpy(exec_args, argv, (size_t)argc * sizeof(char*));
+ exec_args[argc] = nullptr;
+ execv(exec_args[0], exec_args);
+ /* if we reach here, an error has occurred */
+ gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno));
+ _exit(1);
+ return nullptr;
+ } else {
+ r = (gpr_subprocess*)gpr_zalloc(sizeof(gpr_subprocess));
+ r->pid = pid;
+ return r;
+ }
+}
+
+void gpr_subprocess_destroy(gpr_subprocess* p) {
+ if (!p->joined) {
+ kill(p->pid, SIGKILL);
+ gpr_subprocess_join(p);
+ }
+ gpr_free(p);
+}
+
+int gpr_subprocess_join(gpr_subprocess* p) {
+ int status;
+retry:
+ if (waitpid(p->pid, &status, 0) == -1) {
+ if (errno == EINTR) {
+ goto retry;
+ }
+ gpr_log(GPR_ERROR, "waitpid failed for pid %d: %s", p->pid,
+ strerror(errno));
+ return -1;
+ }
+ p->joined = true;
+ return status;
+}
+
+void gpr_subprocess_interrupt(gpr_subprocess* p) {
+ if (!p->joined) {
+ kill(p->pid, SIGINT);
+ }
+}
+
+#endif /* GPR_POSIX_SUBPROCESS */
diff --git a/test/core/util/subprocess_windows.cc b/test/core/util/subprocess_windows.cc
new file mode 100644
index 0000000000..d3295244ea
--- /dev/null
+++ b/test/core/util/subprocess_windows.cc
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright 2016 gRPC authors.
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_SUBPROCESS
+
+#include <string.h>
+#include <tchar.h>
+#include <windows.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/gpr/string.h"
+#include "src/core/lib/gpr/string_windows.h"
+#include "test/core/util/subprocess.h"
+
+struct gpr_subprocess {
+ PROCESS_INFORMATION pi;
+ int joined;
+ int interrupted;
+};
+
+const char* gpr_subprocess_binary_extension() { return ".exe"; }
+
+gpr_subprocess* gpr_subprocess_create(int argc, const char** argv) {
+ gpr_subprocess* r;
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ char* args = gpr_strjoin_sep(argv, (size_t)argc, " ", NULL);
+ TCHAR* args_tchar;
+
+ args_tchar = gpr_char_to_tchar(args);
+ gpr_free(args);
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ memset(&pi, 0, sizeof(pi));
+
+ if (!CreateProcess(NULL, args_tchar, NULL, NULL, FALSE,
+ CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) {
+ gpr_free(args_tchar);
+ return NULL;
+ }
+ gpr_free(args_tchar);
+
+ r = (gpr_subprocess*)gpr_malloc(sizeof(gpr_subprocess));
+ memset(r, 0, sizeof(*r));
+ r->pi = pi;
+ return r;
+}
+
+void gpr_subprocess_destroy(gpr_subprocess* p) {
+ if (p) {
+ if (!p->joined) {
+ gpr_subprocess_interrupt(p);
+ gpr_subprocess_join(p);
+ }
+ if (p->pi.hProcess) {
+ CloseHandle(p->pi.hProcess);
+ }
+ if (p->pi.hThread) {
+ CloseHandle(p->pi.hThread);
+ }
+ gpr_free(p);
+ }
+}
+
+int gpr_subprocess_join(gpr_subprocess* p) {
+ DWORD dwExitCode;
+ if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) {
+ if (dwExitCode == STILL_ACTIVE) {
+ if (WaitForSingleObject(p->pi.hProcess, INFINITE) == WAIT_OBJECT_0) {
+ p->joined = 1;
+ goto getExitCode;
+ }
+ return -1; // failed to join
+ } else {
+ goto getExitCode;
+ }
+ } else {
+ return -1; // failed to get exit code
+ }
+
+getExitCode:
+ if (p->interrupted) {
+ return 0;
+ }
+ if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) {
+ return (int)dwExitCode;
+ } else {
+ return -1; // failed to get exit code
+ }
+}
+
+void gpr_subprocess_interrupt(gpr_subprocess* p) {
+ DWORD dwExitCode;
+ if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) {
+ if (dwExitCode == STILL_ACTIVE) {
+ gpr_log(GPR_INFO, "sending ctrl-break");
+ GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, p->pi.dwProcessId);
+ p->joined = 1;
+ p->interrupted = 1;
+ }
+ }
+ return;
+}
+
+#endif /* GPR_WINDOWS_SUBPROCESS */