Adds ViewManager::Connect
I'm nuking SetRoots as it is no longer needed.
BUG=365012
TEST=covered by tests
[email protected]
Review URL: https://codereview.chromium.org/304503006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@273340 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi
index b5ab1ff5..398835e8 100644
--- a/mojo/mojo_services.gypi
+++ b/mojo/mojo_services.gypi
@@ -339,10 +339,11 @@
'../ui/aura/aura.gyp:aura',
'../ui/gfx/gfx.gyp:gfx_geometry',
'../ui/gl/gl.gyp:gl',
+ 'mojo_application',
'mojo_environment_chromium',
'mojo_geometry_bindings',
'mojo_geometry_lib',
- 'mojo_application',
+ 'mojo_service_manager',
'mojo_shell_test_support',
'mojo_system_impl',
'mojo_view_manager_bindings',
@@ -350,6 +351,8 @@
'mojo_view_manager_run_unittests',
],
'sources': [
+ 'services/view_manager/test_change_tracker.cc',
+ 'services/view_manager/test_change_tracker.h',
'services/view_manager/view_manager_connection_unittest.cc',
],
},
diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom
index d63f7ca..ef50c45 100644
--- a/mojo/services/public/interfaces/view_manager/view_manager.mojom
+++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -88,15 +88,16 @@
handle<shared_buffer> buffer,
uint32 buffer_size) => (bool success);
- // Sets the ids of the roots for the specified connection.
- // TODO(sky): this is temporary for testing. This needs to be conveyed at
- // creation time of a new connection.
- SetRoots(uint16 connection_id, uint32[] nodes) => (bool success);
+ // Connects to |url| creating a connection that has the roots |nodes|. Fails
+ // if |nodes| is empty or contains nodes that were not created by this
+ // connection.
+ Connect(string url, uint32[] nodes) => (bool success);
};
// Changes to nodes/views are not sent to the connection that originated the
// change. For example, if connection 1 attaches a view to a node (SetView())
// connection 1 does not receive OnNodeViewReplaced().
+[Client=IViewManager]
interface IViewManagerClient {
// Invoked once the connection has been established. |connection_id| is the id
// that uniquely identifies this connection. |next_server_change_id| is the
diff --git a/mojo/services/view_manager/DEPS b/mojo/services/view_manager/DEPS
index 81d1c97..78641ff 100644
--- a/mojo/services/view_manager/DEPS
+++ b/mojo/services/view_manager/DEPS
@@ -10,3 +10,9 @@
"+ui/gfx",
"+ui/gl",
]
+
+specific_include_rules = {
+ "view_manager_connection_unittest.cc": [
+ "+mojo/service_manager/service_manager.h",
+ ],
+}
diff --git a/mojo/services/view_manager/root_node_manager.cc b/mojo/services/view_manager/root_node_manager.cc
index eed3efb..37cda55d 100644
--- a/mojo/services/view_manager/root_node_manager.cc
+++ b/mojo/services/view_manager/root_node_manager.cc
@@ -5,6 +5,7 @@
#include "mojo/services/view_manager/root_node_manager.h"
#include "base/logging.h"
+#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
#include "mojo/services/view_manager/view_manager_connection.h"
#include "ui/aura/env.h"
@@ -35,7 +36,8 @@
}
RootNodeManager::RootNodeManager(ServiceProvider* service_provider)
- : next_connection_id_(1),
+ : service_provider_(service_provider),
+ next_connection_id_(1),
next_server_change_id_(1),
change_source_(kRootConnection),
is_processing_delete_node_(false),
@@ -44,6 +46,8 @@
}
RootNodeManager::~RootNodeManager() {
+ while (!connections_created_by_connect_.empty())
+ delete *(connections_created_by_connect_.begin());
// All the connections should have been destroyed.
DCHECK(connection_map_.empty());
}
@@ -61,6 +65,17 @@
void RootNodeManager::RemoveConnection(ViewManagerConnection* connection) {
connection_map_.erase(connection->id());
+ connections_created_by_connect_.erase(connection);
+}
+
+void RootNodeManager::Connect(const String& url,
+ const Array<TransportNodeId>& node_ids) {
+ MessagePipe pipe;
+ service_provider_->ConnectToService(url, pipe.handle1.Pass());
+ ViewManagerConnection* connection = new ViewManagerConnection(this);
+ connection->SetRoots(node_ids);
+ BindToPipe(connection, pipe.handle0.Pass());
+ connections_created_by_connect_.insert(connection);
}
ViewManagerConnection* RootNodeManager::GetConnection(
diff --git a/mojo/services/view_manager/root_node_manager.h b/mojo/services/view_manager/root_node_manager.h
index 3aed8bd4..001e3b2a 100644
--- a/mojo/services/view_manager/root_node_manager.h
+++ b/mojo/services/view_manager/root_node_manager.h
@@ -6,8 +6,10 @@
#define MOJO_SERVICES_VIEW_MANAGER_ROOT_NODE_MANAGER_H_
#include <map>
+#include <set>
#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/array.h"
#include "mojo/services/view_manager/ids.h"
#include "mojo/services/view_manager/node.h"
#include "mojo/services/view_manager/node_delegate.h"
@@ -65,6 +67,8 @@
void AddConnection(ViewManagerConnection* connection);
void RemoveConnection(ViewManagerConnection* connection);
+ void Connect(const String& url, const Array<TransportNodeId>& node_ids);
+
// Returns the connection by id.
ViewManagerConnection* GetConnection(TransportConnectionId connection_id);
@@ -129,6 +133,8 @@
Context context_;
+ ServiceProvider* service_provider_;
+
// ID to use for next ViewManagerConnection.
TransportConnectionId next_connection_id_;
@@ -148,6 +154,10 @@
// Root node.
Node root_;
+ // Set of ViewManagerConnections created by way of Connect(). These have to be
+ // explicitly destroyed.
+ std::set<ViewManagerConnection*> connections_created_by_connect_;
+
DISALLOW_COPY_AND_ASSIGN(RootNodeManager);
};
diff --git a/mojo/services/view_manager/test_change_tracker.cc b/mojo/services/view_manager/test_change_tracker.cc
new file mode 100644
index 0000000..bf8ff5c
--- /dev/null
+++ b/mojo/services/view_manager/test_change_tracker.cc
@@ -0,0 +1,228 @@
+// 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 "mojo/services/view_manager/test_change_tracker.h"
+
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/common/common_type_converters.h"
+#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
+#include "mojo/services/public/cpp/view_manager/util.h"
+
+namespace mojo {
+
+// TODO(sky): remove this when Darin is done with cleanup.
+template <typename T>
+class MOJO_COMMON_EXPORT TypeConverter<T, T> {
+ public:
+ static T ConvertFrom(T input, Buffer* buf) {
+ return input;
+ }
+ static T ConvertTo(T input) {
+ return input;
+ }
+
+ MOJO_ALLOW_IMPLICIT_TYPE_CONVERSION();
+};
+
+namespace view_manager {
+namespace service {
+
+std::string NodeIdToString(TransportNodeId id) {
+ return (id == 0) ? "null" :
+ base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
+}
+
+namespace {
+
+void INodesToTestNodes(const Array<INode>& data,
+ std::vector<TestNode>* test_nodes) {
+ for (size_t i = 0; i < data.size(); ++i) {
+ TestNode node;
+ node.parent_id = data[i].parent_id();
+ node.node_id = data[i].node_id();
+ node.view_id = data[i].view_id();
+ test_nodes->push_back(node);
+ }
+}
+
+std::string RectToString(const gfx::Rect& rect) {
+ return base::StringPrintf("%d,%d %dx%d", rect.x(), rect.y(), rect.width(),
+ rect.height());
+}
+
+std::string ChangeToDescription1(const Change& change) {
+ switch (change.type) {
+ case CHANGE_TYPE_CONNECTION_ESTABLISHED:
+ return "OnConnectionEstablished";
+
+ case CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED:
+ return base::StringPrintf(
+ "ServerChangeIdAdvanced %d", static_cast<int>(change.change_id));
+
+
+ case CHANGE_TYPE_NODE_BOUNDS_CHANGED:
+ return base::StringPrintf(
+ "BoundsChanged node=%s old_bounds=%s new_bounds=%s",
+ NodeIdToString(change.node_id).c_str(),
+ RectToString(change.bounds).c_str(),
+ RectToString(change.bounds2).c_str());
+
+ case CHANGE_TYPE_NODE_HIERARCHY_CHANGED:
+ return base::StringPrintf(
+ "HierarchyChanged change_id=%d node=%s new_parent=%s old_parent=%s",
+ static_cast<int>(change.change_id),
+ NodeIdToString(change.node_id).c_str(),
+ NodeIdToString(change.node_id2).c_str(),
+ NodeIdToString(change.node_id3).c_str());
+
+ case CHANGE_TYPE_NODE_DELETED:
+ return base::StringPrintf("NodeDeleted change_id=%d node=%s",
+ static_cast<int>(change.change_id),
+ NodeIdToString(change.node_id).c_str());
+
+ case CHANGE_TYPE_VIEW_DELETED:
+ return base::StringPrintf("ViewDeleted view=%s",
+ NodeIdToString(change.view_id).c_str());
+
+ case CHANGE_TYPE_VIEW_REPLACED:
+ return base::StringPrintf(
+ "ViewReplaced node=%s new_view=%s old_view=%s",
+ NodeIdToString(change.node_id).c_str(),
+ NodeIdToString(change.view_id).c_str(),
+ NodeIdToString(change.view_id2).c_str());
+ }
+ return std::string();
+}
+
+} // namespace
+
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes) {
+ std::vector<std::string> strings(changes.size());
+ for (size_t i = 0; i < changes.size(); ++i)
+ strings[i] = ChangeToDescription1(changes[i]);
+ return strings;
+}
+
+std::string ChangeNodeDescription(const std::vector<Change>& changes) {
+ if (changes.size() != 1)
+ return std::string();
+ std::vector<std::string> node_strings(changes[0].nodes.size());
+ for (size_t i = 0; i < changes[0].nodes.size(); ++i)
+ node_strings[i] = "[" + changes[0].nodes[i].ToString() + "]";
+ return JoinString(node_strings, ',');
+}
+
+Change::Change()
+ : type(CHANGE_TYPE_CONNECTION_ESTABLISHED),
+ connection_id(0),
+ change_id(0),
+ node_id(0),
+ node_id2(0),
+ node_id3(0),
+ view_id(0),
+ view_id2(0) {}
+
+Change::~Change() {
+}
+
+TestChangeTracker::TestChangeTracker()
+ : delegate_(NULL) {
+}
+
+TestChangeTracker::~TestChangeTracker() {
+}
+
+void TestChangeTracker::OnViewManagerConnectionEstablished(
+ TransportConnectionId connection_id,
+ TransportChangeId next_server_change_id,
+ const Array<INode>& nodes) {
+ Change change;
+ change.type = CHANGE_TYPE_CONNECTION_ESTABLISHED;
+ change.connection_id = connection_id;
+ change.change_id = next_server_change_id;
+ INodesToTestNodes(nodes, &change.nodes);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnServerChangeIdAdvanced(
+ TransportChangeId change_id) {
+ Change change;
+ change.type = CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED;
+ change.change_id = change_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeBoundsChanged(TransportNodeId node_id,
+ const Rect& old_bounds,
+ const Rect& new_bounds) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_BOUNDS_CHANGED;
+ change.node_id = node_id;
+ change.bounds = old_bounds;
+ change.bounds2 = new_bounds;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeHierarchyChanged(
+ TransportNodeId node_id,
+ TransportNodeId new_parent_id,
+ TransportNodeId old_parent_id,
+ TransportChangeId server_change_id,
+ const Array<INode>& nodes) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED;
+ change.node_id = node_id;
+ change.node_id2 = new_parent_id;
+ change.node_id3 = old_parent_id;
+ change.change_id = server_change_id;
+ INodesToTestNodes(nodes, &change.nodes);
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeDeleted(
+ TransportNodeId node_id,
+ TransportChangeId server_change_id) {
+ Change change;
+ change.type = CHANGE_TYPE_NODE_DELETED;
+ change.node_id = node_id;
+ change.change_id = server_change_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnViewDeleted(TransportViewId view_id) {
+ Change change;
+ change.type = CHANGE_TYPE_VIEW_DELETED;
+ change.view_id = view_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::OnNodeViewReplaced(TransportNodeId node_id,
+ TransportViewId new_view_id,
+ TransportViewId old_view_id) {
+ Change change;
+ change.type = CHANGE_TYPE_VIEW_REPLACED;
+ change.node_id = node_id;
+ change.view_id = new_view_id;
+ change.view_id2 = old_view_id;
+ AddChange(change);
+}
+
+void TestChangeTracker::AddChange(const Change& change) {
+ changes_.push_back(change);
+ if (delegate_)
+ delegate_->OnChangeAdded();
+}
+
+std::string TestNode::ToString() const {
+ return base::StringPrintf("node=%s parent=%s view=%s",
+ NodeIdToString(node_id).c_str(),
+ NodeIdToString(parent_id).c_str(),
+ NodeIdToString(view_id).c_str());
+}
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
diff --git a/mojo/services/view_manager/test_change_tracker.h b/mojo/services/view_manager/test_change_tracker.h
new file mode 100644
index 0000000..066017a
--- /dev/null
+++ b/mojo/services/view_manager/test_change_tracker.h
@@ -0,0 +1,122 @@
+// 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.
+
+#ifndef MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+#define MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
+
+#include <string>
+#include <vector>
+
+#include "mojo/public/cpp/bindings/array.h"
+#include "mojo/services/public/cpp/view_manager/view_manager_types.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "ui/gfx/rect.h"
+
+namespace mojo {
+namespace view_manager {
+namespace service {
+
+enum ChangeType {
+ CHANGE_TYPE_CONNECTION_ESTABLISHED,
+ CHANGE_TYPE_SERVER_CHANGE_ID_ADVANCED,
+ CHANGE_TYPE_NODE_BOUNDS_CHANGED,
+ CHANGE_TYPE_NODE_HIERARCHY_CHANGED,
+ CHANGE_TYPE_NODE_DELETED,
+ CHANGE_TYPE_VIEW_DELETED,
+ CHANGE_TYPE_VIEW_REPLACED,
+};
+
+struct TestNode {
+ // Returns a string description of this.
+ std::string ToString() const;
+
+ TransportNodeId parent_id;
+ TransportNodeId node_id;
+ TransportNodeId view_id;
+};
+
+// Tracks a call to IViewManagerClient. See the individual functions for the
+// fields that are used.
+struct Change {
+ Change();
+ ~Change();
+
+ ChangeType type;
+ TransportConnectionId connection_id;
+ TransportChangeId change_id;
+ std::vector<TestNode> nodes;
+ TransportNodeId node_id;
+ TransportNodeId node_id2;
+ TransportNodeId node_id3;
+ TransportViewId view_id;
+ TransportViewId view_id2;
+ gfx::Rect bounds;
+ gfx::Rect bounds2;
+};
+
+// Converts Changes to string descriptions.
+std::vector<std::string> ChangesToDescription1(
+ const std::vector<Change>& changes);
+
+// Returns a string description of |changes[0].nodes|. Returns an empty string
+// if change.size() != 1.
+std::string ChangeNodeDescription(const std::vector<Change>& changes);
+
+// TestChangeTracker is used to record IViewManagerClient functions. It notifies
+// a delegate any time a change is added.
+class TestChangeTracker {
+ public:
+ // Used to notify the delegate when a change is added. A change corresponds to
+ // a single IViewManagerClient function.
+ class Delegate {
+ public:
+ virtual void OnChangeAdded() = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ TestChangeTracker();
+ ~TestChangeTracker();
+
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+ std::vector<Change>* changes() { return &changes_; }
+
+ // Each of these functions generate a Change. There is one per
+ // IViewManagerClient function.
+ void OnViewManagerConnectionEstablished(
+ TransportConnectionId connection_id,
+ TransportChangeId next_server_change_id,
+ const Array<INode>& nodes);
+ void OnServerChangeIdAdvanced(TransportChangeId change_id);
+ void OnNodeBoundsChanged(TransportNodeId node_id,
+ const Rect& old_bounds,
+ const Rect& new_bounds);
+ void OnNodeHierarchyChanged(TransportNodeId node_id,
+ TransportNodeId new_parent_id,
+ TransportNodeId old_parent_id,
+ TransportChangeId server_change_id,
+ const Array<INode>& nodes);
+ void OnNodeDeleted(TransportNodeId node_id,
+ TransportChangeId server_change_id);
+ void OnViewDeleted(TransportViewId view_id);
+ void OnNodeViewReplaced(TransportNodeId node_id,
+ TransportViewId new_view_id,
+ TransportViewId old_view_id);
+
+ private:
+ void AddChange(const Change& change);
+
+ Delegate* delegate_;
+ std::vector<Change> changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestChangeTracker);
+};
+
+} // namespace service
+} // namespace view_manager
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_TEST_CHANGE_TRACKER_H_
diff --git a/mojo/services/view_manager/view_manager_connection.cc b/mojo/services/view_manager/view_manager_connection.cc
index 498dc39..112773d 100644
--- a/mojo/services/view_manager/view_manager_connection.cc
+++ b/mojo/services/view_manager/view_manager_connection.cc
@@ -87,6 +87,16 @@
return root_node_manager_->GetView(id);
}
+void ViewManagerConnection::SetRoots(const Array<TransportNodeId>& node_ids) {
+ DCHECK_EQ(0, id_); // Only valid before connection established.
+ NodeIdSet roots;
+ for (size_t i = 0; i < node_ids.size(); ++i) {
+ DCHECK(GetNode(NodeIdFromTransportId(node_ids[i])));
+ roots.insert(node_ids[i]);
+ }
+ roots_.swap(roots);
+}
+
void ViewManagerConnection::ProcessNodeBoundsChanged(
const Node* node,
const gfx::Rect& old_bounds,
@@ -188,6 +198,11 @@
client()->OnViewDeleted(ViewIdToTransportId(view));
}
+void ViewManagerConnection::OnConnectionError() {
+ // TODO(sky): figure out if need to cleanup here if this
+ // ViewManagerConnection is the result of a Connect().
+}
+
bool ViewManagerConnection::CanRemoveNodeFromParent(const Node* node) const {
if (!node)
return false;
@@ -248,6 +263,16 @@
(IsNodeDescendantOfRoots(node) || node->id().connection_id == id_);
}
+bool ViewManagerConnection::CanConnect(
+ const mojo::Array<uint32_t>& node_ids) const {
+ for (size_t i = 0; i < node_ids.size(); ++i) {
+ const Node* node = GetNode(NodeIdFromTransportId(node_ids[i]));
+ if (!node || node->id().connection_id != id_)
+ return false;
+ }
+ return node_ids.size() > 0;
+}
+
bool ViewManagerConnection::DeleteNodeImpl(ViewManagerConnection* source,
const NodeId& node_id) {
DCHECK_EQ(node_id.connection_id, id_);
@@ -385,40 +410,6 @@
return known_nodes_.count(NodeIdToTransportId(node->id())) > 0;
}
-bool ViewManagerConnection::ProcessSetRoots(
- TransportConnectionId source_connection_id,
- const Array<TransportNodeId>& transport_node_ids) {
- // TODO(sky): these DCHECKs can go away once this is part of a real API. Also
- // make sure that when roots are set nodes are communicate to client. Code in
- // ProcessNodeHierarchyChanged() is depending on this.
- DCHECK(node_map_.empty());
- DCHECK(view_map_.empty());
-
- NodeIdSet roots;
- for (size_t i = 0; i < transport_node_ids.size(); ++i) {
- const Node* node = GetNode(NodeIdFromTransportId(transport_node_ids[i]));
- // Only allow setting roots that are owned by the source connection.
- if (!node || node->id().connection_id != source_connection_id)
- return false;
- roots.insert(transport_node_ids[i]);
- }
- roots_.swap(roots);
-
- // TODO(sky): remove |known_nodes_.clear()| temporary while this is done here
- // instead of at creation time.
- known_nodes_.clear();
- std::vector<const Node*> to_send;
- for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i)
- GetUnknownNodesFrom(GetNode(NodeIdFromTransportId(*i)), &to_send);
- AllocationScope allocation_scope;
- client()->OnViewManagerConnectionEstablished(
- id_,
- root_node_manager_->next_server_change_id(),
- NodesToINodes(to_send));
-
- return true;
-}
-
Array<INode> ViewManagerConnection::NodesToINodes(
const std::vector<const Node*>& nodes) {
Array<INode>::Builder array_builder(nodes.size());
@@ -580,16 +571,6 @@
callback.Run(true);
}
-void ViewManagerConnection::SetRoots(
- TransportConnectionId connection_id,
- const Array<TransportNodeId>& transport_node_ids,
- const Callback<void(bool)>& callback) {
- ViewManagerConnection* connection =
- root_node_manager_->GetConnection(connection_id);
- callback.Run(connection &&
- connection->ProcessSetRoots(id_, transport_node_ids));
-}
-
void ViewManagerConnection::SetNodeBounds(
TransportNodeId node_id,
const Rect& bounds,
@@ -614,6 +595,15 @@
callback.Run(true);
}
+void ViewManagerConnection::Connect(const String& url,
+ const Array<uint32_t>& node_ids,
+ const Callback<void(bool)>& callback) {
+ const bool success = CanConnect(node_ids);
+ if (success)
+ root_node_manager_->Connect(url, node_ids);
+ callback.Run(success);
+}
+
void ViewManagerConnection::OnNodeHierarchyChanged(const Node* node,
const Node* new_parent,
const Node* old_parent) {
@@ -628,10 +618,19 @@
void ViewManagerConnection::OnConnectionEstablished() {
DCHECK_EQ(0, id_); // Should only get OnConnectionEstablished() once.
+
id_ = root_node_manager_->GetAndAdvanceNextConnectionId();
+
root_node_manager_->AddConnection(this);
+
std::vector<const Node*> to_send;
- GetUnknownNodesFrom(root_node_manager_->root(), &to_send);
+ if (roots_.empty()) {
+ GetUnknownNodesFrom(root_node_manager_->root(), &to_send);
+ } else {
+ for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i)
+ GetUnknownNodesFrom(GetNode(NodeIdFromTransportId(*i)), &to_send);
+ }
+
AllocationScope allocation_scope;
client()->OnViewManagerConnectionEstablished(
id_,
diff --git a/mojo/services/view_manager/view_manager_connection.h b/mojo/services/view_manager/view_manager_connection.h
index a8849b3..9ae7d50 100644
--- a/mojo/services/view_manager/view_manager_connection.h
+++ b/mojo/services/view_manager/view_manager_connection.h
@@ -40,7 +40,7 @@
: public InterfaceImpl<IViewManager>,
public NodeDelegate {
public:
- ViewManagerConnection(RootNodeManager* root_node_manager);
+ explicit ViewManagerConnection(RootNodeManager* root_node_manager);
virtual ~ViewManagerConnection();
TransportConnectionId id() const { return id_; }
@@ -59,6 +59,8 @@
}
const View* GetView(const ViewId& id) const;
+ void SetRoots(const Array<TransportNodeId>& node_ids);
+
// The following methods are invoked after the corresponding change has been
// processed. They do the appropriate bookkeeping and update the client as
// necessary.
@@ -80,6 +82,11 @@
bool originated_change);
void ProcessViewDeleted(const ViewId& view, bool originated_change);
+ // TODO(sky): move this to private section (currently can't because of
+ // bindings).
+ // InterfaceImp overrides:
+ virtual void OnConnectionError() MOJO_OVERRIDE;
+
private:
typedef std::map<TransportConnectionSpecificNodeId, Node*> NodeMap;
typedef std::map<TransportConnectionSpecificViewId, View*> ViewMap;
@@ -93,6 +100,7 @@
bool CanDeleteView(const ViewId& view_id) const;
bool CanSetView(const Node* node, const ViewId& view_id) const;
bool CanGetNodeTree(const Node* node) const;
+ bool CanConnect(const mojo::Array<uint32_t>& node_ids) const;
// Deletes a node owned by this connection. Returns true on success. |source|
// is the connection that originated the change.
@@ -125,9 +133,6 @@
const Node** old_parent,
std::vector<const Node*>* to_send);
- bool ProcessSetRoots(TransportConnectionId source_connection_id,
- const Array<TransportNodeId>& transport_node_ids);
-
// Converts an array of Nodes to INodes. This assumes all the nodes are valid
// for the client. The parent of nodes the client is not allowed to see are
// set to NULL (in the returned INodes).
@@ -160,13 +165,12 @@
ScopedSharedBufferHandle buffer,
uint32_t buffer_size,
const Callback<void(bool)>& callback) OVERRIDE;
- virtual void SetRoots(
- TransportConnectionId connection_id,
- const Array<TransportNodeId>& transport_node_ids,
- const Callback<void(bool)>& callback) OVERRIDE;
virtual void SetNodeBounds(TransportNodeId node_id,
const Rect& bounds,
const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void Connect(const mojo::String& url,
+ const mojo::Array<uint32_t>& node_ids,
+ const mojo::Callback<void(bool)>& callback) OVERRIDE;
// Overridden from NodeDelegate:
virtual void OnNodeHierarchyChanged(const Node* node,
diff --git a/mojo/services/view_manager/view_manager_connection_unittest.cc b/mojo/services/view_manager/view_manager_connection_unittest.cc
index 7153ed4..aaa78e5 100644
--- a/mojo/services/view_manager/view_manager_connection_unittest.cc
+++ b/mojo/services/view_manager/view_manager_connection_unittest.cc
@@ -7,17 +7,21 @@
#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "mojo/common/common_type_converters.h"
+#include "mojo/public/cpp/application/application.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/cpp/bindings/allocation_scope.h"
#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/service_manager/service_manager.h"
#include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
#include "mojo/services/public/cpp/view_manager/util.h"
#include "mojo/services/public/cpp/view_manager/view_manager_types.h"
#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/test_change_tracker.h"
#include "mojo/shell/shell_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/rect.h"
@@ -45,6 +49,353 @@
base::RunLoop* current_run_loop = NULL;
+const char kTestServiceURL[] = "mojo:test_url";
+
+void INodesToTestNodes(const Array<INode>& data,
+ std::vector<TestNode>* test_nodes) {
+ for (size_t i = 0; i < data.size(); ++i) {
+ TestNode node;
+ node.parent_id = data[i].parent_id();
+ node.node_id = data[i].node_id();
+ node.view_id = data[i].view_id();
+ test_nodes->push_back(node);
+ }
+}
+
+// BackgroundConnection is used when a child ViewManagerConnection is created by
+// way of Connect(). BackgroundConnection is created on a background thread (the
+// thread where the shell lives). All public methods are to be invoked on the
+// main thread. They wait for a result (by spinning a nested message loop),
+// calling through to background thread, then the underlying IViewManager* and
+// respond back to the calling thread to return control to the test.
+class BackgroundConnection : public TestChangeTracker::Delegate {
+ public:
+ BackgroundConnection(TestChangeTracker* tracker,
+ base::MessageLoop* loop)
+ : tracker_(tracker),
+ main_loop_(loop),
+ background_loop_(base::MessageLoop::current()),
+ view_manager_(NULL),
+ quit_count_(0) {
+ main_loop_->PostTask(FROM_HERE,
+ base::Bind(&BackgroundConnection::SetInstance, this));
+ }
+
+ virtual ~BackgroundConnection() {
+ instance_ = NULL;
+ }
+
+ void set_view_manager(IViewManager* view_manager) {
+ view_manager_ = view_manager;
+ }
+
+ // Runs a message loop until the single instance has been created.
+ static BackgroundConnection* WaitForInstance() {
+ if (!instance_)
+ RunMainLoop();
+ return instance_;
+ }
+
+ // Runs the main loop until |count| changes have been received.
+ std::vector<Change> DoRunLoopUntilChangesCount(size_t count) {
+ background_loop_->PostTask(FROM_HERE,
+ base::Bind(&BackgroundConnection::SetQuitCount,
+ base::Unretained(this), count));
+ // Run the current message loop. When the quit count is reached we'll quit.
+ RunMainLoop();
+ return changes_;
+ }
+
+ // The following functions mirror that of IViewManager. They bounce the
+ // function to the right thread and return the result.
+ bool CreateNode(TransportNodeId node_id) {
+ bool result = false;
+ background_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundConnection::CreateNodeOnBackgroundThread,
+ base::Unretained(this), node_id, &result));
+ RunMainLoop();
+ return result;
+ }
+ bool AddNode(TransportNodeId parent,
+ TransportNodeId child,
+ TransportChangeId server_change_id) {
+ bool result = false;
+ background_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundConnection::AddNodeOnBackgroundThread,
+ base::Unretained(this), parent, child, server_change_id,
+ &result));
+ RunMainLoop();
+ return result;
+ }
+ bool RemoveNodeFromParent(TransportNodeId node_id,
+ TransportChangeId server_change_id) {
+ bool result = false;
+ background_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BackgroundConnection::RemoveNodeFromParentOnBackgroundThread,
+ base::Unretained(this), node_id, server_change_id, &result));
+ RunMainLoop();
+ return result;
+ }
+ bool SetView(TransportNodeId node_id, TransportViewId view_id) {
+ bool result = false;
+ background_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BackgroundConnection::SetViewOnBackgroundThread,
+ base::Unretained(this), node_id, view_id, &result));
+ RunMainLoop();
+ return result;
+ }
+ bool CreateView(TransportViewId view_id) {
+ bool result = false;
+ background_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BackgroundConnection::CreateViewOnBackgroundThread,
+ base::Unretained(this), view_id, &result));
+ RunMainLoop();
+ return result;
+ }
+ void GetNodeTree(TransportNodeId node_id, std::vector<TestNode>* nodes) {
+ background_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &BackgroundConnection::GetNodeTreeOnBackgroundThread,
+ base::Unretained(this), node_id, nodes));
+ RunMainLoop();
+ }
+
+ private:
+ void SetQuitCount(size_t count) {
+ DCHECK_EQ(background_loop_, base::MessageLoop::current());
+ if (tracker_->changes()->size() >= count) {
+ QuitCountReached();
+ return;
+ }
+ quit_count_ = count - tracker_->changes()->size();
+ }
+
+ static void RunMainLoop() {
+ DCHECK(!main_run_loop_);
+ main_run_loop_ = new base::RunLoop;
+ main_run_loop_->Run();
+ delete main_run_loop_;
+ main_run_loop_ = NULL;
+ }
+
+ void QuitCountReached() {
+ std::vector<Change> changes;
+ tracker_->changes()->swap(changes);
+ main_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundConnection::QuitCountReachedOnMain,
+ base::Unretained(this), changes));
+ }
+
+ void QuitCountReachedOnMain(const std::vector<Change>& changes) {
+ changes_ = changes;
+ DCHECK(main_run_loop_);
+ main_run_loop_->Quit();
+ }
+
+ static void SetInstance(BackgroundConnection* instance) {
+ DCHECK(!instance_);
+ instance_ = instance;
+ if (main_run_loop_)
+ main_run_loop_->Quit();
+ }
+
+ // Callbacks from the various IViewManager functions.
+ void GotResultOnBackgroundThread(bool* result_cache, bool result) {
+ *result_cache = result;
+ main_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundConnection::QuitOnMainThread,
+ base::Unretained(this)));
+ }
+
+ void GotNodeTreeOnBackgroundThread(std::vector<TestNode>* nodes,
+ const Array<INode>& results) {
+ INodesToTestNodes(results, nodes);
+ main_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BackgroundConnection::QuitOnMainThread,
+ base::Unretained(this)));
+ }
+
+ void QuitOnMainThread() {
+ DCHECK(main_run_loop_);
+ main_run_loop_->Quit();
+ }
+
+ // The following functions correspond to the IViewManager functions. These run
+ // on the background thread.
+ void CreateNodeOnBackgroundThread(TransportNodeId node_id, bool* result) {
+ view_manager_->CreateNode(
+ node_id,
+ base::Bind(&BackgroundConnection::GotResultOnBackgroundThread,
+ base::Unretained(this), result));
+ }
+ void AddNodeOnBackgroundThread(TransportNodeId parent,
+ TransportNodeId child,
+ TransportChangeId server_change_id,
+ bool* result) {
+ view_manager_->AddNode(
+ parent, child, server_change_id,
+ base::Bind(&BackgroundConnection::GotResultOnBackgroundThread,
+ base::Unretained(this), result));
+ }
+ void RemoveNodeFromParentOnBackgroundThread(
+ TransportNodeId node_id,
+ TransportChangeId server_change_id,
+ bool* result) {
+ view_manager_->RemoveNodeFromParent(node_id, server_change_id,
+ base::Bind(&BackgroundConnection::GotResultOnBackgroundThread,
+ base::Unretained(this), result));
+ }
+ void SetViewOnBackgroundThread(TransportNodeId node_id,
+ TransportViewId view_id,
+ bool* result) {
+ view_manager_->SetView(node_id, view_id,
+ base::Bind(&BackgroundConnection::GotResultOnBackgroundThread,
+ base::Unretained(this), result));
+ }
+ void CreateViewOnBackgroundThread(TransportViewId view_id, bool* result) {
+ view_manager_->CreateView(view_id,
+ base::Bind(&BackgroundConnection::GotResultOnBackgroundThread,
+ base::Unretained(this), result));
+ }
+ void GetNodeTreeOnBackgroundThread(TransportNodeId node_id,
+ std::vector<TestNode>* nodes) {
+ view_manager_->GetNodeTree(node_id,
+ base::Bind(&BackgroundConnection::GotNodeTreeOnBackgroundThread,
+ base::Unretained(this), nodes));
+ }
+
+ // TestChangeTracker::Delegate:
+ virtual void OnChangeAdded() OVERRIDE {
+ if (quit_count_ > 0 && --quit_count_ == 0)
+ QuitCountReached();
+ }
+
+ static BackgroundConnection* instance_;
+ static base::RunLoop* main_run_loop_;
+
+ TestChangeTracker* tracker_;
+
+ // MessageLoop of the test.
+ base::MessageLoop* main_loop_;
+
+ // MessageLoop BackgroundConnection lives on.
+ base::MessageLoop* background_loop_;
+
+ IViewManager* view_manager_;
+
+ // Number of changes we're waiting on until we quit the current loop.
+ size_t quit_count_;
+
+ std::vector<Change> changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackgroundConnection);
+};
+
+// static
+BackgroundConnection* BackgroundConnection::instance_ = NULL;
+
+// static
+base::RunLoop* BackgroundConnection::main_run_loop_ = NULL;
+
+class TestViewManagerClientConnection
+ : public InterfaceImpl<IViewManagerClient> {
+ public:
+ explicit TestViewManagerClientConnection(base::MessageLoop* loop)
+ : connection_(&tracker_, loop) {
+ tracker_.set_delegate(&connection_);
+ }
+
+ // InterfaceImp:
+ virtual void OnConnectionEstablished() OVERRIDE {
+ connection_.set_view_manager(client());
+ }
+
+ // IViewMangerClient:
+ virtual void OnViewManagerConnectionEstablished(
+ TransportConnectionId connection_id,
+ TransportChangeId next_server_change_id,
+ const Array<INode>& nodes) OVERRIDE {
+ tracker_.OnViewManagerConnectionEstablished(
+ connection_id, next_server_change_id, nodes);
+ }
+ virtual void OnServerChangeIdAdvanced(
+ TransportChangeId next_server_change_id) OVERRIDE {
+ tracker_.OnServerChangeIdAdvanced(next_server_change_id);
+ }
+ virtual void OnNodeBoundsChanged(TransportNodeId node_id,
+ const Rect& old_bounds,
+ const Rect& new_bounds) OVERRIDE {
+ tracker_.OnNodeBoundsChanged(node_id, old_bounds, new_bounds);
+ }
+ virtual void OnNodeHierarchyChanged(
+ TransportNodeId node,
+ TransportNodeId new_parent,
+ TransportNodeId old_parent,
+ TransportChangeId server_change_id,
+ const Array<INode>& nodes) OVERRIDE {
+ tracker_.OnNodeHierarchyChanged(node, new_parent, old_parent,
+ server_change_id, nodes);
+ }
+ virtual void OnNodeDeleted(TransportNodeId node,
+ TransportChangeId server_change_id) OVERRIDE {
+ tracker_.OnNodeDeleted(node, server_change_id);
+ }
+ virtual void OnViewDeleted(TransportViewId view) OVERRIDE {
+ tracker_.OnViewDeleted(view);
+ }
+ virtual void OnNodeViewReplaced(TransportNodeId node,
+ TransportViewId new_view_id,
+ TransportViewId old_view_id) OVERRIDE {
+ tracker_.OnNodeViewReplaced(node, new_view_id, old_view_id);
+ }
+
+ private:
+ TestChangeTracker tracker_;
+ BackgroundConnection connection_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestViewManagerClientConnection);
+};
+
+// Used with IViewManager::Connect(). Creates a TestViewManagerClientConnection,
+// which creates and owns the BackgroundConnection.
+class ConnectServiceLoader : public ServiceLoader {
+ public:
+ ConnectServiceLoader() : initial_loop_(base::MessageLoop::current()) {
+ }
+ virtual ~ConnectServiceLoader() {
+ }
+
+ // ServiceLoader:
+ virtual void LoadService(ServiceManager* manager,
+ const GURL& url,
+ ScopedMessagePipeHandle shell_handle) OVERRIDE {
+ scoped_ptr<Application> app(new Application(shell_handle.Pass()));
+ app->AddService<TestViewManagerClientConnection>(initial_loop_);
+ apps_.push_back(app.release());
+ }
+ virtual void OnServiceError(ServiceManager* manager,
+ const GURL& url) OVERRIDE {
+ }
+
+ private:
+ base::MessageLoop* initial_loop_;
+ ScopedVector<Application> apps_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConnectServiceLoader);
+};
+
// Sets |current_run_loop| and runs it. It is expected that someone else quits
// the loop.
void DoRunLoop() {
@@ -56,21 +407,6 @@
current_run_loop = NULL;
}
-// Converts |id| into a string.
-std::string NodeIdToString(TransportNodeId id) {
- return (id == 0) ? "null" :
- base::StringPrintf("%d,%d", HiWord(id), LoWord(id));
-}
-
-// Converts |rect| into a string.
-std::string RectToString(const Rect& rect) {
- return base::StringPrintf("%d,%d %dx%d",
- rect.position().x(),
- rect.position().y(),
- rect.size().width(),
- rect.size().height());
-}
-
// Boolean callback. Sets |result_cache| to the value of |result| and quits
// the run loop.
void BooleanCallback(bool* result_cache, bool result) {
@@ -78,34 +414,10 @@
current_run_loop->Quit();
}
-struct TestNode {
- std::string ToString() const {
- return base::StringPrintf("node=%s parent=%s view=%s",
- NodeIdToString(node_id).c_str(),
- NodeIdToString(parent_id).c_str(),
- NodeIdToString(view_id).c_str());
- }
-
- TransportNodeId parent_id;
- TransportNodeId node_id;
- TransportNodeId view_id;
-};
-
-void INodesToTestNodes(const mojo::Array<INode>& data,
- std::vector<TestNode>* test_nodes) {
- for (size_t i = 0; i < data.size(); ++i) {
- TestNode node;
- node.parent_id = data[i].parent_id();
- node.node_id = data[i].node_id();
- node.view_id = data[i].view_id();
- test_nodes->push_back(node);
- }
-}
-
// Callback that results in a vector of INodes. The INodes are converted to
// TestNodes.
void INodesCallback(std::vector<TestNode>* test_nodes,
- const mojo::Array<INode>& data) {
+ const Array<INode>& data) {
INodesToTestNodes(data, test_nodes);
current_run_loop->Quit();
}
@@ -214,13 +526,18 @@
return result;
}
-bool SetRoots(IViewManager* view_manager,
- TransportConnectionId connection_id,
- const std::vector<uint32_t>& node_ids) {
+bool Connect(IViewManager* view_manager,
+ const std::string& url,
+ TransportNodeId id,
+ TransportNodeId id2) {
+ AllocationScope scope;
bool result = false;
- view_manager->SetRoots(connection_id,
- Array<uint32_t>::From(node_ids),
- base::Bind(&BooleanCallback, &result));
+ std::vector<TransportNodeId> node_ids;
+ node_ids.push_back(id);
+ if (id2 != 0)
+ node_ids.push_back(id2);
+ view_manager->Connect(url, Array<uint32_t>::From(node_ids),
+ base::Bind(&BooleanCallback, &result));
DoRunLoop();
return result;
}
@@ -229,12 +546,40 @@
typedef std::vector<std::string> Changes;
+class MainLoopTrackerDelegate : public TestChangeTracker::Delegate {
+ public:
+ explicit MainLoopTrackerDelegate(TestChangeTracker* tracker)
+ : tracker_(tracker),
+ quit_count_(0) {}
+
+ void DoRunLoopUntilChangesCount(size_t count) {
+ if (tracker_->changes()->size() >= count)
+ return;
+ quit_count_ = count - tracker_->changes()->size();
+ DoRunLoop();
+ }
+
+ // TestChangeTracker::Delegate:
+ virtual void OnChangeAdded() OVERRIDE {
+ if (quit_count_ > 0 && --quit_count_ == 0)
+ current_run_loop->Quit();
+ }
+
+ private:
+ TestChangeTracker* tracker_;
+ size_t quit_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(MainLoopTrackerDelegate);
+};
+
class ViewManagerClientImpl : public IViewManagerClient {
public:
ViewManagerClientImpl()
: id_(0),
next_server_change_id_(0),
- quit_count_(0) {}
+ main_loop_tracker_delegate_(&tracker_) {
+ tracker_.set_delegate(&main_loop_tracker_delegate_);
+ }
TransportConnectionId id() const { return id_; }
@@ -249,8 +594,8 @@
}
Changes GetAndClearChanges() {
- Changes changes;
- changes.swap(changes_);
+ Changes changes = ChangesToDescription1(*tracker_.changes());
+ tracker_.changes()->clear();
return changes;
}
@@ -264,10 +609,7 @@
}
void DoRunLoopUntilChangesCount(size_t count) {
- if (changes_.size() >= count)
- return;
- quit_count_ = count - changes_.size();
- DoRunLoop();
+ main_loop_tracker_delegate_.DoRunLoopUntilChangesCount(count);
}
private:
@@ -275,103 +617,65 @@
virtual void OnViewManagerConnectionEstablished(
TransportConnectionId connection_id,
TransportChangeId next_server_change_id,
- const mojo::Array<INode>& nodes) OVERRIDE {
+ const Array<INode>& nodes) OVERRIDE {
id_ = connection_id;
next_server_change_id_ = next_server_change_id;
initial_nodes_.clear();
INodesToTestNodes(nodes, &initial_nodes_);
- changes_.push_back("OnConnectionEstablished");
- QuitIfNecessary();
+ tracker_.OnViewManagerConnectionEstablished(
+ connection_id, next_server_change_id, nodes);
}
virtual void OnServerChangeIdAdvanced(
- uint32_t next_server_change_id) OVERRIDE {
- changes_.push_back(
- base::StringPrintf(
- "ServerChangeIdAdvanced %d",
- static_cast<int>(next_server_change_id)));
- QuitIfNecessary();
+ TransportChangeId next_server_change_id) OVERRIDE {
+ tracker_.OnServerChangeIdAdvanced(next_server_change_id);
}
virtual void OnNodeBoundsChanged(TransportNodeId node_id,
const Rect& old_bounds,
const Rect& new_bounds) OVERRIDE {
- changes_.push_back(
- base::StringPrintf(
- "BoundsChanged node=%s old_bounds=%s new_bounds=%s",
- NodeIdToString(node_id).c_str(),
- RectToString(old_bounds).c_str(),
- RectToString(new_bounds).c_str()));
- QuitIfNecessary();
+ tracker_.OnNodeBoundsChanged(node_id, old_bounds, new_bounds);
}
virtual void OnNodeHierarchyChanged(
TransportNodeId node,
TransportNodeId new_parent,
TransportNodeId old_parent,
TransportChangeId server_change_id,
- const mojo::Array<INode>& nodes) OVERRIDE {
- changes_.push_back(
- base::StringPrintf(
- "HierarchyChanged change_id=%d node=%s new_parent=%s old_parent=%s",
- static_cast<int>(server_change_id),
- NodeIdToString(node).c_str(),
- NodeIdToString(new_parent).c_str(),
- NodeIdToString(old_parent).c_str()));
+ const Array<INode>& nodes) OVERRIDE {
+ tracker_.OnNodeHierarchyChanged(node, new_parent, old_parent,
+ server_change_id, nodes);
hierarchy_changed_nodes_.clear();
INodesToTestNodes(nodes, &hierarchy_changed_nodes_);
- QuitIfNecessary();
}
virtual void OnNodeDeleted(TransportNodeId node,
TransportChangeId server_change_id) OVERRIDE {
- changes_.push_back(
- base::StringPrintf(
- "NodeDeleted change_id=%d node=%s",
- static_cast<int>(server_change_id),
- NodeIdToString(node).c_str()));
- QuitIfNecessary();
+ tracker_.OnNodeDeleted(node, server_change_id);
}
virtual void OnViewDeleted(TransportViewId view) OVERRIDE {
- changes_.push_back(
- base::StringPrintf(
- "ViewDeleted view=%s",
- NodeIdToString(view).c_str()));
- QuitIfNecessary();
+ tracker_.OnViewDeleted(view);
}
virtual void OnNodeViewReplaced(TransportNodeId node,
TransportViewId new_view_id,
TransportViewId old_view_id) OVERRIDE {
- changes_.push_back(
- base::StringPrintf(
- "ViewReplaced node=%s new_view=%s old_view=%s",
- NodeIdToString(node).c_str(),
- NodeIdToString(new_view_id).c_str(),
- NodeIdToString(old_view_id).c_str()));
- QuitIfNecessary();
- }
-
- void QuitIfNecessary() {
- if (quit_count_ > 0 && --quit_count_ == 0)
- current_run_loop->Quit();
+ tracker_.OnNodeViewReplaced(node, new_view_id, old_view_id);
}
TransportConnectionId id_;
TransportChangeId next_server_change_id_;
- // Used to determine when/if to quit the run loop.
- size_t quit_count_;
-
- Changes changes_;
-
// Set of nodes sent when connection created.
std::vector<TestNode> initial_nodes_;
// Nodes sent from last OnNodeHierarchyChanged.
std::vector<TestNode> hierarchy_changed_nodes_;
+ TestChangeTracker tracker_;
+ MainLoopTrackerDelegate main_loop_tracker_delegate_;
+
DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
};
class ViewManagerConnectionTest : public testing::Test {
public:
- ViewManagerConnectionTest() {}
+ ViewManagerConnectionTest() : background_connection_(NULL) {}
virtual void SetUp() OVERRIDE {
test_helper_.Init();
@@ -383,6 +687,10 @@
client_.WaitForId();
client_.GetAndClearChanges();
+
+ test_helper_.SetLoaderForURL(
+ scoped_ptr<ServiceLoader>(new ConnectServiceLoader()),
+ GURL(kTestServiceURL));
}
protected:
@@ -397,22 +705,22 @@
client2_.GetAndClearChanges();
}
- void EstablishSecondConnectionWithRoot(TransportNodeId root_id) {
- EstablishSecondConnection();
- client2_.ClearId();
+ std::vector<Change> EstablishBackgroundConnectionWithRoots(
+ TransportNodeId id1,
+ TransportNodeId id2) {
+ Connect(view_manager_.get(), kTestServiceURL, id1, id2);
+ background_connection_ = BackgroundConnection::WaitForInstance();
+ return background_connection_->DoRunLoopUntilChangesCount(1);
+ }
- AllocationScope scope;
- std::vector<uint32_t> roots;
- roots.push_back(root_id);
- ASSERT_TRUE(SetRoots(view_manager_.get(), 2, roots));
- client2_.DoRunLoopUntilChangesCount(1);
- Changes changes(client2_.GetAndClearChanges());
+ void EstablishBackgroundConnectionWithRoot1() {
+ std::vector<Change> changes(
+ EstablishBackgroundConnectionWithRoots(CreateNodeId(1, 1), 0));
+ ASSERT_NO_FATAL_FAILURE();
ASSERT_EQ(1u, changes.size());
- EXPECT_EQ("OnConnectionEstablished", changes[0]);
- ASSERT_NE(0u, client2_.id());
- const std::vector<TestNode>& nodes(client2_.initial_nodes());
- ASSERT_EQ(1u, nodes.size());
- EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString());
+ EXPECT_EQ("OnConnectionEstablished", ChangesToDescription1(changes)[0]);
+ EXPECT_EQ("[node=1,1 parent=null view=null]",
+ ChangeNodeDescription(changes));
}
void DestroySecondConnection() {
@@ -428,6 +736,8 @@
ViewManagerClientImpl client2_;
IViewManagerPtr view_manager2_;
+ BackgroundConnection* background_connection_;
+
DISALLOW_COPY_AND_ASSIGN(ViewManagerConnectionTest);
};
@@ -1222,23 +1532,15 @@
1));
// Establish the second connection and give it the roots 1 and 3.
- EstablishSecondConnection();
- client2_.ClearId();
{
- AllocationScope scope;
- std::vector<uint32_t> roots;
- roots.push_back(CreateNodeId(1, 1));
- roots.push_back(CreateNodeId(1, 3));
- ASSERT_TRUE(SetRoots(view_manager_.get(), 2, roots));
- client2_.DoRunLoopUntilChangesCount(1);
- Changes changes(client2_.GetAndClearChanges());
+ std::vector<Change> changes(EstablishBackgroundConnectionWithRoots(
+ CreateNodeId(1, 1), CreateNodeId(1, 3)));
+ ASSERT_NO_FATAL_FAILURE();
ASSERT_EQ(1u, changes.size());
- EXPECT_EQ("OnConnectionEstablished", changes[0]);
- ASSERT_NE(0u, client2_.id());
- const std::vector<TestNode>& nodes(client2_.initial_nodes());
- ASSERT_EQ(2u, nodes.size());
- EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString());
- EXPECT_EQ("node=1,3 parent=null view=null", nodes[1].ToString());
+ EXPECT_EQ("OnConnectionEstablished", ChangesToDescription1(changes)[0]);
+ EXPECT_EQ("[node=1,1 parent=null view=null],"
+ "[node=1,3 parent=null view=null]",
+ ChangeNodeDescription(changes));
}
// Create 4 and add it to the root, connection 2 should only get id advanced.
@@ -1248,8 +1550,8 @@
CreateNodeId(0, 1),
CreateNodeId(client_.id(), 4),
2));
- client2_.DoRunLoopUntilChangesCount(1);
- Changes changes(client2_.GetAndClearChanges());
+ Changes changes = ChangesToDescription1(
+ background_connection_->DoRunLoopUntilChangesCount(1));
ASSERT_EQ(1u, changes.size());
EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]);
}
@@ -1260,15 +1562,15 @@
CreateNodeId(1, 3),
CreateNodeId(1, 4),
3));
- client2_.DoRunLoopUntilChangesCount(1);
- Changes changes(client2_.GetAndClearChanges());
- ASSERT_EQ(1u, changes.size());
+ std::vector<Change> changes =
+ background_connection_->DoRunLoopUntilChangesCount(1);
+ Changes change_strings(ChangesToDescription1(changes));
+ ASSERT_EQ(1u, change_strings.size());
EXPECT_EQ(
"HierarchyChanged change_id=3 node=1,4 new_parent=1,3 "
- "old_parent=null", changes[0]);
- const std::vector<TestNode>& nodes(client2_.hierarchy_changed_nodes());
- ASSERT_EQ(1u, nodes.size());
- EXPECT_EQ("node=1,4 parent=1,3 view=null", nodes[0].ToString());
+ "old_parent=null", change_strings[0]);
+ EXPECT_EQ("[node=1,4 parent=1,3 view=null]",
+ ChangeNodeDescription(changes));
}
// Move 4 under 2, since 2 isn't a root client should get a delete.
@@ -1277,8 +1579,8 @@
CreateNodeId(1, 2),
CreateNodeId(1, 4),
4));
- client2_.DoRunLoopUntilChangesCount(1);
- Changes changes(client2_.GetAndClearChanges());
+ Changes changes = ChangesToDescription1(
+ background_connection_->DoRunLoopUntilChangesCount(1));
ASSERT_EQ(1u, changes.size());
EXPECT_EQ("NodeDeleted change_id=4 node=1,4", changes[0]);
}
@@ -1289,8 +1591,8 @@
ASSERT_TRUE(DeleteNode(view_manager_.get(), CreateNodeId(client_.id(), 4)));
ASSERT_TRUE(client_.GetAndClearChanges().empty());
- client2_.DoRunLoopUntilChangesCount(1);
- Changes changes(client2_.GetAndClearChanges());
+ Changes changes = ChangesToDescription1(
+ background_connection_->DoRunLoopUntilChangesCount(1));
ASSERT_EQ(1u, changes.size());
EXPECT_EQ("ServerChangeIdAdvanced 6", changes[0]);
}
@@ -1302,26 +1604,19 @@
ASSERT_TRUE(CreateNode(view_manager_.get(), 1, 1));
ASSERT_TRUE(CreateNode(view_manager_.get(), 1, 2));
- // Establish the second connection and give it the root 1.
- ASSERT_NO_FATAL_FAILURE(
- EstablishSecondConnectionWithRoot(CreateNodeId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishBackgroundConnectionWithRoot1());
// Try to move 2 to be a child of 1 from connection 2. This should fail as 2
// should not be able to access 1.
- ASSERT_FALSE(AddNode(view_manager2_.get(),
- CreateNodeId(1, 1),
- CreateNodeId(1, 2),
- 1));
+ ASSERT_FALSE(background_connection_->AddNode(
+ CreateNodeId(1, 1), CreateNodeId(1, 2), 1));
// Try to reparent 1 to the root. A connection is not allowed to reparent its
// roots.
- ASSERT_FALSE(AddNode(view_manager2_.get(),
- CreateNodeId(0, 1),
- CreateNodeId(1, 1),
- 1));
+ ASSERT_FALSE(background_connection_->AddNode(CreateNodeId(0, 1),
+ CreateNodeId(1, 1), 1));
}
-
// Verify RemoveNodeFromParent fails for nodes that are descendants of the
// roots.
TEST_F(ViewManagerConnectionTest, CantRemoveNodesInOtherRoots) {
@@ -1339,30 +1634,24 @@
2));
// Establish the second connection and give it the root 1.
- ASSERT_NO_FATAL_FAILURE(
- EstablishSecondConnectionWithRoot(CreateNodeId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishBackgroundConnectionWithRoot1());
// Connection 2 should not be able to remove node 2 or 1 from its parent.
- ASSERT_FALSE(RemoveNodeFromParent(view_manager2_.get(),
- CreateNodeId(1, 2),
- 3));
- ASSERT_FALSE(RemoveNodeFromParent(view_manager2_.get(),
- CreateNodeId(1, 1),
- 3));
+ ASSERT_FALSE(background_connection_->RemoveNodeFromParent(
+ CreateNodeId(1, 2), 3));
+ ASSERT_FALSE(background_connection_->RemoveNodeFromParent(CreateNodeId(1, 1),
+ 3));
// Create nodes 10 and 11 in 2.
- ASSERT_TRUE(CreateNode(view_manager2_.get(), 2, 10));
- ASSERT_TRUE(CreateNode(view_manager2_.get(), 2, 11));
+ ASSERT_TRUE(background_connection_->CreateNode(CreateNodeId(2, 10)));
+ ASSERT_TRUE(background_connection_->CreateNode(CreateNodeId(2, 11)));
// Parent 11 to 10.
- ASSERT_TRUE(AddNode(view_manager2_.get(),
- CreateNodeId(client2_.id(), 10),
- CreateNodeId(client2_.id(), 11),
- 3));
+ ASSERT_TRUE(background_connection_->AddNode(CreateNodeId(2, 10),
+ CreateNodeId(2, 11), 3));
// Remove 11 from 10.
- ASSERT_TRUE(RemoveNodeFromParent(view_manager2_.get(),
- CreateNodeId(2, 11),
- 4));
+ ASSERT_TRUE(background_connection_->RemoveNodeFromParent(
+ CreateNodeId(2, 11), 4));
// Verify nothing was actually removed.
{
@@ -1391,21 +1680,17 @@
CreateNodeId(client_.id(), 2),
2));
- // Establish the second connection and give it the root 1.
- ASSERT_NO_FATAL_FAILURE(
- EstablishSecondConnectionWithRoot(CreateNodeId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishBackgroundConnectionWithRoot1());
// Create a view in the second connection.
- ASSERT_TRUE(CreateView(view_manager2_.get(), 2, 51));
+ ASSERT_TRUE(background_connection_->CreateView(CreateViewId(2, 51)));
// Connection 2 should be able to set the view on node 1 (it's root), but not
// on 2.
- ASSERT_TRUE(SetView(view_manager2_.get(),
- CreateNodeId(client_.id(), 1),
- CreateViewId(client2_.id(), 51)));
- ASSERT_FALSE(SetView(view_manager2_.get(),
- CreateNodeId(client_.id(), 2),
- CreateViewId(client2_.id(), 51)));
+ ASSERT_TRUE(background_connection_->SetView(CreateNodeId(client_.id(), 1),
+ CreateViewId(2, 51)));
+ ASSERT_FALSE(background_connection_->SetView(CreateNodeId(client_.id(), 2),
+ CreateViewId(2, 51)));
}
// Verify GetNodeTree fails for nodes that are not descendants of the roots.
@@ -1423,27 +1708,38 @@
CreateNodeId(client_.id(), 2),
2));
- // Establish the second connection and give it the root 1.
- ASSERT_NO_FATAL_FAILURE(
- EstablishSecondConnectionWithRoot(CreateNodeId(1, 1)));
+ ASSERT_NO_FATAL_FAILURE(EstablishBackgroundConnectionWithRoot1());
AllocationScope scope;
std::vector<TestNode> nodes;
// Should get nothing for the root.
- GetNodeTree(view_manager2_.get(), CreateNodeId(0, 1), &nodes);
+ background_connection_->GetNodeTree(CreateNodeId(0, 1), &nodes);
ASSERT_TRUE(nodes.empty());
// Should get nothing for node 2.
- GetNodeTree(view_manager2_.get(), CreateNodeId(1, 2), &nodes);
+ background_connection_->GetNodeTree(CreateNodeId(1, 2), &nodes);
ASSERT_TRUE(nodes.empty());
// Should get node 1 if asked for.
- GetNodeTree(view_manager2_.get(), CreateNodeId(1, 1), &nodes);
+ background_connection_->GetNodeTree(CreateNodeId(1, 1), &nodes);
ASSERT_EQ(1u, nodes.size());
EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString());
}
+// Verify GetNodeTree fails for nodes that are not descendants of the roots.
+TEST_F(ViewManagerConnectionTest, Connect) {
+ ASSERT_TRUE(CreateNode(view_manager_.get(), 1, 1));
+ ASSERT_TRUE(Connect(view_manager_.get(), kTestServiceURL, CreateNodeId(1, 1),
+ 0));
+ BackgroundConnection* instance = BackgroundConnection::WaitForInstance();
+ ASSERT_TRUE(instance != NULL);
+ Changes changes(
+ ChangesToDescription1(instance->DoRunLoopUntilChangesCount(1)));;
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("OnConnectionEstablished", changes[0]);
+}
+
// TODO(sky): add coverage of test that destroys connections and ensures other
// connections get deletion notification (or advanced server id).
diff --git a/mojo/shell/shell_test_helper.cc b/mojo/shell/shell_test_helper.cc
index a198e15..4b8f0b8 100644
--- a/mojo/shell/shell_test_helper.cc
+++ b/mojo/shell/shell_test_helper.cc
@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#include "mojo/service_manager/service_loader.h"
#include "mojo/shell/context.h"
#include "mojo/shell/init.h"
@@ -31,6 +32,12 @@
state->service_provider_handle = state->test_api->GetServiceProviderHandle();
}
+void SetLoaderForURLOnShellThread(ShellTestHelper::State* state,
+ scoped_ptr<ServiceLoader> loader,
+ const GURL& url) {
+ state->context->service_manager()->SetLoaderForURL(loader.Pass(), url);
+}
+
} // namespace
class ShellTestHelper::TestServiceProvider : public ServiceProvider {
@@ -79,6 +86,14 @@
run_loop_->Run();
}
+void ShellTestHelper::SetLoaderForURL(scoped_ptr<ServiceLoader> loader,
+ const GURL& url) {
+ service_provider_thread_.message_loop()->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&SetLoaderForURLOnShellThread, state_, base::Passed(&loader),
+ url));
+}
+
void ShellTestHelper::OnServiceProviderStarted() {
DCHECK(state_);
local_service_provider_.reset(new TestServiceProvider);
diff --git a/mojo/shell/shell_test_helper.h b/mojo/shell/shell_test_helper.h
index ab495cb..56ed4b1 100644
--- a/mojo/shell/shell_test_helper.h
+++ b/mojo/shell/shell_test_helper.h
@@ -12,12 +12,17 @@
#include "mojo/public/cpp/environment/environment.h"
#include "mojo/public/interfaces/service_provider/service_provider.mojom.h"
+class GURL;
+
namespace base {
class MessageLoopProxy;
class RunLoop;
}
namespace mojo {
+
+class ServiceLoader;
+
namespace shell {
// ShellTestHelper is useful for tests to establish a connection to the
@@ -37,6 +42,10 @@
// ServiceProvider.
ServiceProvider* service_provider() { return service_provider_.get(); }
+ // Sets a ServiceLoader for the specified URL. |loader| is ultimately used on
+ // the thread this class spawns.
+ void SetLoaderForURL(scoped_ptr<ServiceLoader> loader, const GURL& url);
+
private:
class TestServiceProvider;