Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(304)

Side by Side Diff: chromeos/process_proxy/process_output_watcher_unittest.cc

Issue 261743002: Improve process output watcher's handling of multi-byte UTF8 characters (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <gtest/gtest.h> 5 #include <gtest/gtest.h>
6 6
7 #include <queue> 7 #include <queue>
8 #include <string> 8 #include <string>
9 #include <vector> 9 #include <vector>
10 10
11 #include <sys/wait.h>
12
13 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/callback.h"
14 #include "base/file_util.h" 13 #include "base/file_util.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/posix/eintr_wrapper.h" 15 #include "base/posix/eintr_wrapper.h"
16 #include "base/synchronization/waitable_event.h" 16 #include "base/run_loop.h"
17 #include "base/strings/string_util.h"
17 #include "base/threading/thread.h" 18 #include "base/threading/thread.h"
18 #include "chromeos/process_proxy/process_output_watcher.h" 19 #include "chromeos/process_proxy/process_output_watcher.h"
19 20
20 namespace chromeos { 21 namespace chromeos {
21 22
22 struct TestCase { 23 struct TestCase {
23 std::string str; 24 TestCase(const std::string& input, bool send_terminating_null)
25 : input(input),
26 should_send_terminating_null(send_terminating_null),
27 expected_output(input) {}
28
29 // Conctructor for cases where the output is not expected to be the same as
30 // input.
31 TestCase(const std::string& input,
32 bool send_terminating_null,
33 const std::string& expected_output)
34 : input(input),
35 should_send_terminating_null(send_terminating_null),
36 expected_output(expected_output) {}
37
38 std::string input;
24 bool should_send_terminating_null; 39 bool should_send_terminating_null;
25 40 std::string expected_output;
26 TestCase(const std::string& expected_string,
27 bool send_terminating_null)
28 : str(expected_string),
29 should_send_terminating_null(send_terminating_null) {
30 }
31 }; 41 };
32 42
33 class ProcessWatcherExpectations { 43 class ProcessWatcherExpectations {
34 public: 44 public:
35 ProcessWatcherExpectations() {} 45 ProcessWatcherExpectations() {}
36 46
37 void Init(const std::vector<TestCase>& expectations) { 47 void SetTestCase(const TestCase& test_case) {
38 received_from_out_ = 0; 48 received_from_out_ = 0;
39 49
40 for (size_t i = 0; i < expectations.size(); i++) { 50 out_expectations_ = test_case.expected_output;
41 out_expectations_.append(expectations[i].str); 51 if (test_case.should_send_terminating_null)
42 if (expectations[i].should_send_terminating_null) 52 out_expectations_.append(std::string("", 1));
43 out_expectations_.append(std::string("", 1));
44 }
45 } 53 }
46 54
47 bool CheckExpectations(const std::string& data, ProcessOutputType type) { 55 bool CheckExpectations(const std::string& data, ProcessOutputType type) {
48 EXPECT_EQ(PROCESS_OUTPUT_TYPE_OUT, type); 56 EXPECT_EQ(PROCESS_OUTPUT_TYPE_OUT, type);
49 if (type != PROCESS_OUTPUT_TYPE_OUT) 57 if (type != PROCESS_OUTPUT_TYPE_OUT)
50 return false; 58 return false;
51 59
60 if (out_expectations_.length() == 0 && data.length() == 0)
61 return true;
62
52 EXPECT_LT(received_from_out_, out_expectations_.length()); 63 EXPECT_LT(received_from_out_, out_expectations_.length());
53 if (received_from_out_ >= out_expectations_.length()) 64 if (received_from_out_ >= out_expectations_.length())
54 return false; 65 return false;
55 66
56 EXPECT_EQ(received_from_out_, 67 EXPECT_EQ(received_from_out_,
57 out_expectations_.find(data, received_from_out_)); 68 out_expectations_.find(data, received_from_out_));
69 if (received_from_out_ != out_expectations_.find(data, received_from_out_))
70 return false;
58 71
59 received_from_out_ += data.length(); 72 received_from_out_ += data.length();
60 return true; 73 return true;
61 } 74 }
62 75
63 bool IsDone() { 76 bool IsDone() {
64 return received_from_out_ >= out_expectations_.length(); 77 return received_from_out_ >= out_expectations_.length();
65 } 78 }
66 79
67 private: 80 private:
68 std::string out_expectations_; 81 std::string out_expectations_;
69 size_t received_from_out_; 82 size_t received_from_out_;
70 }; 83 };
71 84
72 class ProcessOutputWatcherTest : public testing::Test { 85 class ProcessOutputWatcherTest : public testing::Test {
73 public: 86 public:
74 void StartWatch(int pt, int stop, 87 ProcessOutputWatcherTest() : output_watch_thread_started_(false),
75 const std::vector<TestCase>& expectations) { 88 failed_(false) {
76 expectations_.Init(expectations); 89 }
77 90
91 virtual ~ProcessOutputWatcherTest() {}
92
93 virtual void TearDown() OVERRIDE {
94 if (output_watch_thread_started_)
95 output_watch_thread_->Stop();
96 }
97
98 void StartWatch(int pt, int stop) {
78 // This will delete itself. 99 // This will delete itself.
79 ProcessOutputWatcher* crosh_watcher = new ProcessOutputWatcher(pt, stop, 100 ProcessOutputWatcher* crosh_watcher = new ProcessOutputWatcher(pt, stop,
80 base::Bind(&ProcessOutputWatcherTest::OnRead, base::Unretained(this))); 101 base::Bind(&ProcessOutputWatcherTest::OnRead, base::Unretained(this)));
81 crosh_watcher->Start(); 102 crosh_watcher->Start();
82 } 103 }
83 104
84 void OnRead(ProcessOutputType type, const std::string& output) { 105 void OnRead(ProcessOutputType type, const std::string& output) {
85 bool success = expectations_.CheckExpectations(output, type); 106 ASSERT_FALSE(failed_);
86 if (!success || expectations_.IsDone()) 107 failed_ = !expectations_.CheckExpectations(output, type);
87 all_data_received_->Signal(); 108 if (failed_ || expectations_.IsDone()) {
109 ASSERT_FALSE(test_case_done_callback_.is_null());
110 message_loop_.PostTask(FROM_HERE, test_case_done_callback_);
111 test_case_done_callback_.Reset();
112 }
88 } 113 }
89 114
90 protected: 115 protected:
91 std::string VeryLongString() { 116 std::string VeryLongString() {
92 std::string result = "0123456789"; 117 std::string result = "0123456789";
93 for (int i = 0; i < 8; i++) 118 for (int i = 0; i < 8; i++)
94 result = result.append(result); 119 result = result.append(result);
95 return result; 120 return result;
96 } 121 }
97 122
98 void RunTest(const std::vector<TestCase>& test_cases) { 123 void RunTest(const std::vector<TestCase>& test_cases) {
99 all_data_received_.reset(new base::WaitableEvent(true, false)); 124 ASSERT_FALSE(output_watch_thread_started_);
100 125 output_watch_thread_.reset(new base::Thread("ProcessOutpuWatchThread"));
101 base::Thread output_watch_thread("ProcessOutpuWatchThread"); 126 output_watch_thread_started_ = output_watch_thread_->Start();
102 ASSERT_TRUE(output_watch_thread.Start()); 127 ASSERT_TRUE(output_watch_thread_started_);
103 128
104 int pt_pipe[2], stop_pipe[2]; 129 int pt_pipe[2], stop_pipe[2];
105 ASSERT_FALSE(HANDLE_EINTR(pipe(pt_pipe))); 130 ASSERT_FALSE(HANDLE_EINTR(pipe(pt_pipe)));
106 ASSERT_FALSE(HANDLE_EINTR(pipe(stop_pipe))); 131 ASSERT_FALSE(HANDLE_EINTR(pipe(stop_pipe)));
107 132
108 output_watch_thread.message_loop()->PostTask(FROM_HERE, 133 output_watch_thread_->message_loop()->PostTask(
134 FROM_HERE,
109 base::Bind(&ProcessOutputWatcherTest::StartWatch, 135 base::Bind(&ProcessOutputWatcherTest::StartWatch,
110 base::Unretained(this), 136 base::Unretained(this),
111 pt_pipe[0], stop_pipe[0], test_cases)); 137 pt_pipe[0],
138 stop_pipe[0]));
112 139
113 for (size_t i = 0; i < test_cases.size(); i++) { 140 for (size_t i = 0; i < test_cases.size(); i++) {
114 const std::string& test_str = test_cases[i].str; 141 expectations_.SetTestCase(test_cases[i]);
142
143 base::RunLoop run_loop;
144 ASSERT_TRUE(test_case_done_callback_.is_null());
145 test_case_done_callback_ = run_loop.QuitClosure();
146
147 const std::string& test_str = test_cases[i].input;
115 // Let's make inputs not NULL terminated, unless other is specified in 148 // Let's make inputs not NULL terminated, unless other is specified in
116 // the test case. 149 // the test case.
117 ssize_t test_size = test_str.length() * sizeof(*test_str.c_str()); 150 ssize_t test_size = test_str.length() * sizeof(*test_str.c_str());
118 if (test_cases[i].should_send_terminating_null) 151 if (test_cases[i].should_send_terminating_null)
119 test_size += sizeof(*test_str.c_str()); 152 test_size += sizeof(*test_str.c_str());
120 EXPECT_EQ(test_size, 153 EXPECT_EQ(test_size,
121 base::WriteFileDescriptor(pt_pipe[1], test_str.c_str(), 154 base::WriteFileDescriptor(pt_pipe[1], test_str.c_str(),
122 test_size)); 155 test_size));
156
157 run_loop.Run();
158 EXPECT_TRUE(expectations_.IsDone());
159 if (failed_)
160 break;
123 } 161 }
124 162
125 all_data_received_->Wait();
126
127 // Send stop signal. It is not important which string we send. 163 // Send stop signal. It is not important which string we send.
128 EXPECT_EQ(1, base::WriteFileDescriptor(stop_pipe[1], "q", 1)); 164 EXPECT_EQ(1, base::WriteFileDescriptor(stop_pipe[1], "q", 1));
129 165
130 EXPECT_NE(-1, IGNORE_EINTR(close(stop_pipe[1]))); 166 EXPECT_NE(-1, IGNORE_EINTR(close(stop_pipe[1])));
131 EXPECT_NE(-1, IGNORE_EINTR(close(pt_pipe[1]))); 167 EXPECT_NE(-1, IGNORE_EINTR(close(pt_pipe[1])));
132
133 output_watch_thread.Stop();
134 } 168 }
135 169
136 scoped_ptr<base::WaitableEvent> all_data_received_;
137
138 private: 170 private:
171 base::Closure test_case_done_callback_;
172 base::MessageLoop message_loop_;
173 scoped_ptr<base::Thread> output_watch_thread_;
174 bool output_watch_thread_started_;
175 bool failed_;
139 ProcessWatcherExpectations expectations_; 176 ProcessWatcherExpectations expectations_;
140 std::vector<TestCase> exp; 177 std::vector<TestCase> exp;
141 }; 178 };
142 179
143 180
144 TEST_F(ProcessOutputWatcherTest, OutputWatcher) { 181 TEST_F(ProcessOutputWatcherTest, OutputWatcher) {
145 std::vector<TestCase> test_cases; 182 std::vector<TestCase> test_cases;
183 test_cases.push_back(TestCase("t", false));
146 test_cases.push_back(TestCase("testing output\n", false)); 184 test_cases.push_back(TestCase("testing output\n", false));
147 test_cases.push_back(TestCase("testing error\n", false)); 185 test_cases.push_back(TestCase("testing error\n", false));
148 test_cases.push_back(TestCase("testing error1\n", false)); 186 test_cases.push_back(TestCase("testing error1\n", false));
149 test_cases.push_back(TestCase("testing output1\n", false)); 187 test_cases.push_back(TestCase("testing output1\n", false));
150 test_cases.push_back(TestCase("testing output2\n", false)); 188 test_cases.push_back(TestCase("testing output2\n", false));
151 test_cases.push_back(TestCase("testing output3\n", false)); 189 test_cases.push_back(TestCase("testing output3\n", false));
152 test_cases.push_back(TestCase(VeryLongString(), false)); 190 test_cases.push_back(TestCase(VeryLongString(), false));
153 test_cases.push_back(TestCase("testing error2\n", false)); 191 test_cases.push_back(TestCase("testing error2\n", false));
154 192
155 RunTest(test_cases); 193 RunTest(test_cases);
156 }; 194 };
157 195
196 TEST_F(ProcessOutputWatcherTest, SplitUTF8Character) {
197 std::vector<TestCase> test_cases;
198 test_cases.push_back(TestCase("test1\xc2", false, "test1"));
199 test_cases.push_back(TestCase("\xb5test1", false, "\xc2\xb5test1"));
200
201 RunTest(test_cases);
202 }
203
204 TEST_F(ProcessOutputWatcherTest, SplitSoleUTF8Character) {
205 std::vector<TestCase> test_cases;
206 test_cases.push_back(TestCase("\xc2", false, ""));
207 test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
208
209 RunTest(test_cases);
210 }
211
212 TEST_F(ProcessOutputWatcherTest, SplitUTF8CharacterLength3) {
213 std::vector<TestCase> test_cases;
214 test_cases.push_back(TestCase("test3\xe2\x82", false, "test3"));
215 test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
216
217 RunTest(test_cases);
218 }
219
220 TEST_F(ProcessOutputWatcherTest, SplitSoleUTF8CharacterThreeWays) {
221 std::vector<TestCase> test_cases;
222 test_cases.push_back(TestCase("\xe2", false, ""));
223 test_cases.push_back(TestCase("\x82", false, ""));
224 test_cases.push_back(TestCase("\xac", false, "\xe2\x82\xac"));
225
226 RunTest(test_cases);
227 }
228
229 TEST_F(ProcessOutputWatcherTest, EndsWithThreeByteUTF8Character) {
230 std::vector<TestCase> test_cases;
231 test_cases.push_back(TestCase("test\xe2\x82\xac", false, "test\xe2\x82\xac"));
232
233 RunTest(test_cases);
234 }
235
236 TEST_F(ProcessOutputWatcherTest, SoleThreeByteUTF8Character) {
237 std::vector<TestCase> test_cases;
238 test_cases.push_back(TestCase("\xe2\x82\xac", false, "\xe2\x82\xac"));
239
240 RunTest(test_cases);
241 }
242
243 TEST_F(ProcessOutputWatcherTest, HasThreeByteUTF8Character) {
244 std::vector<TestCase> test_cases;
245 test_cases.push_back(
246 TestCase("test\xe2\x82\xac_", false, "test\xe2\x82\xac_"));
247
248 RunTest(test_cases);
249 }
250
251 TEST_F(ProcessOutputWatcherTest, MulitByteUTF8CharNullTerminated) {
252 std::vector<TestCase> test_cases;
253 test_cases.push_back(TestCase("test\xe2\x82\xac", true, "test\xe2\x82\xac"));
254
255 RunTest(test_cases);
256 }
257
258 TEST_F(ProcessOutputWatcherTest, MultipleMultiByteUTF8Characters) {
259 std::vector<TestCase> test_cases;
260 test_cases.push_back(
261 TestCase("test\xe2\x82\xac\xc2", false, "test\xe2\x82\xac"));
262 test_cases.push_back(TestCase("\xb5", false, "\xc2\xb5"));
263
264 RunTest(test_cases);
265 }
266
267 TEST_F(ProcessOutputWatcherTest, ContainsInvalidUTF8) {
268 std::vector<TestCase> test_cases;
269 test_cases.push_back(TestCase("\xc2_", false, "\xc2_"));
270
271 RunTest(test_cases);
272 }
273
274 TEST_F(ProcessOutputWatcherTest, InvalidUTF8SeriesOfTrailingBytes) {
275 std::vector<TestCase> test_cases;
276 test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
277 test_cases.push_back(TestCase("\x82\x82\x82", false, "\x82\x82\x82"));
278
279 RunTest(test_cases);
280 }
281
282 TEST_F(ProcessOutputWatcherTest, EndsWithInvalidUTF8) {
283 std::vector<TestCase> test_cases;
284 test_cases.push_back(TestCase("\xff", false, "\xff"));
285
286 RunTest(test_cases);
287 }
288
289 TEST_F(ProcessOutputWatcherTest, FourByteUTF8) {
290 std::vector<TestCase> test_cases;
291 test_cases.push_back(TestCase("\xf0\xa4\xad", false, ""));
292 test_cases.push_back(TestCase("\xa2", false, "\xf0\xa4\xad\xa2"));
293
294 RunTest(test_cases);
295 };
296
158 // Verifies that sending '\0' generates PROCESS_OUTPUT_TYPE_OUT event and does 297 // Verifies that sending '\0' generates PROCESS_OUTPUT_TYPE_OUT event and does
159 // not terminate output watcher. 298 // not terminate output watcher.
160 TEST_F(ProcessOutputWatcherTest, SendNull) { 299 TEST_F(ProcessOutputWatcherTest, SendNull) {
161 std::vector<TestCase> test_cases; 300 std::vector<TestCase> test_cases;
162 // This will send '\0' to output wathcer. 301 // This will send '\0' to output watcher.
163 test_cases.push_back(TestCase("", true)); 302 test_cases.push_back(TestCase("", true));
164 // Let's verify that next input also gets detected (i.e. output watcher does 303 // Let's verify that next input also gets detected (i.e. output watcher does
165 // not exit after seeing '\0' from previous test case). 304 // not exit after seeing '\0' from previous test case).
166 test_cases.push_back(TestCase("a", true)); 305 test_cases.push_back(TestCase("a", true));
167 306
168 RunTest(test_cases); 307 RunTest(test_cases);
169 }; 308 };
170 309
171 } // namespace chromeos 310 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/process_proxy/process_output_watcher.cc ('k') | chromeos/process_proxy/process_proxy_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698