blob: 37dea0501259dd348de01c5c57a46aabdab23ba4 [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 Vakulenkod2779df2014-06-16 13:19:00 -070071} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000072
Alex Deymo60ca1a72015-06-18 18:19:15 -070073TEST_F(SubprocessTest, SimpleTest) {
74 loop_.PostTask(
75 FROM_HERE,
76 base::Bind([] {
77 Subprocess::Get().Exec(vector<string>{"/bin/false"}, Callback, nullptr);
78 }));
79 loop_.Run();
adlr@google.com3defe6a2009-12-04 20:57:17 +000080}
81
Alex Deymo60ca1a72015-06-18 18:19:15 -070082TEST_F(SubprocessTest, EchoTest) {
83 loop_.PostTask(
84 FROM_HERE,
85 base::Bind([] {
86 Subprocess::Get().Exec(
87 vector<string>{
88 "/bin/sh",
89 "-c",
90 "echo this is stdout; echo this is stderr > /dev/stderr"},
91 CallbackEcho,
92 nullptr);
93 }));
94 loop_.Run();
Darin Petkov6f03a3b2010-11-10 14:27:14 -080095}
96
Alex Deymo60ca1a72015-06-18 18:19:15 -070097TEST_F(SubprocessTest, SynchronousEchoTest) {
98 vector<string> cmd = {
99 "/bin/sh",
100 "-c",
101 "echo -n stdout-here; echo -n stderr-there > /dev/stderr"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700102 int rc = -1;
103 string stdout;
104 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
105 EXPECT_EQ(0, rc);
106 EXPECT_EQ("stdout-herestderr-there", stdout);
107}
108
Alex Deymo60ca1a72015-06-18 18:19:15 -0700109TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
110 vector<string> cmd = {
111 "/bin/sh",
112 "-c",
113 "echo test"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700114 int rc = -1;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700115 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
Darin Petkov85d02b72011-05-17 13:25:51 -0700116 EXPECT_EQ(0, rc);
117}
118
adlr@google.com3defe6a2009-12-04 20:57:17 +0000119namespace {
Alex Deymo60ca1a72015-06-18 18:19:15 -0700120void CallbackBad(int return_code, const string& output, void* p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000121 CHECK(false) << "should never be called.";
122}
123
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700124// TODO(garnold) this test method uses test_http_server as a representative for
125// interactive processes that can be spawned/terminated at will. This causes us
126// to go through hoops when spawning this process (e.g. obtaining the port
127// number it uses so we can control it with wget). It would have been much
128// preferred to use something else and thus simplify both test_http_server
129// (doesn't have to be able to communicate through a temp file) and the test
130// code below; for example, it sounds like a brain dead sleep loop with proper
131// signal handlers could be used instead.
Alex Deymo60ca1a72015-06-18 18:19:15 -0700132void StartAndCancelInRunLoop(bool* spawned) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700133 // Create a temp file for test_http_server to communicate its port number.
134 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
135 int temp_fd = mkstemp(temp_file_name);
136 CHECK_GE(temp_fd, 0);
137 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
138 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
139
adlr@google.com3defe6a2009-12-04 20:57:17 +0000140 vector<string> cmd;
141 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700142 cmd.push_back(temp_file_name);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700143 uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, nullptr);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000144 EXPECT_NE(0, tag);
Alex Deymo60ca1a72015-06-18 18:19:15 -0700145 *spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700146 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000147 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800148 TimeDelta total_wait_time;
149 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
150 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700151 local_server_port = 0;
152 static const char* kServerListeningMsgPrefix = "listening on port ";
153 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
154 char line[80];
155 int line_len = read(temp_fd, line, sizeof(line) - 1);
156 if (line_len > 0) {
157 line[line_len] = '\0';
158 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
159 const char* listening_port_str =
160 line + strlen(kServerListeningMsgPrefix);
161 char* end_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700162 long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
163 &end_ptr, 10);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700164 CHECK(!*end_ptr || *end_ptr == '\n');
165 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000166 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700167 } else if (line_len < 0 && errno != EAGAIN) {
168 LOG(INFO) << "error reading from " << temp_file_name << ": "
169 << strerror(errno);
170 break;
171 }
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800172 g_usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700173 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000174 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700175 close(temp_fd);
176 remove(temp_file_name);
177 CHECK_GT(local_server_port, 0);
178 LOG(INFO) << "server listening on port " << local_server_port;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000179 Subprocess::Get().CancelExec(tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000180}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000181
Alex Deymo60ca1a72015-06-18 18:19:15 -0700182void ExitWhenDone(bool* spawned) {
183 if (*spawned && !Subprocess::Get().SubprocessInFlight()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000184 // tear down the sub process
185 printf("tear down time\n");
Alex Deymo10875d92014-11-10 21:52:57 -0800186 int status = test_utils::System(
Alex Vakulenko75039d72014-03-25 12:36:28 -0700187 base::StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
188 local_server_port));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000189 EXPECT_NE(-1, status) << "system() failed";
190 EXPECT_TRUE(WIFEXITED(status))
191 << "command failed to run or died abnormally";
Alex Deymo60ca1a72015-06-18 18:19:15 -0700192 MessageLoop::current()->BreakLoop();
193 } else {
194 // Re-run this callback again in 10 ms.
195 MessageLoop::current()->PostDelayedTask(
196 FROM_HERE,
197 base::Bind(&ExitWhenDone, spawned),
198 TimeDelta::FromMilliseconds(10));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000199 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000200}
201
Alex Deymo60ca1a72015-06-18 18:19:15 -0700202} // namespace
203
204TEST_F(SubprocessTest, CancelTest) {
205 bool spawned = false;
206 loop_.PostDelayedTask(
207 FROM_HERE,
208 base::Bind(&StartAndCancelInRunLoop, &spawned),
209 TimeDelta::FromMilliseconds(100));
210 loop_.PostDelayedTask(
211 FROM_HERE,
212 base::Bind(&ExitWhenDone, &spawned),
213 TimeDelta::FromMilliseconds(10));
214 loop_.Run();
215 // This test would leak a callback that runs when the child process exits
216 // unless we wait for it to run.
217 chromeos::MessageLoopRunUntil(
218 &loop_,
219 TimeDelta::FromSeconds(10),
220 base::Bind([] {
221 return Subprocess::Get().subprocess_records_.empty();
222 }));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000223}
224
225} // namespace chromeos_update_engine