blob: cb295e1fc68d9938e587dd4783c07ab814caff5f [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
Gilad Arnoldb6c562a2013-07-01 02:19:26 -07005#include <netinet/in.h>
6#include <netinet/ip.h>
7#include <poll.h>
8#include <sys/socket.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +00009#include <sys/stat.h>
10#include <sys/types.h>
11#include <unistd.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080012
adlr@google.com3defe6a2009-12-04 20:57:17 +000013#include <string>
14#include <vector>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080015
16#include <base/string_util.h>
Mike Frysinger8155d082012-04-06 15:23:18 -040017#include <base/stringprintf.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080018#include <base/time.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000019#include <glib.h>
20#include <gtest/gtest.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080021
adlr@google.com3defe6a2009-12-04 20:57:17 +000022#include "update_engine/subprocess.h"
23#include "update_engine/test_utils.h"
24#include "update_engine/utils.h"
25
Gilad Arnold8e3f1262013-01-08 14:59:54 -080026using base::TimeDelta;
adlr@google.com3defe6a2009-12-04 20:57:17 +000027using std::string;
28using std::vector;
29
30namespace chromeos_update_engine {
31
32class SubprocessTest : public ::testing::Test {
33 protected:
34 bool callback_done;
35};
36
37namespace {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070038int local_server_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +000039
Darin Petkov6f03a3b2010-11-10 14:27:14 -080040void Callback(int return_code, const string& output, void *p) {
Andrew de los Reyesc1d5c932011-04-20 17:15:47 -070041 EXPECT_EQ(1, return_code);
adlr@google.com3defe6a2009-12-04 20:57:17 +000042 GMainLoop* loop = reinterpret_cast<GMainLoop*>(p);
43 g_main_loop_quit(loop);
44}
45
Darin Petkov6f03a3b2010-11-10 14:27:14 -080046void CallbackEcho(int return_code, const string& output, void *p) {
47 EXPECT_EQ(0, return_code);
48 EXPECT_NE(string::npos, output.find("this is stdout"));
49 EXPECT_NE(string::npos, output.find("this is stderr"));
50 GMainLoop* loop = reinterpret_cast<GMainLoop*>(p);
51 g_main_loop_quit(loop);
52}
53
adlr@google.com3defe6a2009-12-04 20:57:17 +000054gboolean LaunchFalseInMainLoop(gpointer data) {
55 vector<string> cmd;
56 cmd.push_back("/bin/false");
57 Subprocess::Get().Exec(cmd, Callback, data);
58 return FALSE;
59}
Darin Petkov6f03a3b2010-11-10 14:27:14 -080060
61gboolean LaunchEchoInMainLoop(gpointer data) {
62 vector<string> cmd;
63 cmd.push_back("/bin/sh");
64 cmd.push_back("-c");
65 cmd.push_back("echo this is stdout; echo this is stderr > /dev/stderr");
66 Subprocess::Get().Exec(cmd, CallbackEcho, data);
67 return FALSE;
68}
adlr@google.com3defe6a2009-12-04 20:57:17 +000069} // namespace {}
70
71TEST(SubprocessTest, SimpleTest) {
72 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
73 g_timeout_add(0, &LaunchFalseInMainLoop, loop);
74 g_main_loop_run(loop);
75 g_main_loop_unref(loop);
76}
77
Darin Petkov6f03a3b2010-11-10 14:27:14 -080078TEST(SubprocessTest, EchoTest) {
79 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
80 g_timeout_add(0, &LaunchEchoInMainLoop, loop);
81 g_main_loop_run(loop);
82 g_main_loop_unref(loop);
83}
84
Darin Petkov85d02b72011-05-17 13:25:51 -070085TEST(SubprocessTest, SynchronousEchoTest) {
86 vector<string> cmd;
87 cmd.push_back("/bin/sh");
88 cmd.push_back("-c");
89 cmd.push_back("echo -n stdout-here; echo -n stderr-there > /dev/stderr");
90 int rc = -1;
91 string stdout;
92 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
93 EXPECT_EQ(0, rc);
94 EXPECT_EQ("stdout-herestderr-there", stdout);
95}
96
97TEST(SubprocessTest, SynchronousEchoNoOutputTest) {
98 vector<string> cmd;
99 cmd.push_back("/bin/sh");
100 cmd.push_back("-c");
101 cmd.push_back("echo test");
102 int rc = -1;
103 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, NULL));
104 EXPECT_EQ(0, rc);
105}
106
adlr@google.com3defe6a2009-12-04 20:57:17 +0000107namespace {
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800108void CallbackBad(int return_code, const string& output, void *p) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000109 CHECK(false) << "should never be called.";
110}
111
112struct CancelTestData {
113 bool spawned;
114 GMainLoop *loop;
115};
116
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700117// TODO(garnold) this test method uses test_http_server as a representative for
118// interactive processes that can be spawned/terminated at will. This causes us
119// to go through hoops when spawning this process (e.g. obtaining the port
120// number it uses so we can control it with wget). It would have been much
121// preferred to use something else and thus simplify both test_http_server
122// (doesn't have to be able to communicate through a temp file) and the test
123// code below; for example, it sounds like a brain dead sleep loop with proper
124// signal handlers could be used instead.
adlr@google.com3defe6a2009-12-04 20:57:17 +0000125gboolean StartAndCancelInRunLoop(gpointer data) {
126 CancelTestData* cancel_test_data = reinterpret_cast<CancelTestData*>(data);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700127
128 // Create a temp file for test_http_server to communicate its port number.
129 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
130 int temp_fd = mkstemp(temp_file_name);
131 CHECK_GE(temp_fd, 0);
132 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
133 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
134
adlr@google.com3defe6a2009-12-04 20:57:17 +0000135 vector<string> cmd;
136 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700137 cmd.push_back(temp_file_name);
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700138 uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000139 EXPECT_NE(0, tag);
140 cancel_test_data->spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700141 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000142 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800143 TimeDelta total_wait_time;
144 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
145 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700146 local_server_port = 0;
147 static const char* kServerListeningMsgPrefix = "listening on port ";
148 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
149 char line[80];
150 int line_len = read(temp_fd, line, sizeof(line) - 1);
151 if (line_len > 0) {
152 line[line_len] = '\0';
153 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
154 const char* listening_port_str =
155 line + strlen(kServerListeningMsgPrefix);
156 char* end_ptr;
157 long raw_port = strtol(listening_port_str, &end_ptr, 10);
158 CHECK(!*end_ptr || *end_ptr == '\n');
159 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000160 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700161 } else if (line_len < 0 && errno != EAGAIN) {
162 LOG(INFO) << "error reading from " << temp_file_name << ": "
163 << strerror(errno);
164 break;
165 }
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800166 g_usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700167 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000168 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700169 close(temp_fd);
170 remove(temp_file_name);
171 CHECK_GT(local_server_port, 0);
172 LOG(INFO) << "server listening on port " << local_server_port;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000173 Subprocess::Get().CancelExec(tag);
174 return FALSE;
175}
176} // namespace {}
177
178gboolean ExitWhenDone(gpointer data) {
179 CancelTestData* cancel_test_data = reinterpret_cast<CancelTestData*>(data);
180 if (cancel_test_data->spawned && !Subprocess::Get().SubprocessInFlight()) {
181 // tear down the sub process
182 printf("tear down time\n");
Darin Petkovb7de1d52010-08-24 13:38:33 -0700183 int status = System(
184 StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700185 local_server_port));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000186 EXPECT_NE(-1, status) << "system() failed";
187 EXPECT_TRUE(WIFEXITED(status))
188 << "command failed to run or died abnormally";
189 g_main_loop_quit(cancel_test_data->loop);
190 return FALSE;
191 }
192 return TRUE;
193}
194
195TEST(SubprocessTest, CancelTest) {
196 GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
197 CancelTestData cancel_test_data;
198 cancel_test_data.spawned = false;
199 cancel_test_data.loop = loop;
200 g_timeout_add(100, &StartAndCancelInRunLoop, &cancel_test_data);
201 g_timeout_add(10, &ExitWhenDone, &cancel_test_data);
202 g_main_loop_run(loop);
203 g_main_loop_unref(loop);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000204}
205
206} // namespace chromeos_update_engine