OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "files/public/cpp/output_stream_file.h" | |
6 | |
7 #include <string.h> | |
8 | |
9 #include <memory> | |
10 #include <string> | |
11 | |
12 #include "files/public/interfaces/files.mojom.h" | |
13 #include "files/public/interfaces/types.mojom.h" | |
14 #include "mojo/public/cpp/application/application_test_base.h" | |
15 #include "mojo/public/cpp/bindings/interface_request.h" | |
16 #include "mojo/public/cpp/system/macros.h" | |
17 #include "mojo/public/cpp/utility/run_loop.h" | |
18 | |
19 namespace files_impl { | |
20 namespace { | |
21 | |
22 using OutputStreamFileTest = mojo::test::ApplicationTestBase; | |
23 | |
24 void QuitMessageLoop() { | |
25 mojo::RunLoop::current()->Quit(); | |
26 } | |
27 | |
28 void RunMessageLoop() { | |
29 mojo::RunLoop::current()->Run(); | |
30 } | |
31 | |
32 void RunMessageLoopUntilIdle() { | |
33 mojo::RunLoop::current()->RunUntilIdle(); | |
34 } | |
35 | |
36 // Converts a string to a |mojo::Array<uint8_t>|. | |
37 mojo::Array<uint8_t> StringToArray(const std::string& s) { | |
38 auto rv = mojo::Array<uint8_t>::New(s.size()); | |
39 if (s.size()) | |
40 memcpy(&rv[0], &s[0], s.size()); | |
41 return rv; | |
42 } | |
43 | |
44 class TestClient : public OutputStreamFile::Client { | |
45 public: | |
46 TestClient() { Reset(); } | |
47 ~TestClient() override {} | |
48 | |
49 void Reset() { | |
50 got_on_data_received_ = false; | |
51 data_ = std::string(); | |
52 got_on_closed_ = false; | |
53 } | |
54 | |
55 bool got_on_data_received() const { return got_on_data_received_; } | |
56 const std::string& data() const { return data_; } | |
57 bool got_on_closed() const { return got_on_closed_; } | |
58 | |
59 private: | |
60 // |OutputStreamFile::Client|: | |
61 void OnDataReceived(const void* bytes, size_t num_bytes) override { | |
62 got_on_data_received_ = true; | |
63 data_ = std::string(static_cast<const char*>(bytes), num_bytes); | |
64 QuitMessageLoop(); | |
65 } | |
66 void OnClosed() override { | |
67 got_on_closed_ = true; | |
68 QuitMessageLoop(); | |
69 } | |
70 | |
71 bool got_on_data_received_; | |
72 std::string data_; | |
73 bool got_on_closed_; | |
74 | |
75 MOJO_DISALLOW_COPY_AND_ASSIGN(TestClient); | |
76 }; | |
77 | |
78 void TestWrite(mojo::files::File* file, | |
79 TestClient* client, | |
80 const std::string& s) { | |
81 bool write_cb_called = false; | |
82 mojo::files::Error error = mojo::files::Error::INTERNAL; | |
83 uint32_t num_bytes_written = 0; | |
84 file->Write(StringToArray(s), 0, mojo::files::Whence::FROM_CURRENT, | |
85 [&write_cb_called, &error, &num_bytes_written]( | |
86 mojo::files::Error e, uint32_t n) { | |
87 write_cb_called = true; | |
88 error = e; | |
89 num_bytes_written = n; | |
90 QuitMessageLoop(); | |
91 }); | |
92 if (client) { | |
93 // If there's a client, since we're running everything on one thread, the | |
94 // impl (which will call the client, which will quit the message loop) will | |
95 // get called before the callback. | |
96 client->Reset(); | |
97 RunMessageLoop(); | |
98 EXPECT_TRUE(client->got_on_data_received()); | |
99 EXPECT_EQ(s, client->data()); | |
100 EXPECT_FALSE(client->got_on_closed()); | |
101 EXPECT_FALSE(write_cb_called); | |
102 // Spin the message loop again to get the callback. | |
103 client->Reset(); | |
104 RunMessageLoop(); | |
105 EXPECT_FALSE(client->got_on_data_received()); | |
106 EXPECT_FALSE(client->got_on_closed()); | |
107 } else { | |
108 // Otherwise, only the write callback will be called and quit the message | |
109 // loop. | |
110 RunMessageLoop(); | |
111 } | |
112 EXPECT_TRUE(write_cb_called); | |
113 EXPECT_EQ(mojo::files::Error::OK, error); | |
114 EXPECT_EQ(s.size(), num_bytes_written); | |
115 } | |
116 | |
117 void TestClose(mojo::files::File* file, TestClient* client) { | |
118 bool close_cb_called = false; | |
119 mojo::files::Error error = mojo::files::Error::INTERNAL; | |
120 file->Close([&close_cb_called, &error](mojo::files::Error e) { | |
121 close_cb_called = true; | |
122 error = e; | |
123 QuitMessageLoop(); | |
124 }); | |
125 // (This is analogous to |TestWrite()|.) | |
126 if (client) { | |
127 client->Reset(); | |
128 RunMessageLoop(); | |
129 EXPECT_FALSE(client->got_on_data_received()); | |
130 EXPECT_TRUE(client->got_on_closed()); | |
131 EXPECT_FALSE(close_cb_called); | |
132 client->Reset(); | |
133 RunMessageLoop(); | |
134 EXPECT_FALSE(client->got_on_data_received()); | |
135 EXPECT_FALSE(client->got_on_closed()); | |
136 } else { | |
137 RunMessageLoop(); | |
138 } | |
139 EXPECT_TRUE(close_cb_called); | |
140 EXPECT_EQ(mojo::files::Error::OK, error); | |
141 } | |
142 | |
143 TEST_F(OutputStreamFileTest, Basic) { | |
144 mojo::files::FilePtr file; | |
145 TestClient client; | |
146 std::unique_ptr<OutputStreamFile> file_impl = | |
147 OutputStreamFile::Create(&client, GetProxy(&file)); | |
148 | |
149 TestWrite(file.get(), &client, "hello"); | |
150 TestWrite(file.get(), &client, "world"); | |
151 TestClose(file.get(), &client); | |
152 } | |
153 | |
154 TEST_F(OutputStreamFileTest, SetClient) { | |
155 mojo::files::FilePtr file; | |
156 TestClient client1; | |
157 std::unique_ptr<OutputStreamFile> file_impl = | |
158 OutputStreamFile::Create(&client1, GetProxy(&file)); | |
159 | |
160 TestWrite(file.get(), &client1, "hello"); | |
161 | |
162 TestClient client2; | |
163 file_impl->set_client(&client2); | |
164 TestWrite(file.get(), &client2, "world"); | |
165 | |
166 file_impl->set_client(&client1); | |
167 TestWrite(file.get(), &client1, "!"); | |
168 TestClose(file.get(), &client1); | |
169 } | |
170 | |
171 TEST_F(OutputStreamFileTest, NullClient) { | |
172 mojo::files::FilePtr file; | |
173 std::unique_ptr<OutputStreamFile> file_impl = | |
174 OutputStreamFile::Create(nullptr, GetProxy(&file)); | |
175 | |
176 TestWrite(file.get(), nullptr, "hello"); | |
177 | |
178 TestClient client; | |
179 file_impl->set_client(&client); | |
180 TestWrite(file.get(), &client, "world"); | |
181 | |
182 file_impl->set_client(nullptr); | |
183 client.Reset(); | |
184 TestWrite(file.get(), nullptr, "!"); | |
185 TestClose(file.get(), nullptr); | |
186 EXPECT_FALSE(client.got_on_data_received()); | |
187 EXPECT_FALSE(client.got_on_closed()); | |
188 } | |
189 | |
190 TEST_F(OutputStreamFileTest, ImplOnlyClosesMessagePipeOnDestruction) { | |
191 mojo::files::FilePtr file; | |
192 std::unique_ptr<OutputStreamFile> file_impl = | |
193 OutputStreamFile::Create(nullptr, GetProxy(&file)); | |
194 bool got_connection_error = false; | |
195 file.set_connection_error_handler([&got_connection_error]() { | |
196 got_connection_error = true; | |
197 QuitMessageLoop(); | |
198 }); | |
199 | |
200 TestClose(file.get(), nullptr); | |
201 // The impl should only close its end when it's destroyed (even if |Close()| | |
202 // has been called). | |
203 RunMessageLoopUntilIdle(); | |
204 EXPECT_FALSE(got_connection_error); | |
205 file_impl.reset(); | |
206 RunMessageLoop(); | |
207 EXPECT_TRUE(got_connection_error); | |
208 } | |
209 | |
210 TEST_F(OutputStreamFileTest, ClosingMessagePipeCausesOnClosed) { | |
211 mojo::files::FilePtr file; | |
212 TestClient client; | |
213 std::unique_ptr<OutputStreamFile> file_impl = | |
214 OutputStreamFile::Create(&client, GetProxy(&file)); | |
215 | |
216 file.reset(); | |
217 RunMessageLoop(); | |
218 EXPECT_FALSE(client.got_on_data_received()); | |
219 EXPECT_TRUE(client.got_on_closed()); | |
220 } | |
221 | |
222 // Clients may own the impl (and this is a typical pattern). This client will | |
223 // own/destroy its impl on any |Client| call (and we'll test that this doesn't | |
224 // result in any additional calls to the client). | |
225 class TestClientDestroysImplClient : public OutputStreamFile::Client { | |
226 public: | |
227 explicit TestClientDestroysImplClient( | |
228 mojo::InterfaceRequest<mojo::files::File> request) | |
229 : file_impl_(OutputStreamFile::Create(this, request.Pass())) {} | |
230 ~TestClientDestroysImplClient() override {} | |
231 | |
232 private: | |
233 // OutputStreamFile::Client|: | |
234 void OnDataReceived(const void* /*bytes*/, size_t /*num_bytes*/) override { | |
235 // We reset the impl on any call, and afterwards it shouldn't call us. | |
236 EXPECT_TRUE(file_impl_); | |
237 file_impl_.reset(); | |
238 } | |
239 void OnClosed() override { | |
240 // We reset the impl on any call, and afterwards it shouldn't call us. | |
241 EXPECT_TRUE(file_impl_); | |
242 file_impl_.reset(); | |
243 } | |
244 | |
245 std::unique_ptr<OutputStreamFile> file_impl_; | |
246 | |
247 MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientDestroysImplClient); | |
248 }; | |
249 | |
250 TEST_F(OutputStreamFileTest, ClientDestroysImpl) { | |
251 // Test destruction due to writing. | |
252 { | |
253 mojo::files::FilePtr file; | |
254 TestClientDestroysImplClient client(GetProxy(&file)); | |
255 bool got_connection_error = false; | |
256 file.set_connection_error_handler([&got_connection_error]() { | |
257 got_connection_error = true; | |
258 QuitMessageLoop(); | |
259 }); | |
260 // |TestClientDestroysImplClient| doesn't quit the message loop, so it | |
261 // behaves like a null client. | |
262 TestWrite(file.get(), nullptr, "hello"); | |
263 // The connection error may be called immediately after the write callback, | |
264 // in which case we have to spin the message loop again. | |
265 if (!got_connection_error) | |
266 RunMessageLoop(); | |
267 EXPECT_TRUE(got_connection_error); | |
268 } | |
269 | |
270 // Test destruction due to closing. | |
271 { | |
272 mojo::files::FilePtr file; | |
273 TestClientDestroysImplClient client(GetProxy(&file)); | |
274 bool got_connection_error = false; | |
275 file.set_connection_error_handler([&got_connection_error]() { | |
276 got_connection_error = true; | |
277 QuitMessageLoop(); | |
278 }); | |
279 TestClose(file.get(), nullptr); | |
280 if (!got_connection_error) | |
281 RunMessageLoop(); | |
282 EXPECT_TRUE(got_connection_error); | |
283 } | |
284 } | |
285 | |
286 } // namespace | |
287 } // namespace files_impl | |
OLD | NEW |