YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/subprocess-test.cc
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include <stdlib.h>
34
35
#include <string>
36
37
#include <gtest/gtest.h>
38
39
#include "yb/util/subprocess.h"
40
#include "yb/util/test_util.h"
41
42
using std::string;
43
using std::vector;
44
using namespace std::literals;
45
46
namespace yb {
47
48
class SubprocessTest : public YBTest {};
49
50
1
TEST_F(SubprocessTest, TestSimplePipe) {
51
1
  vector<string> argv;
52
1
  argv.push_back("tr");
53
1
  argv.push_back("a-z");
54
1
  argv.push_back("A-Z");
55
1
  Subprocess p("/usr/bin/tr", argv);
56
1
  p.PipeParentStdout();
57
1
  ASSERT_OK(p.Start());
58
59
1
  FILE* out = fdopen(p.ReleaseChildStdinFd(), "w");
60
1
  PCHECK(out);
61
1
  FILE* in = fdopen(p.from_child_stdout_fd(), "r");
62
1
  PCHECK(in);
63
64
1
  fprintf(out, "hello world\n");
65
  // We have to close 'out' or else tr won't write any output, since
66
  // it enters a buffered mode if it detects that its input is a FIFO.
67
1
  fclose(out);
68
69
1
  char buf[1024];
70
1
  ASSERT_EQ(buf, fgets(buf, sizeof(buf), in));
71
1
  ASSERT_STREQ("HELLO WORLD\n", &buf[0]);
72
73
1
  int wait_status = 0;
74
1
  ASSERT_OK(p.Wait(&wait_status));
75
1
  ASSERT_TRUE(WIFEXITED(wait_status));
76
1
  ASSERT_EQ(0, WEXITSTATUS(wait_status));
77
1
}
78
79
1
TEST_F(SubprocessTest, TestErrPipe) {
80
1
  vector<string> argv;
81
1
  argv.push_back("tee");
82
1
  argv.push_back("/dev/stderr");
83
1
  Subprocess p("/usr/bin/tee", argv);
84
1
  p.PipeParentStderr();
85
1
  ASSERT_OK(p.Start());
86
87
1
  FILE* out = fdopen(p.ReleaseChildStdinFd(), "w");
88
1
  PCHECK(out);
89
90
1
  fprintf(out, "Hello, World\n");
91
1
  fclose(out); // same reasoning as above, flush to prevent tee buffering
92
93
1
  FILE* in = fdopen(p.from_child_stderr_fd(), "r");
94
1
  PCHECK(in);
95
96
1
  char buf[1024];
97
1
  ASSERT_EQ(buf, fgets(buf, sizeof(buf), in));
98
1
  ASSERT_STREQ("Hello, World\n", &buf[0]);
99
100
1
  int wait_status = 0;
101
1
  ASSERT_OK(p.Wait(&wait_status));
102
1
  ASSERT_TRUE(WIFEXITED(wait_status));
103
1
  ASSERT_EQ(0, WEXITSTATUS(wait_status));
104
1
}
105
106
1
TEST_F(SubprocessTest, TestOutPipe) {
107
1
  vector<string> argv;
108
1
  argv.push_back("tee");
109
1
  argv.push_back("/dev/stdout");
110
1
  Subprocess p("/usr/bin/tee", argv);
111
1
  p.PipeParentStdout();
112
1
  ASSERT_OK(p.Start());
113
114
1
  FILE* out = fdopen(p.ReleaseChildStdinFd(), "w");
115
1
  PCHECK(out);
116
117
1
  fprintf(out, "Hello, World (stdout)\n");
118
1
  fclose(out); // same reasoning as above, flush to prevent tee buffering
119
120
1
  FILE* in = fdopen(p.from_child_stdout_fd(), "r");
121
1
  PCHECK(in);
122
123
1
  char buf[1024];
124
1
  ASSERT_EQ(buf, fgets(buf, sizeof(buf), in));
125
1
  ASSERT_STREQ("Hello, World (stdout)\n", &buf[0]);
126
127
1
  int wait_status = 0;
128
1
  ASSERT_OK(p.Wait(&wait_status));
129
1
  ASSERT_TRUE(WIFEXITED(wait_status));
130
1
  ASSERT_EQ(0, WEXITSTATUS(wait_status));
131
1
}
132
133
1
TEST_F(SubprocessTest, TestKill) {
134
1
  vector<string> argv;
135
1
  argv.push_back("cat");
136
1
  Subprocess p("/bin/cat", argv);
137
1
  ASSERT_OK(p.Start());
138
139
1
  ASSERT_OK(p.Kill(SIGKILL));
140
141
1
  int wait_status = 0;
142
1
  ASSERT_OK(p.Wait(&wait_status));
143
1
  ASSERT_EQ(SIGKILL, WTERMSIG(wait_status));
144
145
  // Test that calling Wait() a second time returns the same
146
  // cached value instead of trying to wait on some other process
147
  // that was assigned the same pid.
148
1
  wait_status = 0;
149
1
  ASSERT_OK(p.Wait(&wait_status));
150
1
  ASSERT_EQ(SIGKILL, WTERMSIG(wait_status));
151
1
}
152
153
1
TEST_F(SubprocessTest, TestEnvVars) {
154
1
  auto kEnvVarFromParentName = "YB_VAR_FROM_PARENT_PROCESS"s;
155
1
  auto kEnvVarFromParentValue = "value_from_parent_process"s;
156
1
  auto kEnvVarForChildOnlyName = "YB_VAR_SET_FOR_CHILD_ONLY"s;
157
1
  auto kEnvVarForChildOnlyValue = "value_set_for_child_only"s;
158
159
1
  setenv(kEnvVarFromParentName.c_str(), kEnvVarFromParentValue.c_str(), /* replace= */ true);
160
1
  vector<string> argv {"env" };
161
1
  Subprocess p("env", argv);
162
1
  p.PipeParentStdout();
163
1
  p.SetEnv(kEnvVarForChildOnlyName, kEnvVarForChildOnlyValue);
164
1
  ASSERT_OK(p.Start());
165
166
1
  FILE* in = fdopen(p.from_child_stdout_fd(), "r");
167
1
  PCHECK(in);
168
169
1
  char buf[1024];
170
1
  std::map<std::string, std::string> subprocess_env;
171
75
  while (fgets(buf, sizeof(buf), in) != nullptr) {
172
74
    const char* equal_sign_ptr = strchr(buf, '=');
173
74
    if (equal_sign_ptr != nullptr) {
174
74
      size_t name_length = equal_sign_ptr - buf;
175
74
      subprocess_env[std::string(buf, name_length)] =
176
74
          std::string(equal_sign_ptr + 1, strlen(buf) - name_length - 1);
177
74
    }
178
74
  }
179
180
1
  auto it = subprocess_env.find(kEnvVarFromParentName);
181
1
  ASSERT_TRUE(it != subprocess_env.end());
182
1
  ASSERT_EQ(kEnvVarFromParentValue + "\n", it->second);
183
184
1
  it = subprocess_env.find(kEnvVarForChildOnlyName);
185
1
  ASSERT_TRUE(it != subprocess_env.end());
186
1
  ASSERT_EQ(kEnvVarForChildOnlyValue + "\n", it->second);
187
188
1
  int wait_status = 0;
189
1
  ASSERT_OK(p.Wait(&wait_status));
190
1
  ASSERT_TRUE(WIFEXITED(wait_status));
191
1
  ASSERT_EQ(0, WEXITSTATUS(wait_status));
192
1
}
193
194
} // namespace yb