[go: nahoru, domu]

blob: 561167da94c0c610f975ed85fcefbf87f92d7534 [file] [log] [blame]
tbarzic@chromium.orga504ba72012-01-25 01:42:571// Copyright (c) 2012 The Chromium Authors. All rights reserved.
tbarzic@chromium.org8f427982011-12-13 23:40:232// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
dcheng0a6e80c2016-04-08 18:37:385#include "chromeos/process_proxy/process_output_watcher.h"
6
tbarzic@chromium.org8f427982011-12-13 23:40:237#include <gtest/gtest.h>
avi6e1a22d2015-12-21 03:43:208#include <stddef.h>
tbarzic@chromium.org8f427982011-12-13 23:40:239
dcheng0a6e80c2016-04-08 18:37:3810#include <memory>
tbarzic@chromium.org8f427982011-12-13 23:40:2311#include <queue>
12#include <string>
13#include <vector>
14
tbarzic@chromium.org8f427982011-12-13 23:40:2315#include "base/bind.h"
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3716#include "base/callback.h"
thestigb44bd352014-09-10 01:47:0617#include "base/files/file_util.h"
skyostil1472c842015-06-11 18:40:2418#include "base/location.h"
gabf64a25e2017-05-12 19:42:5619#include "base/message_loop/message_loop.h"
brettw@chromium.org2025d002012-11-14 20:54:3520#include "base/posix/eintr_wrapper.h"
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3721#include "base/run_loop.h"
skyostil1472c842015-06-11 18:40:2422#include "base/single_thread_task_runner.h"
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3723#include "base/strings/string_util.h"
tbarzic@chromium.org8f427982011-12-13 23:40:2324#include "base/threading/thread.h"
phajdan.jr@chromium.org3a01162b2013-03-12 20:22:4825
26namespace chromeos {
tbarzic@chromium.org8f427982011-12-13 23:40:2327
tbarzic6ca50552015-08-10 22:32:4528namespace {
29
tbarzic@chromium.org8f427982011-12-13 23:40:2330struct TestCase {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3731 TestCase(const std::string& input, bool send_terminating_null)
32 : input(input),
33 should_send_terminating_null(send_terminating_null),
34 expected_output(input) {}
tbarzic@chromium.org8f427982011-12-13 23:40:2335
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3736 // Conctructor for cases where the output is not expected to be the same as
37 // input.
38 TestCase(const std::string& input,
39 bool send_terminating_null,
40 const std::string& expected_output)
41 : input(input),
42 should_send_terminating_null(send_terminating_null),
43 expected_output(expected_output) {}
44
45 std::string input;
46 bool should_send_terminating_null;
47 std::string expected_output;
tbarzic@chromium.org8f427982011-12-13 23:40:2348};
49
50class ProcessWatcherExpectations {
51 public:
Chris Watkins2c529d62017-11-29 02:14:4152 ProcessWatcherExpectations() = default;
tbarzic@chromium.org8f427982011-12-13 23:40:2353
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3754 void SetTestCase(const TestCase& test_case) {
tbarzic@chromium.org8f427982011-12-13 23:40:2355 received_from_out_ = 0;
tbarzic@chromium.org8f427982011-12-13 23:40:2356
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3757 out_expectations_ = test_case.expected_output;
58 if (test_case.should_send_terminating_null)
59 out_expectations_.append(std::string("", 1));
tbarzic@chromium.org8f427982011-12-13 23:40:2360 }
61
tbarzic@chromium.org6866c6b2012-04-09 21:38:1862 bool CheckExpectations(const std::string& data, ProcessOutputType type) {
63 EXPECT_EQ(PROCESS_OUTPUT_TYPE_OUT, type);
thakis@chromium.orge2524b02013-07-22 17:51:2864 if (type != PROCESS_OUTPUT_TYPE_OUT)
tbarzic@chromium.org6866c6b2012-04-09 21:38:1865 return false;
tbarzic@chromium.org8f427982011-12-13 23:40:2366
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3767 if (out_expectations_.length() == 0 && data.length() == 0)
68 return true;
69
tbarzic@chromium.org6866c6b2012-04-09 21:38:1870 EXPECT_LT(received_from_out_, out_expectations_.length());
71 if (received_from_out_ >= out_expectations_.length())
72 return false;
tbarzic@chromium.org8f427982011-12-13 23:40:2373
tbarzic@chromium.org6866c6b2012-04-09 21:38:1874 EXPECT_EQ(received_from_out_,
75 out_expectations_.find(data, received_from_out_));
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3776 if (received_from_out_ != out_expectations_.find(data, received_from_out_))
77 return false;
tbarzic@chromium.org8f427982011-12-13 23:40:2378
tbarzic@chromium.org6866c6b2012-04-09 21:38:1879 received_from_out_ += data.length();
80 return true;
tbarzic@chromium.org8f427982011-12-13 23:40:2381 }
82
83 bool IsDone() {
tbarzic@chromium.org6866c6b2012-04-09 21:38:1884 return received_from_out_ >= out_expectations_.length();
tbarzic@chromium.org8f427982011-12-13 23:40:2385 }
86
87 private:
88 std::string out_expectations_;
89 size_t received_from_out_;
tbarzic@chromium.org8f427982011-12-13 23:40:2390};
91
dcheng0a6e80c2016-04-08 18:37:3892void StopProcessOutputWatcher(std::unique_ptr<ProcessOutputWatcher> watcher) {
tbarzic6ca50552015-08-10 22:32:4593 // Just deleting |watcher| if sufficient.
94}
95
96} // namespace
97
tbarzic@chromium.org8f427982011-12-13 23:40:2398class ProcessOutputWatcherTest : public testing::Test {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:3799 public:
100 ProcessOutputWatcherTest() : output_watch_thread_started_(false),
101 failed_(false) {
102 }
tbarzic@chromium.org8f427982011-12-13 23:40:23103
Chris Watkins2c529d62017-11-29 02:14:41104 ~ProcessOutputWatcherTest() override = default;
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37105
dchengae98daa2015-01-21 20:30:49106 void TearDown() override {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37107 if (output_watch_thread_started_)
108 output_watch_thread_->Stop();
109 }
110
tbarzic170f12c2016-01-08 05:15:29111 void OnRead(ProcessOutputType type,
112 const std::string& output,
113 const base::Closure& ack_callback) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37114 ASSERT_FALSE(failed_);
tbarzicdaf936a2015-07-29 21:05:14115 // There may be an EXIT signal sent during test tear down (which is sent
116 // by process output watcher when master end of test pseudo-terminal is
117 // closed). If this happens, ignore it. If EXIT is seen before test
118 // expectations are met, fall through in order to fail the test.
119 if (type == PROCESS_OUTPUT_TYPE_EXIT && expectations_.IsDone()) {
120 ASSERT_TRUE(test_case_done_callback_.is_null());
121 return;
122 }
123
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37124 failed_ = !expectations_.CheckExpectations(output, type);
125 if (failed_ || expectations_.IsDone()) {
126 ASSERT_FALSE(test_case_done_callback_.is_null());
skyostil1472c842015-06-11 18:40:24127 message_loop_.task_runner()->PostTask(FROM_HERE,
128 test_case_done_callback_);
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37129 test_case_done_callback_.Reset();
130 }
tbarzic170f12c2016-01-08 05:15:29131
132 ASSERT_FALSE(ack_callback.is_null());
133 message_loop_.task_runner()->PostTask(FROM_HERE, ack_callback);
tbarzic@chromium.org8f427982011-12-13 23:40:23134 }
135
136 protected:
137 std::string VeryLongString() {
138 std::string result = "0123456789";
139 for (int i = 0; i < 8; i++)
140 result = result.append(result);
141 return result;
142 }
143
tbarzic@chromium.org250f7f892012-04-11 02:46:43144 void RunTest(const std::vector<TestCase>& test_cases) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37145 ASSERT_FALSE(output_watch_thread_started_);
146 output_watch_thread_.reset(new base::Thread("ProcessOutpuWatchThread"));
tbarzic6ca50552015-08-10 22:32:45147 output_watch_thread_started_ = output_watch_thread_->StartWithOptions(
148 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37149 ASSERT_TRUE(output_watch_thread_started_);
tbarzic@chromium.org250f7f892012-04-11 02:46:43150
tbarzic6ca50552015-08-10 22:32:45151 int pt_pipe[2];
tbarzic@chromium.org250f7f892012-04-11 02:46:43152 ASSERT_FALSE(HANDLE_EINTR(pipe(pt_pipe)));
tbarzic6ca50552015-08-10 22:32:45153
dcheng0a6e80c2016-04-08 18:37:38154 std::unique_ptr<ProcessOutputWatcher> crosh_watcher(
155 new ProcessOutputWatcher(pt_pipe[0],
156 base::Bind(&ProcessOutputWatcherTest::OnRead,
157 base::Unretained(this))));
tbarzic@chromium.org250f7f892012-04-11 02:46:43158
skyostil1472c842015-06-11 18:40:24159 output_watch_thread_->task_runner()->PostTask(
tzikc8fdd6c32018-04-11 04:02:40160 FROM_HERE, base::BindOnce(&ProcessOutputWatcher::Start,
161 base::Unretained(crosh_watcher.get())));
tbarzic@chromium.org250f7f892012-04-11 02:46:43162
163 for (size_t i = 0; i < test_cases.size(); i++) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37164 expectations_.SetTestCase(test_cases[i]);
165
166 base::RunLoop run_loop;
167 ASSERT_TRUE(test_case_done_callback_.is_null());
168 test_case_done_callback_ = run_loop.QuitClosure();
169
170 const std::string& test_str = test_cases[i].input;
tbarzic@chromium.org250f7f892012-04-11 02:46:43171 // Let's make inputs not NULL terminated, unless other is specified in
172 // the test case.
173 ssize_t test_size = test_str.length() * sizeof(*test_str.c_str());
174 if (test_cases[i].should_send_terminating_null)
175 test_size += sizeof(*test_str.c_str());
chirantan75ea2fd2014-10-07 23:15:30176 EXPECT_TRUE(base::WriteFileDescriptor(pt_pipe[1], test_str.c_str(),
177 test_size));
tbarzic@chromium.org250f7f892012-04-11 02:46:43178
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37179 run_loop.Run();
180 EXPECT_TRUE(expectations_.IsDone());
181 if (failed_)
182 break;
183 }
tbarzic@chromium.org250f7f892012-04-11 02:46:43184
tbarzic6ca50552015-08-10 22:32:45185 output_watch_thread_->task_runner()->PostTask(
186 FROM_HERE,
tzikc8fdd6c32018-04-11 04:02:40187 base::BindOnce(&StopProcessOutputWatcher, std::move(crosh_watcher)));
tbarzic@chromium.org250f7f892012-04-11 02:46:43188
mark@chromium.orgd89eec82013-12-03 14:10:59189 EXPECT_NE(-1, IGNORE_EINTR(close(pt_pipe[1])));
tbarzic@chromium.org250f7f892012-04-11 02:46:43190 }
191
tbarzic@chromium.org8f427982011-12-13 23:40:23192 private:
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37193 base::Closure test_case_done_callback_;
194 base::MessageLoop message_loop_;
dcheng0a6e80c2016-04-08 18:37:38195 std::unique_ptr<base::Thread> output_watch_thread_;
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37196 bool output_watch_thread_started_;
197 bool failed_;
tbarzic@chromium.org8f427982011-12-13 23:40:23198 ProcessWatcherExpectations expectations_;
199 std::vector<TestCase> exp;
200};
201
tbarzicdaf936a2015-07-29 21:05:14202TEST_F(ProcessOutputWatcherTest, OutputWatcher) {
tbarzic@chromium.org8f427982011-12-13 23:40:23203 std::vector<TestCase> test_cases;
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37204 test_cases.push_back(TestCase("t", false));
tbarzic@chromium.org250f7f892012-04-11 02:46:43205 test_cases.push_back(TestCase("testing output\n", false));
206 test_cases.push_back(TestCase("testing error\n", false));
207 test_cases.push_back(TestCase("testing error1\n", false));
208 test_cases.push_back(TestCase("testing output1\n", false));
209 test_cases.push_back(TestCase("testing output2\n", false));
210 test_cases.push_back(TestCase("testing output3\n", false));
211 test_cases.push_back(TestCase(VeryLongString(), false));
212 test_cases.push_back(TestCase("testing error2\n", false));
tbarzic@chromium.org8f427982011-12-13 23:40:23213
tbarzic@chromium.org250f7f892012-04-11 02:46:43214 RunTest(test_cases);
thestigb44bd352014-09-10 01:47:06215}
tbarzic@chromium.org8f427982011-12-13 23:40:23216
tbarzicdaf936a2015-07-29 21:05:14217TEST_F(ProcessOutputWatcherTest, SplitUTF8Character) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37218 std::vector<TestCase> test_cases;
219 test_cases.push_back(TestCase("test1\xc2", false, "test1"));
220 test_cases.push_back(TestCase("\xb5test1", false, "\xc2\xb5test1"));
221
222 RunTest(test_cases);
223}
224
tbarzicdaf936a2015-07-29 21:05:14225TEST_F(ProcessOutputWatcherTest, SplitSoleUTF8Character) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37226 std::vector<TestCase> test_cases;
227 test_cases.push_back(TestCase("\xc2", false, ""));
228 test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
229
230 RunTest(test_cases);
231}
232
tbarzicdaf936a2015-07-29 21:05:14233TEST_F(ProcessOutputWatcherTest, SplitUTF8CharacterLength3) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37234 std::vector<TestCase> test_cases;
235 test_cases.push_back(TestCase("test3\xe2\x82", false, "test3"));
236 test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
237
238 RunTest(test_cases);
239}
240
tbarzicdaf936a2015-07-29 21:05:14241TEST_F(ProcessOutputWatcherTest, SplitSoleUTF8CharacterThreeWays) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37242 std::vector<TestCase> test_cases;
243 test_cases.push_back(TestCase("\xe2", false, ""));
244 test_cases.push_back(TestCase("\x82", false, ""));
245 test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
246
247 RunTest(test_cases);
248}
249
250TEST_F(ProcessOutputWatcherTest, EndsWithThreeByteUTF8Character) {
251 std::vector<TestCase> test_cases;
252 test_cases.push_back(TestCase("test\xe2\x82\xac", false, "test\xe2\x82\xac"));
253
254 RunTest(test_cases);
255}
256
257TEST_F(ProcessOutputWatcherTest, SoleThreeByteUTF8Character) {
258 std::vector<TestCase> test_cases;
259 test_cases.push_back(TestCase("\xe2\x82\xac", false, "\xe2\x82\xac"));
260
261 RunTest(test_cases);
262}
263
264TEST_F(ProcessOutputWatcherTest, HasThreeByteUTF8Character) {
265 std::vector<TestCase> test_cases;
266 test_cases.push_back(
267 TestCase("test\xe2\x82\xac_", false, "test\xe2\x82\xac_"));
268
269 RunTest(test_cases);
270}
271
272TEST_F(ProcessOutputWatcherTest, MulitByteUTF8CharNullTerminated) {
273 std::vector<TestCase> test_cases;
274 test_cases.push_back(TestCase("test\xe2\x82\xac", true, "test\xe2\x82\xac"));
275
276 RunTest(test_cases);
277}
278
tbarzicdaf936a2015-07-29 21:05:14279TEST_F(ProcessOutputWatcherTest, MultipleMultiByteUTF8Characters) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37280 std::vector<TestCase> test_cases;
281 test_cases.push_back(
282 TestCase("test\xe2\x82\xac\xc2", false, "test\xe2\x82\xac"));
283 test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
284
285 RunTest(test_cases);
286}
287
288TEST_F(ProcessOutputWatcherTest, ContainsInvalidUTF8) {
289 std::vector<TestCase> test_cases;
290 test_cases.push_back(TestCase("\xc2_", false, "\xc2_"));
291
292 RunTest(test_cases);
293}
294
tbarzicdaf936a2015-07-29 21:05:14295TEST_F(ProcessOutputWatcherTest, InvalidUTF8SeriesOfTrailingBytes) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37296 std::vector<TestCase> test_cases;
297 test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
298 test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
299
300 RunTest(test_cases);
301}
302
303TEST_F(ProcessOutputWatcherTest, EndsWithInvalidUTF8) {
304 std::vector<TestCase> test_cases;
305 test_cases.push_back(TestCase("\xff", false, "\xff"));
306
307 RunTest(test_cases);
308}
309
tbarzicdaf936a2015-07-29 21:05:14310TEST_F(ProcessOutputWatcherTest, FourByteUTF8) {
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37311 std::vector<TestCase> test_cases;
312 test_cases.push_back(TestCase("\xf0\xa4\xad", false, ""));
313 test_cases.push_back(TestCase("\xa2", false, "\xf0\xa4\xad\xa2"));
314
315 RunTest(test_cases);
thestigb44bd352014-09-10 01:47:06316}
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37317
tbarzic@chromium.org250f7f892012-04-11 02:46:43318// Verifies that sending '\0' generates PROCESS_OUTPUT_TYPE_OUT event and does
319// not terminate output watcher.
tbarzicdaf936a2015-07-29 21:05:14320TEST_F(ProcessOutputWatcherTest, SendNull) {
tbarzic@chromium.org250f7f892012-04-11 02:46:43321 std::vector<TestCase> test_cases;
tbarzic@chromium.org65bbd9d22014-05-15 20:34:37322 // This will send '\0' to output watcher.
tbarzic@chromium.org250f7f892012-04-11 02:46:43323 test_cases.push_back(TestCase("", true));
324 // Let's verify that next input also gets detected (i.e. output watcher does
325 // not exit after seeing '\0' from previous test case).
326 test_cases.push_back(TestCase("a", true));
327
328 RunTest(test_cases);
thestigb44bd352014-09-10 01:47:06329}
phajdan.jr@chromium.org3a01162b2013-03-12 20:22:48330
331} // namespace chromeos