blob: 4e7ebf6157a407b13c631724a2971cabaa897470 [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 Vakulenko75039d72014-03-25 12:36:28 -070019#include <base/strings/string_util.h>
20#include <base/strings/stringprintf.h>
21#include <base/time/time.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000022#include <glib.h>
23#include <gtest/gtest.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080024
adlr@google.com3defe6a2009-12-04 20:57:17 +000025#include "update_engine/test_utils.h"
26#include "update_engine/utils.h"
27
Gilad Arnold8e3f1262013-01-08 14:59:54 -080028using base::TimeDelta;
adlr@google.com3defe6a2009-12-04 20:57:17 +000029using std::string;
30using std::vector;
31
32namespace chromeos_update_engine {
33
34class SubprocessTest : public ::testing::Test {
35 protected:
36 bool callback_done;
37};
38
39namespace {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070040int local_server_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +000041
Darin Petkov6f03a3b2010-11-10 14:27:14 -080042void Callback(int return_code, const string& output, void *p) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070043 EXPECT_EQ(1, return_code);
adlr@google.com3defe6a2009-12-04 20:57:17 +000044 GMainLoop* loop = reinterpret_cast<GMainLoop*>(p);
45 g_main_loop_quit(loop);
46}
47
Darin Petkov6f03a3b2010-11-10 14:27:14 -080048void CallbackEcho(int return_code, const string& output, void *p) {
49 EXPECT_EQ(0, return_code);
50 EXPECT_NE(string::npos, output.find("this is stdout"));
51 EXPECT_NE(string::npos, output.find("this is stderr"));
52 GMainLoop* loop = reinterpret_cast<GMainLoop*>(p);
53 g_main_loop_quit(loop);
54}
55
adlr@google.com3defe6a2009-12-04 20:57:17 +000056gboolean LaunchFalseInMainLoop(gpointer data) {
57 vector<string> cmd;
58 cmd.push_back("/bin/false");
59 Subprocess::Get().Exec(cmd, Callback, data);
60 return FALSE;
61}
Darin Petkov6f03a3b2010-11-10 14:27:14 -080062
63gboolean LaunchEchoInMainLoop(gpointer data) {
64 vector<string> cmd;
65 cmd.push_back("/bin/sh");
66 cmd.push_back("-c");
67 cmd.push_back("echo this is stdout; echo this is stderr > /dev/stderr");
68 Subprocess::Get().Exec(cmd, CallbackEcho, data);
69 return FALSE;
70}
Alex Vakulenkod2779df2014-06-16 13:19:00 -070071} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000072
73TEST(SubprocessTest, SimpleTest) {
74 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
75 g_timeout_add(0, &LaunchFalseInMainLoop, loop);
76 g_main_loop_run(loop);
77 g_main_loop_unref(loop);
78}
79
Darin Petkov6f03a3b2010-11-10 14:27:14 -080080TEST(SubprocessTest, EchoTest) {
81 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
82 g_timeout_add(0, &LaunchEchoInMainLoop, loop);
83 g_main_loop_run(loop);
84 g_main_loop_unref(loop);
85}
86
Darin Petkov85d02b72011-05-17 13:25:51 -070087TEST(SubprocessTest, SynchronousEchoTest) {
88 vector<string> cmd;
89 cmd.push_back("/bin/sh");
90 cmd.push_back("-c");
91 cmd.push_back("echo -n stdout-here; echo -n stderr-there > /dev/stderr");
92 int rc = -1;
93 string stdout;
94 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
95 EXPECT_EQ(0, rc);
96 EXPECT_EQ("stdout-herestderr-there", stdout);
97}
98
99TEST(SubprocessTest, SynchronousEchoNoOutputTest) {
100 vector<string> cmd;
101 cmd.push_back("/bin/sh");
102 cmd.push_back("-c");
103 cmd.push_back("echo test");
104 int rc = -1;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700105 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
Darin Petkov85d02b72011-05-17 13:25:51 -0700106 EXPECT_EQ(0, rc);
107}
108
adlr@google.com3defe6a2009-12-04 20:57:17 +0000109namespace {
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800110void CallbackBad(int return_code, const string& output, void *p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000111 CHECK(false) << "should never be called.";
112}
113
114struct CancelTestData {
115 bool spawned;
116 GMainLoop *loop;
117};
118
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700119// TODO(garnold) this test method uses test_http_server as a representative for
120// interactive processes that can be spawned/terminated at will. This causes us
121// to go through hoops when spawning this process (e.g. obtaining the port
122// number it uses so we can control it with wget). It would have been much
123// preferred to use something else and thus simplify both test_http_server
124// (doesn't have to be able to communicate through a temp file) and the test
125// code below; for example, it sounds like a brain dead sleep loop with proper
126// signal handlers could be used instead.
adlr@google.com3defe6a2009-12-04 20:57:17 +0000127gboolean StartAndCancelInRunLoop(gpointer data) {
128 CancelTestData* cancel_test_data = reinterpret_cast<CancelTestData*>(data);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700129
130 // Create a temp file for test_http_server to communicate its port number.
131 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
132 int temp_fd = mkstemp(temp_file_name);
133 CHECK_GE(temp_fd, 0);
134 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
135 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
136
adlr@google.com3defe6a2009-12-04 20:57:17 +0000137 vector<string> cmd;
138 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700139 cmd.push_back(temp_file_name);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700140 uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, nullptr);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000141 EXPECT_NE(0, tag);
142 cancel_test_data->spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700143 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000144 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800145 TimeDelta total_wait_time;
146 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
147 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700148 local_server_port = 0;
149 static const char* kServerListeningMsgPrefix = "listening on port ";
150 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
151 char line[80];
152 int line_len = read(temp_fd, line, sizeof(line) - 1);
153 if (line_len > 0) {
154 line[line_len] = '\0';
155 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
156 const char* listening_port_str =
157 line + strlen(kServerListeningMsgPrefix);
158 char* end_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700159 long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
160 &end_ptr, 10);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700161 CHECK(!*end_ptr || *end_ptr == '\n');
162 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000163 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700164 } else if (line_len < 0 && errno != EAGAIN) {
165 LOG(INFO) << "error reading from " << temp_file_name << ": "
166 << strerror(errno);
167 break;
168 }
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800169 g_usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700170 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000171 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700172 close(temp_fd);
173 remove(temp_file_name);
174 CHECK_GT(local_server_port, 0);
175 LOG(INFO) << "server listening on port " << local_server_port;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000176 Subprocess::Get().CancelExec(tag);
177 return FALSE;
178}
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700179} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +0000180
181gboolean ExitWhenDone(gpointer data) {
182 CancelTestData* cancel_test_data = reinterpret_cast<CancelTestData*>(data);
183 if (cancel_test_data->spawned && !Subprocess::Get().SubprocessInFlight()) {
184 // 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";
192 g_main_loop_quit(cancel_test_data->loop);
193 return FALSE;
194 }
195 return TRUE;
196}
197
198TEST(SubprocessTest, CancelTest) {
199 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
200 CancelTestData cancel_test_data;
201 cancel_test_data.spawned = false;
202 cancel_test_data.loop = loop;
203 g_timeout_add(100, &StartAndCancelInRunLoop, &cancel_test_data);
204 g_timeout_add(10, &ExitWhenDone, &cancel_test_data);
205 g_main_loop_run(loop);
206 g_main_loop_unref(loop);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000207}
208
209} // namespace chromeos_update_engine