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

Unified Diff: mojo/services/view_manager/view_manager_client_apptest.cc

Issue 1049993002: Get mojo_shell building inside chromium checkout. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix presubmit Created 5 years, 9 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
Index: mojo/services/view_manager/view_manager_client_apptest.cc
diff --git a/mojo/services/view_manager/view_manager_client_apptest.cc b/mojo/services/view_manager/view_manager_client_apptest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9d441d723b2b61c14872085124a3d0ec622c1dcd
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_client_apptest.cc
@@ -0,0 +1,613 @@
+// Copyright 2014 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 "third_party/mojo_services/src/view_manager/public/cpp/view_manager.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_timeouts.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "third_party/mojo_services/src/geometry/public/cpp/geometry_util.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/lib/view_manager_client_impl.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_context.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_delegate.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_observer.h"
+
+namespace mojo {
+
+namespace {
+
+base::RunLoop* current_run_loop = nullptr;
+
+void TimeoutRunLoop(const base::Closure& timeout_task, bool* timeout) {
+ CHECK(current_run_loop);
+ *timeout = true;
+ timeout_task.Run();
+}
+
+bool DoRunLoopWithTimeout() {
+ if (current_run_loop != nullptr)
+ return false;
+
+ bool timeout = false;
+ base::RunLoop run_loop;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE, base::Bind(&TimeoutRunLoop, run_loop.QuitClosure(), &timeout),
+ TestTimeouts::action_timeout());
+
+ current_run_loop = &run_loop;
+ current_run_loop->Run();
+ current_run_loop = nullptr;
+ return !timeout;
+}
+
+void QuitRunLoop() {
+ current_run_loop->Quit();
+ current_run_loop = nullptr;
+}
+
+class BoundsChangeObserver : public ViewObserver {
+ public:
+ explicit BoundsChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ ~BoundsChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+ // Overridden from ViewObserver:
+ void OnViewBoundsChanged(View* view,
+ const Rect& old_bounds,
+ const Rect& new_bounds) override {
+ DCHECK_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver);
+};
+
+// Wait until the bounds of the supplied view change; returns false on timeout.
+bool WaitForBoundsToChange(View* view) {
+ BoundsChangeObserver observer(view);
+ return DoRunLoopWithTimeout();
+}
+
+// Spins a run loop until the tree beginning at |root| has |tree_size| views
+// (including |root|).
+class TreeSizeMatchesObserver : public ViewObserver {
+ public:
+ TreeSizeMatchesObserver(View* tree, size_t tree_size)
+ : tree_(tree), tree_size_(tree_size) {
+ tree_->AddObserver(this);
+ }
+ ~TreeSizeMatchesObserver() override { tree_->RemoveObserver(this); }
+
+ bool IsTreeCorrectSize() { return CountViews(tree_) == tree_size_; }
+
+ private:
+ // Overridden from ViewObserver:
+ void OnTreeChanged(const TreeChangeParams& params) override {
+ if (IsTreeCorrectSize())
+ QuitRunLoop();
+ }
+
+ size_t CountViews(const View* view) const {
+ size_t count = 1;
+ View::Children::const_iterator it = view->children().begin();
+ for (; it != view->children().end(); ++it)
+ count += CountViews(*it);
+ return count;
+ }
+
+ View* tree_;
+ size_t tree_size_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver);
+};
+
+// Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
+bool WaitForTreeSizeToMatch(View* view, size_t tree_size) {
+ TreeSizeMatchesObserver observer(view, tree_size);
+ return observer.IsTreeCorrectSize() || DoRunLoopWithTimeout();
+}
+
+class OrderChangeObserver : public ViewObserver {
+ public:
+ OrderChangeObserver(View* view) : view_(view) { view_->AddObserver(this); }
+ ~OrderChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+ // Overridden from ViewObserver:
+ void OnViewReordered(View* view,
+ View* relative_view,
+ OrderDirection direction) override {
+ DCHECK_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver);
+};
+
+// Wait until |view|'s tree size matches |tree_size|; returns false on timeout.
+bool WaitForOrderChange(ViewManager* view_manager, View* view) {
+ OrderChangeObserver observer(view);
+ return DoRunLoopWithTimeout();
+}
+
+// Tracks a view's destruction. Query is_valid() for current state.
+class ViewTracker : public ViewObserver {
+ public:
+ explicit ViewTracker(View* view) : view_(view) { view_->AddObserver(this); }
+ ~ViewTracker() override {
+ if (view_)
+ view_->RemoveObserver(this);
+ }
+
+ bool is_valid() const { return !!view_; }
+
+ private:
+ // Overridden from ViewObserver:
+ void OnViewDestroyed(View* view) override {
+ DCHECK_EQ(view, view_);
+ view_ = nullptr;
+ }
+
+ int id_;
+ View* view_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ViewTracker);
+};
+
+} // namespace
+
+// ViewManager -----------------------------------------------------------------
+
+// These tests model synchronization of two peer connections to the view manager
+// service, that are given access to some root view.
+
+class ViewManagerTest : public test::ApplicationTestBase,
+ public ApplicationDelegate,
+ public ViewManagerDelegate {
+ public:
+ ViewManagerTest()
+ : most_recent_view_manager_(nullptr), window_manager_(nullptr) {}
+
+ // Overridden from ApplicationDelegate:
+ void Initialize(ApplicationImpl* app) override {
+ view_manager_client_factory_.reset(
+ new ViewManagerClientFactory(app->shell(), this));
+ }
+
+ // ApplicationDelegate implementation.
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ connection->AddService(view_manager_client_factory_.get());
+ return true;
+ }
+
+ ViewManager* window_manager() { return window_manager_; }
+
+ // Embeds another version of the test app @ view; returns nullptr on timeout.
+ ViewManager* Embed(ViewManager* view_manager, View* view) {
+ DCHECK_EQ(view_manager, view->view_manager());
+ most_recent_view_manager_ = nullptr;
+ view->Embed(application_impl()->url());
+ if (!DoRunLoopWithTimeout())
+ return nullptr;
+ ViewManager* vm = nullptr;
+ std::swap(vm, most_recent_view_manager_);
+ return vm;
+ }
+
+ ApplicationDelegate* GetApplicationDelegate() override { return this; }
+
+ // Overridden from ViewManagerDelegate:
+ void OnEmbed(View* root,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) override {
+ most_recent_view_manager_ = root->view_manager();
+ QuitRunLoop();
+ }
+ void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+ private:
+ // Overridden from testing::Test:
+ void SetUp() override {
+ ApplicationTestBase::SetUp();
+
+ view_manager_context_.reset(new ViewManagerContext(application_impl()));
+ view_manager_context_->Embed(application_impl()->url());
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ std::swap(window_manager_, most_recent_view_manager_);
+ }
+
+ // Overridden from testing::Test:
+ void TearDown() override { ApplicationTestBase::TearDown(); }
+
+ scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+ scoped_ptr<ViewManagerContext> view_manager_context_;
+
+ // Used to receive the most recent view manager loaded by an embed action.
+ ViewManager* most_recent_view_manager_;
+ // The View Manager connection held by the window manager (app running at the
+ // root view).
+ ViewManager* window_manager_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(ViewManagerTest);
+};
+
+TEST_F(ViewManagerTest, RootView) {
+ ASSERT_NE(nullptr, window_manager());
+ EXPECT_NE(nullptr, window_manager()->GetRoot());
+ EXPECT_EQ("mojo:window_manager", window_manager()->GetEmbedderURL());
+}
+
+TEST_F(ViewManagerTest, Embed) {
+ View* view = window_manager()->CreateView();
+ ASSERT_NE(nullptr, view);
+ view->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ ASSERT_NE(nullptr, embedded);
+
+ View* view_in_embedded = embedded->GetRoot();
+ ASSERT_NE(nullptr, view_in_embedded);
+ EXPECT_EQ(view->id(), view_in_embedded->id());
+ EXPECT_EQ(nullptr, view_in_embedded->parent());
+ EXPECT_TRUE(view_in_embedded->children().empty());
+}
+
+// Window manager has two views, N1 and N11. Embeds A at N1. A should not see
+// N11.
+TEST_F(ViewManagerTest, EmbeddedDoesntSeeChild) {
+ View* view = window_manager()->CreateView();
+ ASSERT_NE(nullptr, view);
+ view->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view);
+ View* nested = window_manager()->CreateView();
+ ASSERT_NE(nullptr, nested);
+ nested->SetVisible(true);
+ view->AddChild(nested);
+
+ ViewManager* embedded = Embed(window_manager(), view);
+ ASSERT_NE(nullptr, embedded);
+ View* view_in_embedded = embedded->GetRoot();
+ EXPECT_EQ(view->id(), view_in_embedded->id());
+ EXPECT_EQ(nullptr, view_in_embedded->parent());
+ EXPECT_TRUE(view_in_embedded->children().empty());
+}
+
+// TODO(beng): write a replacement test for the one that once existed here:
+// This test validates the following scenario:
+// - a view originating from one connection
+// - a view originating from a second connection
+// + the connection originating the view is destroyed
+// -> the view should still exist (since the second connection is live) but
+// should be disconnected from any views.
+// http://crbug.com/396300
+//
+// TODO(beng): The new test should validate the scenario as described above
+// except that the second connection still has a valid tree.
+
+// Verifies that bounds changes applied to a view hierarchy in one connection
+// are reflected to another.
+TEST_F(ViewManagerTest, SetBounds) {
+ View* view = window_manager()->CreateView();
+ view->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ ASSERT_NE(nullptr, embedded);
+
+ View* view_in_embedded = embedded->GetViewById(view->id());
+ EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+
+ Rect rect;
+ rect.width = rect.height = 100;
+ view->SetBounds(rect);
+ ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
+ EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+}
+
+// Verifies that bounds changes applied to a view owned by a different
+// connection are refused.
+TEST_F(ViewManagerTest, SetBoundsSecurity) {
+ View* view = window_manager()->CreateView();
+ view->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ ASSERT_NE(nullptr, embedded);
+
+ View* view_in_embedded = embedded->GetViewById(view->id());
+ Rect rect;
+ rect.width = 800;
+ rect.height = 600;
+ view->SetBounds(rect);
+ ASSERT_TRUE(WaitForBoundsToChange(view_in_embedded));
+
+ rect.width = 1024;
+ rect.height = 768;
+ view_in_embedded->SetBounds(rect);
+ // Bounds change should have been rejected.
+ EXPECT_EQ(view->bounds(), view_in_embedded->bounds());
+}
+
+// Verifies that a view can only be destroyed by the connection that created it.
+TEST_F(ViewManagerTest, DestroySecurity) {
+ View* view = window_manager()->CreateView();
+ view->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ ASSERT_NE(nullptr, embedded);
+
+ View* view_in_embedded = embedded->GetViewById(view->id());
+
+ ViewTracker tracker2(view_in_embedded);
+ view_in_embedded->Destroy();
+ // View should not have been destroyed.
+ EXPECT_TRUE(tracker2.is_valid());
+
+ ViewTracker tracker1(view);
+ view->Destroy();
+ EXPECT_FALSE(tracker1.is_valid());
+}
+
+TEST_F(ViewManagerTest, MultiRoots) {
+ View* view1 = window_manager()->CreateView();
+ view1->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view1);
+ View* view2 = window_manager()->CreateView();
+ view2->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view2);
+ ViewManager* embedded1 = Embed(window_manager(), view1);
+ ASSERT_NE(nullptr, embedded1);
+ ViewManager* embedded2 = Embed(window_manager(), view2);
+ ASSERT_NE(nullptr, embedded2);
+ EXPECT_NE(embedded1, embedded2);
+}
+
+TEST_F(ViewManagerTest, EmbeddingIdentity) {
+ View* view = window_manager()->CreateView();
+ view->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view);
+ ViewManager* embedded = Embed(window_manager(), view);
+ ASSERT_NE(nullptr, embedded);
+ EXPECT_EQ(application_impl()->url(), embedded->GetEmbedderURL());
+}
+
+// TODO(alhaad): Currently, the RunLoop gets stuck waiting for order change.
+// Debug and re-enable this.
+TEST_F(ViewManagerTest, DISABLED_Reorder) {
+ View* view1 = window_manager()->CreateView();
+ view1->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view1);
+
+ ViewManager* embedded = Embed(window_manager(), view1);
+ ASSERT_NE(nullptr, embedded);
+
+ View* view11 = embedded->CreateView();
+ view11->SetVisible(true);
+ embedded->GetRoot()->AddChild(view11);
+ View* view12 = embedded->CreateView();
+ view12->SetVisible(true);
+ embedded->GetRoot()->AddChild(view12);
+
+ View* root_in_embedded = embedded->GetRoot();
+
+ {
+ ASSERT_TRUE(WaitForTreeSizeToMatch(root_in_embedded, 3u));
+ view11->MoveToFront();
+ ASSERT_TRUE(WaitForOrderChange(embedded, root_in_embedded));
+
+ EXPECT_EQ(root_in_embedded->children().front(),
+ embedded->GetViewById(view12->id()));
+ EXPECT_EQ(root_in_embedded->children().back(),
+ embedded->GetViewById(view11->id()));
+ }
+
+ {
+ view11->MoveToBack();
+ ASSERT_TRUE(WaitForOrderChange(embedded,
+ embedded->GetViewById(view11->id())));
+
+ EXPECT_EQ(root_in_embedded->children().front(),
+ embedded->GetViewById(view11->id()));
+ EXPECT_EQ(root_in_embedded->children().back(),
+ embedded->GetViewById(view12->id()));
+ }
+}
+
+namespace {
+
+class VisibilityChangeObserver : public ViewObserver {
+ public:
+ explicit VisibilityChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ ~VisibilityChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+ // Overridden from ViewObserver:
+ void OnViewVisibilityChanged(View* view) override {
+ EXPECT_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(VisibilityChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewManagerTest, Visible) {
+ View* view1 = window_manager()->CreateView();
+ view1->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view1);
+
+ // Embed another app and verify initial state.
+ ViewManager* embedded = Embed(window_manager(), view1);
+ ASSERT_NE(nullptr, embedded);
+ ASSERT_NE(nullptr, embedded->GetRoot());
+ View* embedded_root = embedded->GetRoot();
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+
+ // Change the visible state from the first connection and verify its mirrored
+ // correctly to the embedded app.
+ {
+ VisibilityChangeObserver observer(embedded_root);
+ view1->SetVisible(false);
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ }
+
+ EXPECT_FALSE(view1->visible());
+ EXPECT_FALSE(view1->IsDrawn());
+
+ EXPECT_FALSE(embedded_root->visible());
+ EXPECT_FALSE(embedded_root->IsDrawn());
+
+ // Make the node visible again.
+ {
+ VisibilityChangeObserver observer(embedded_root);
+ view1->SetVisible(true);
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ }
+
+ EXPECT_TRUE(view1->visible());
+ EXPECT_TRUE(view1->IsDrawn());
+
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+}
+
+namespace {
+
+class DrawnChangeObserver : public ViewObserver {
+ public:
+ explicit DrawnChangeObserver(View* view) : view_(view) {
+ view_->AddObserver(this);
+ }
+ ~DrawnChangeObserver() override { view_->RemoveObserver(this); }
+
+ private:
+ // Overridden from ViewObserver:
+ void OnViewDrawnChanged(View* view) override {
+ EXPECT_EQ(view, view_);
+ QuitRunLoop();
+ }
+
+ View* view_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(DrawnChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewManagerTest, Drawn) {
+ View* view1 = window_manager()->CreateView();
+ view1->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view1);
+
+ // Embed another app and verify initial state.
+ ViewManager* embedded = Embed(window_manager(), view1);
+ ASSERT_NE(nullptr, embedded);
+ ASSERT_NE(nullptr, embedded->GetRoot());
+ View* embedded_root = embedded->GetRoot();
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_TRUE(embedded_root->IsDrawn());
+
+ // Change the visibility of the root, this should propagate a drawn state
+ // change to |embedded|.
+ {
+ DrawnChangeObserver observer(embedded_root);
+ window_manager()->GetRoot()->SetVisible(false);
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ }
+
+ EXPECT_TRUE(view1->visible());
+ EXPECT_FALSE(view1->IsDrawn());
+
+ EXPECT_TRUE(embedded_root->visible());
+ EXPECT_FALSE(embedded_root->IsDrawn());
+}
+
+// TODO(beng): tests for view event dispatcher.
+// - verify that we see events for all views.
+
+namespace {
+
+class FocusChangeObserver : public ViewObserver {
+ public:
+ explicit FocusChangeObserver(View* view)
+ : view_(view), last_gained_focus_(nullptr), last_lost_focus_(nullptr) {
+ view_->AddObserver(this);
+ }
+ ~FocusChangeObserver() override { view_->RemoveObserver(this); }
+
+ View* last_gained_focus() { return last_gained_focus_; }
+
+ View* last_lost_focus() { return last_lost_focus_; }
+
+ private:
+ // Overridden from ViewObserver.
+ void OnViewFocusChanged(View* gained_focus, View* lost_focus) override {
+ last_gained_focus_ = gained_focus;
+ last_lost_focus_ = lost_focus;
+ QuitRunLoop();
+ }
+
+ View* view_;
+ View* last_gained_focus_;
+ View* last_lost_focus_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(FocusChangeObserver);
+};
+
+} // namespace
+
+TEST_F(ViewManagerTest, Focus) {
+ View* view1 = window_manager()->CreateView();
+ view1->SetVisible(true);
+ window_manager()->GetRoot()->AddChild(view1);
+
+ ViewManager* embedded = Embed(window_manager(), view1);
+ ASSERT_NE(nullptr, embedded);
+ View* view11 = embedded->CreateView();
+ view11->SetVisible(true);
+ embedded->GetRoot()->AddChild(view11);
+
+ // TODO(alhaad): Figure out why switching focus between views from different
+ // connections is causing the tests to crash and add tests for that.
+ {
+ View* embedded_root = embedded->GetRoot();
+ FocusChangeObserver observer(embedded_root);
+ embedded_root->SetFocus();
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ ASSERT_NE(nullptr, observer.last_gained_focus());
+ EXPECT_EQ(embedded_root->id(), observer.last_gained_focus()->id());
+ }
+ {
+ FocusChangeObserver observer(view11);
+ view11->SetFocus();
+ ASSERT_TRUE(DoRunLoopWithTimeout());
+ ASSERT_NE(nullptr, observer.last_gained_focus());
+ ASSERT_NE(nullptr, observer.last_lost_focus());
+ EXPECT_EQ(view11->id(), observer.last_gained_focus()->id());
+ EXPECT_EQ(embedded->GetRoot()->id(), observer.last_lost_focus()->id());
+ }
+}
+
+} // namespace mojo
« no previous file with comments | « mojo/services/view_manager/view_manager_app.cc ('k') | mojo/services/view_manager/view_manager_service_apptest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698