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

Unified 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: files_impl Created 5 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « mojo/services/files/public/cpp/output_stream_file.h ('k') | mojo/tools/data/apptests » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc
diff --git a/mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc b/mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1a0999cf1429102805897929d82d2695eebc95bc
--- /dev/null
+++ b/mojo/services/files/public/cpp/tests/output_stream_file_unittest.cc
@@ -0,0 +1,287 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "files/public/cpp/output_stream_file.h"
+
+#include <string.h>
+
+#include <memory>
+#include <string>
+
+#include "files/public/interfaces/files.mojom.h"
+#include "files/public/interfaces/types.mojom.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/cpp/utility/run_loop.h"
+
+namespace files_impl {
+namespace {
+
+using OutputStreamFileTest = mojo::test::ApplicationTestBase;
+
+void QuitMessageLoop() {
+ mojo::RunLoop::current()->Quit();
+}
+
+void RunMessageLoop() {
+ mojo::RunLoop::current()->Run();
+}
+
+void RunMessageLoopUntilIdle() {
+ mojo::RunLoop::current()->RunUntilIdle();
+}
+
+// Converts a string to a |mojo::Array<uint8_t>| (not in
+mojo::Array<uint8_t> StringToArray(const std::string& s) {
+ auto rv = mojo::Array<uint8_t>::New(s.size());
+ if (s.size())
+ memcpy(&rv[0], &s[0], s.size());
+ return rv;
+}
+
+class TestClient : public OutputStreamFile::Client {
+ public:
+ TestClient() { Reset(); }
+ ~TestClient() override {}
+
+ void Reset() {
+ got_on_data_received_ = false;
+ data_ = std::string();
+ got_on_closed_ = false;
+ }
+
+ bool got_on_data_received() const { return got_on_data_received_; }
+ const std::string& data() const { return data_; }
+ bool got_on_closed() const { return got_on_closed_; }
+
+ private:
+ // |OutputStreamFile::Client|:
+ void OnDataReceived(const void* bytes, size_t num_bytes) override {
+ got_on_data_received_ = true;
+ data_ = std::string(static_cast<const char*>(bytes), num_bytes);
+ QuitMessageLoop();
+ }
+ void OnClosed() override {
+ got_on_closed_ = true;
+ QuitMessageLoop();
+ }
+
+ bool got_on_data_received_;
+ std::string data_;
+ bool got_on_closed_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestClient);
+};
+
+void TestWrite(mojo::files::File* file,
+ TestClient* client,
+ const std::string& s) {
+ bool write_cb_called = false;
+ mojo::files::Error error = mojo::files::ERROR_INTERNAL;
+ uint32_t num_bytes_written = 0;
+ file->Write(StringToArray(s), 0, mojo::files::WHENCE_FROM_CURRENT,
+ [&write_cb_called, &error, &num_bytes_written](
+ mojo::files::Error e, uint32_t n) {
+ write_cb_called = true;
+ error = e;
+ num_bytes_written = n;
+ QuitMessageLoop();
+ });
+ if (client) {
+ // If there's a client, since we're running everything on one thread, the
+ // impl (which will call the client, which will quit the message loop) will
+ // get called before the callback.
+ client->Reset();
+ RunMessageLoop();
+ EXPECT_TRUE(client->got_on_data_received());
+ EXPECT_EQ(s, client->data());
+ EXPECT_FALSE(client->got_on_closed());
+ EXPECT_FALSE(write_cb_called);
+ // Spin the message loop again to get the callback.
+ client->Reset();
+ RunMessageLoop();
+ EXPECT_FALSE(client->got_on_data_received());
+ EXPECT_FALSE(client->got_on_closed());
+ } else {
+ // Otherwise, only the write callback will be called and quit the message
+ // loop.
+ RunMessageLoop();
+ }
+ EXPECT_TRUE(write_cb_called);
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+ EXPECT_EQ(s.size(), num_bytes_written);
+}
+
+void TestClose(mojo::files::File* file, TestClient* client) {
+ bool close_cb_called = false;
+ mojo::files::Error error = mojo::files::ERROR_INTERNAL;
+ file->Close([&close_cb_called, &error](mojo::files::Error e) {
+ close_cb_called = true;
+ error = e;
+ QuitMessageLoop();
+ });
+ // (This is analogous to |TestWrite()|.)
+ if (client) {
+ client->Reset();
+ RunMessageLoop();
+ EXPECT_FALSE(client->got_on_data_received());
+ EXPECT_TRUE(client->got_on_closed());
+ EXPECT_FALSE(close_cb_called);
+ client->Reset();
+ RunMessageLoop();
+ EXPECT_FALSE(client->got_on_data_received());
+ EXPECT_FALSE(client->got_on_closed());
+ } else {
+ RunMessageLoop();
+ }
+ EXPECT_TRUE(close_cb_called);
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+}
+
+TEST_F(OutputStreamFileTest, Basic) {
+ mojo::files::FilePtr file;
+ TestClient client;
+ std::unique_ptr<OutputStreamFile> file_impl =
+ OutputStreamFile::Create(&client, GetProxy(&file));
+
+ TestWrite(file.get(), &client, "hello");
+ TestWrite(file.get(), &client, "world");
+ TestClose(file.get(), &client);
+}
+
+TEST_F(OutputStreamFileTest, SetClient) {
+ mojo::files::FilePtr file;
+ TestClient client1;
+ std::unique_ptr<OutputStreamFile> file_impl =
+ OutputStreamFile::Create(&client1, GetProxy(&file));
+
+ TestWrite(file.get(), &client1, "hello");
+
+ TestClient client2;
+ file_impl->set_client(&client2);
+ TestWrite(file.get(), &client2, "world");
+
+ file_impl->set_client(&client1);
+ TestWrite(file.get(), &client1, "!");
+ TestClose(file.get(), &client1);
+}
+
+TEST_F(OutputStreamFileTest, NullClient) {
+ mojo::files::FilePtr file;
+ std::unique_ptr<OutputStreamFile> file_impl =
+ OutputStreamFile::Create(nullptr, GetProxy(&file));
+
+ TestWrite(file.get(), nullptr, "hello");
+
+ TestClient client;
+ file_impl->set_client(&client);
+ TestWrite(file.get(), &client, "world");
+
+ file_impl->set_client(nullptr);
+ client.Reset();
+ TestWrite(file.get(), nullptr, "!");
+ TestClose(file.get(), nullptr);
+ EXPECT_FALSE(client.got_on_data_received());
+ EXPECT_FALSE(client.got_on_closed());
+}
+
+TEST_F(OutputStreamFileTest, ImplOnlyClosesMessagePipeOnDestruction) {
+ mojo::files::FilePtr file;
+ std::unique_ptr<OutputStreamFile> file_impl =
+ OutputStreamFile::Create(nullptr, GetProxy(&file));
+ bool got_connection_error = false;
+ file.set_connection_error_handler([&got_connection_error]() {
+ got_connection_error = true;
+ QuitMessageLoop();
+ });
+
+ TestClose(file.get(), nullptr);
+ // The impl should only close its end when it's destroyed (even if |Close()|
+ // has been called).
+ RunMessageLoopUntilIdle();
+ EXPECT_FALSE(got_connection_error);
+ file_impl.reset();
+ RunMessageLoop();
+ EXPECT_TRUE(got_connection_error);
+}
+
+TEST_F(OutputStreamFileTest, ClosingMessagePipeCausesOnClosed) {
+ mojo::files::FilePtr file;
+ TestClient client;
+ std::unique_ptr<OutputStreamFile> file_impl =
+ OutputStreamFile::Create(&client, GetProxy(&file));
+
+ file.reset();
+ RunMessageLoop();
+ EXPECT_FALSE(client.got_on_data_received());
+ EXPECT_TRUE(client.got_on_closed());
+}
+
+// Clients may own the impl (and this is a typical pattern). This client will
+// own/destroy its impl on any |Client| call (and we'll test that this doesn't
+// result in any additional calls to the client).
+class TestClientDestroysImplClient : public OutputStreamFile::Client {
+ public:
+ explicit TestClientDestroysImplClient(
+ mojo::InterfaceRequest<mojo::files::File> request)
+ : file_impl_(OutputStreamFile::Create(this, request.Pass())) {}
+ ~TestClientDestroysImplClient() override {}
+
+ private:
+ // OutputStreamFile::Client|:
+ void OnDataReceived(const void* /*bytes*/, size_t /*num_bytes*/) override {
+ // We reset the impl on any call, and afterwards it shouldn't call us.
+ EXPECT_TRUE(file_impl_);
+ file_impl_.reset();
+ }
+ void OnClosed() override {
+ // We reset the impl on any call, and afterwards it shouldn't call us.
+ EXPECT_TRUE(file_impl_);
+ file_impl_.reset();
+ }
+
+ std::unique_ptr<OutputStreamFile> file_impl_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientDestroysImplClient);
+};
+
+TEST_F(OutputStreamFileTest, ClientDestroysImpl) {
+ // Test destruction due to writing.
+ {
+ mojo::files::FilePtr file;
+ TestClientDestroysImplClient client(GetProxy(&file));
+ bool got_connection_error = false;
+ file.set_connection_error_handler([&got_connection_error]() {
+ got_connection_error = true;
+ QuitMessageLoop();
+ });
+ // |TestClientDestroysImplClient| doesn't quit the message loop, so it
+ // behaves like a null client.
+ TestWrite(file.get(), nullptr, "hello");
+ // The connection error may be called immediately after the write callback,
+ // in which case we have to spin the message loop again.
+ if (!got_connection_error)
+ RunMessageLoop();
+ EXPECT_TRUE(got_connection_error);
+ }
+
+ // Test destruction due to closing.
+ {
+ mojo::files::FilePtr file;
+ TestClientDestroysImplClient client(GetProxy(&file));
+ bool got_connection_error = false;
+ file.set_connection_error_handler([&got_connection_error]() {
+ got_connection_error = true;
+ QuitMessageLoop();
+ });
+ TestClose(file.get(), nullptr);
+ if (!got_connection_error)
+ RunMessageLoop();
+ EXPECT_TRUE(got_connection_error);
+ }
+}
+
+} // namespace
+} // namespace files_impl
« no previous file with comments | « mojo/services/files/public/cpp/output_stream_file.h ('k') | mojo/tools/data/apptests » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698