| 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 | 
|---|