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

Side by Side Diff: mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698