blob: dba9e6c08a57a5708fceb72fb593e3b45c4ba757 [file] [log] [blame]
Mike Frysinger8155d082012-04-06 15:23:18 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Deymoaab50e32014-11-10 19:55:35 -08005#include "update_engine/subprocess.h"
6
Alex Deymo5d527802014-07-18 14:24:13 -07007#include <fcntl.h>
Gilad Arnoldb6c562a2013-07-01 02:19:26 -07008#include <netinet/in.h>
9#include <netinet/ip.h>
10#include <poll.h>
11#include <sys/socket.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000012#include <sys/stat.h>
13#include <sys/types.h>
14#include <unistd.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080015
adlr@google.com3defe6a2009-12-04 20:57:17 +000016#include <string>
17#include <vector>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080018
Alex Deymo60ca1a72015-06-18 18:19:15 -070019#include <base/bind.h>
20#include <base/location.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070021#include <base/strings/string_util.h>
22#include <base/strings/stringprintf.h>
23#include <base/time/time.h>
Alex Deymo60ca1a72015-06-18 18:19:15 -070024#include <chromeos/bind_lambda.h>
25#include <chromeos/message_loops/glib_message_loop.h>
26#include <chromeos/message_loops/message_loop.h>
27#include <chromeos/message_loops/message_loop_utils.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000028#include <glib.h>
29#include <gtest/gtest.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080030
adlr@google.com3defe6a2009-12-04 20:57:17 +000031#include "update_engine/test_utils.h"
32#include "update_engine/utils.h"
33
Gilad Arnold8e3f1262013-01-08 14:59:54 -080034using base::TimeDelta;
Alex Deymo60ca1a72015-06-18 18:19:15 -070035using chromeos::MessageLoop;
adlr@google.com3defe6a2009-12-04 20:57:17 +000036using std::string;
37using std::vector;
38
39namespace chromeos_update_engine {
40
41class SubprocessTest : public ::testing::Test {
42 protected:
Alex Deymo60ca1a72015-06-18 18:19:15 -070043 void SetUp() override {
44 loop_.SetAsCurrent();
45 }
46
47 void TearDown() override {
48 EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
49 }
50
51 // TODO(deymo): Replace this with a FakeMessageLoop. Subprocess uses glib to
52 // asynchronously spawn a process, so we need to run a GlibMessageLoop here.
53 chromeos::GlibMessageLoop loop_;
adlr@google.com3defe6a2009-12-04 20:57:17 +000054};
55
56namespace {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070057int local_server_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +000058
Alex Deymo60ca1a72015-06-18 18:19:15 -070059void Callback(int return_code, const string& output, void* /* unused */) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070060 EXPECT_EQ(1, return_code);
Alex Deymo60ca1a72015-06-18 18:19:15 -070061 MessageLoop::current()->BreakLoop();
adlr@google.com3defe6a2009-12-04 20:57:17 +000062}
63
Alex Deymo60ca1a72015-06-18 18:19:15 -070064void CallbackEcho(int return_code, const string& output, void* /* unused */) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080065 EXPECT_EQ(0, return_code);
66 EXPECT_NE(string::npos, output.find("this is stdout"));
67 EXPECT_NE(string::npos, output.find("this is stderr"));
Alex Deymo60ca1a72015-06-18 18:19:15 -070068 MessageLoop::current()->BreakLoop();
Darin Petkov6f03a3b2010-11-10 14:27:14 -080069}
70
Alex Deymo29b81532015-07-09 11:51:49 -070071void CallbackStdoutOnlyEcho(int return_code,
72 const string& output,
73 void* /* unused */) {
74 EXPECT_EQ(0, return_code);
75 EXPECT_NE(string::npos, output.find("on stdout"));
76 EXPECT_EQ(string::npos, output.find("on stderr"));
77 MessageLoop::current()->BreakLoop();
78}
79
Alex Vakulenkod2779df2014-06-16 13:19:00 -070080} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000081
Alex Deymo60ca1a72015-06-18 18:19:15 -070082TEST_F(SubprocessTest, SimpleTest) {
Alex Deymo29b81532015-07-09 11:51:49 -070083 Subprocess::Get().Exec(vector<string>{"/bin/false"}, Callback, nullptr);
Alex Deymo60ca1a72015-06-18 18:19:15 -070084 loop_.Run();
adlr@google.com3defe6a2009-12-04 20:57:17 +000085}
86
Alex Deymo60ca1a72015-06-18 18:19:15 -070087TEST_F(SubprocessTest, EchoTest) {
Alex Deymo29b81532015-07-09 11:51:49 -070088 Subprocess::Get().Exec(
89 vector<string>{
90 "/bin/sh",
91 "-c",
92 "echo this is stdout; echo this is stderr > /dev/stderr"},
93 CallbackEcho,
94 nullptr);
95 loop_.Run();
96}
97
98TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
99 Subprocess::Get().ExecFlags(
100 vector<string>{"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
101 static_cast<GSpawnFlags>(0),
102 false, // don't redirect stderr
103 CallbackStdoutOnlyEcho,
104 nullptr);
Alex Deymo60ca1a72015-06-18 18:19:15 -0700105 loop_.Run();
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800106}
107
Alex Deymo60ca1a72015-06-18 18:19:15 -0700108TEST_F(SubprocessTest, SynchronousEchoTest) {
109 vector<string> cmd = {
110 "/bin/sh",
111 "-c",
112 "echo -n stdout-here; echo -n stderr-there > /dev/stderr"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700113 int rc = -1;
114 string stdout;
115 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
116 EXPECT_EQ(0, rc);
117 EXPECT_EQ("stdout-herestderr-there", stdout);
118}
119
Alex Deymo60ca1a72015-06-18 18:19:15 -0700120TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
Alex Deymo29b81532015-07-09 11:51:49 -0700121 vector<string> cmd = {"/bin/sh", "-c", "echo test"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700122 int rc = -1;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700123 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
Darin Petkov85d02b72011-05-17 13:25:51 -0700124 EXPECT_EQ(0, rc);
125}
126
adlr@google.com3defe6a2009-12-04 20:57:17 +0000127namespace {
Alex Deymo60ca1a72015-06-18 18:19:15 -0700128void CallbackBad(int return_code, const string& output, void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000129 CHECK(false) << "should never be called.";
130}
131
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700132// TODO(garnold) this test method uses test_http_server as a representative for
133// interactive processes that can be spawned/terminated at will. This causes us
134// to go through hoops when spawning this process (e.g. obtaining the port
135// number it uses so we can control it with wget). It would have been much
136// preferred to use something else and thus simplify both test_http_server
137// (doesn't have to be able to communicate through a temp file) and the test
138// code below; for example, it sounds like a brain dead sleep loop with proper
139// signal handlers could be used instead.
Alex Deymo60ca1a72015-06-18 18:19:15 -0700140void StartAndCancelInRunLoop(bool* spawned) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700141 // Create a temp file for test_http_server to communicate its port number.
142 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
143 int temp_fd = mkstemp(temp_file_name);
144 CHECK_GE(temp_fd, 0);
145 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
146 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
147
adlr@google.com3defe6a2009-12-04 20:57:17 +0000148 vector<string> cmd;
149 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700150 cmd.push_back(temp_file_name);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700151 uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, nullptr);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000152 EXPECT_NE(0, tag);
Alex Deymo60ca1a72015-06-18 18:19:15 -0700153 *spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700154 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000155 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800156 TimeDelta total_wait_time;
157 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
158 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700159 local_server_port = 0;
160 static const char* kServerListeningMsgPrefix = "listening on port ";
161 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
162 char line[80];
163 int line_len = read(temp_fd, line, sizeof(line) - 1);
164 if (line_len > 0) {
165 line[line_len] = '\0';
166 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
167 const char* listening_port_str =
168 line + strlen(kServerListeningMsgPrefix);
169 char* end_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700170 long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
171 &end_ptr, 10);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700172 CHECK(!*end_ptr || *end_ptr == '\n');
173 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000174 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700175 } else if (line_len < 0 && errno != EAGAIN) {
176 LOG(INFO) << "error reading from " << temp_file_name << ": "
177 << strerror(errno);
178 break;
179 }
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800180 g_usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700181 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000182 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700183 close(temp_fd);
184 remove(temp_file_name);
185 CHECK_GT(local_server_port, 0);
186 LOG(INFO) << "server listening on port " << local_server_port;
Alex Deymo29b81532015-07-09 11:51:49 -0700187 Subprocess::Get().KillExec(tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000188}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000189
Alex Deymo60ca1a72015-06-18 18:19:15 -0700190void ExitWhenDone(bool* spawned) {
191 if (*spawned && !Subprocess::Get().SubprocessInFlight()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000192 // tear down the sub process
193 printf("tear down time\n");
Alex Deymo10875d92014-11-10 21:52:57 -0800194 int status = test_utils::System(
Alex Vakulenko75039d72014-03-25 12:36:28 -0700195 base::StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
196 local_server_port));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000197 EXPECT_NE(-1, status) << "system() failed";
198 EXPECT_TRUE(WIFEXITED(status))
199 << "command failed to run or died abnormally";
Alex Deymo60ca1a72015-06-18 18:19:15 -0700200 MessageLoop::current()->BreakLoop();
201 } else {
202 // Re-run this callback again in 10 ms.
203 MessageLoop::current()->PostDelayedTask(
204 FROM_HERE,
205 base::Bind(&ExitWhenDone, spawned),
206 TimeDelta::FromMilliseconds(10));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000207 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000208}
209
Alex Deymo60ca1a72015-06-18 18:19:15 -0700210} // namespace
211
212TEST_F(SubprocessTest, CancelTest) {
213 bool spawned = false;
214 loop_.PostDelayedTask(
215 FROM_HERE,
216 base::Bind(&StartAndCancelInRunLoop, &spawned),
217 TimeDelta::FromMilliseconds(100));
218 loop_.PostDelayedTask(
219 FROM_HERE,
220 base::Bind(&ExitWhenDone, &spawned),
221 TimeDelta::FromMilliseconds(10));
222 loop_.Run();
223 // This test would leak a callback that runs when the child process exits
224 // unless we wait for it to run.
225 chromeos::MessageLoopRunUntil(
226 &loop_,
227 TimeDelta::FromSeconds(10),
228 base::Bind([] {
229 return Subprocess::Get().subprocess_records_.empty();
230 }));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000231}
232
233} // namespace chromeos_update_engine