blob: 797da44dcc33139d1bec19636435f6c2a089013c [file] [log] [blame]
// 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 "base/bind.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "mojo/common/common_type_converters.h"
#include "mojo/public/cpp/application/application_impl.h"
#include "mojo/public/cpp/application/application_test_base.h"
#include "services/reaper/diagnostics.mojom.h"
#include "services/reaper/reaper.mojom.h"
namespace reaper {
namespace {
template <typename T>
void Ping(T* service) {
base::RunLoop run_loop;
(*service)
->Ping(base::Bind(&base::RunLoop::Quit, base::Unretained(&run_loop)));
run_loop.Run();
}
class ReaperAppTest : public mojo::test::ApplicationTestBase {
public:
ReaperAppTest() : ApplicationTestBase() {}
~ReaperAppTest() override {}
void SetUp() override {
mojo::test::ApplicationTestBase::SetUp();
application_impl()->ConnectToService("mojo:reaper", &reaper_);
application_impl()->ConnectToService("mojo:reaper", &diagnostics_);
diagnostics_->Reset();
Ping(&diagnostics_);
}
protected:
ReaperPtr reaper_;
DiagnosticsPtr diagnostics_;
DISALLOW_COPY_AND_ASSIGN(ReaperAppTest);
};
struct NodeCatcher {
NodeCatcher(mojo::Array<NodePtr>* nodes) : nodes(nodes) {}
void Run(mojo::Array<NodePtr> nodes) const { *(this->nodes) = nodes.Pass(); }
mojo::Array<NodePtr>* nodes;
};
struct SecretCatcher {
SecretCatcher(uint64* secret) : secret(secret) {}
void Run(uint64 secret) const { *(this->secret) = secret; }
uint64* secret;
};
NodePtr CreateNode(const std::string& app_url,
uint32 node_id,
const std::string& other_app_url,
uint32 other_node_id,
bool is_source) {
NodePtr result(Node::New());
result->app_url = app_url;
result->node_id = node_id;
result->other_app_url = other_app_url;
result->other_id = other_node_id;
result->is_source = is_source;
return result.Pass();
}
bool CompareNodes(const NodePtr* a, const NodePtr* b) {
if ((*a)->app_url < (*b)->app_url)
return true;
else if ((*a)->app_url == (*b)->app_url && (*a)->node_id < (*b)->node_id)
return true;
else
return false;
}
std::vector<const NodePtr*> GetNodeVec(const mojo::Array<NodePtr>& node_arr) {
std::vector<const NodePtr*> result;
for (size_t i = 0; i < node_arr.size(); ++i)
result.push_back(&node_arr[i]);
std::sort(result.begin(), result.end(), CompareNodes);
return result;
}
void AddReference(mojo::Array<NodePtr>* nodes,
const std::string& source_app,
uint32 source_node_id,
const std::string& dest_app,
uint32 dest_node_id) {
nodes->push_back(CreateNode(source_app, source_node_id, dest_app,
dest_node_id, true).Pass());
nodes->push_back(CreateNode(dest_app, dest_node_id, source_app,
source_node_id, false).Pass());
}
void DumpNodes(DiagnosticsPtr* diagnostics, mojo::Array<NodePtr>* nodes) {
nodes->reset();
(*diagnostics)->DumpNodes(NodeCatcher(nodes));
Ping(diagnostics);
}
void ExpectEqual(const std::vector<const NodePtr*>& expected,
const std::vector<const NodePtr*>& actual,
const std::string& message) {
EXPECT_EQ(expected.size(), actual.size()) << message;
for (size_t i = 0; i < expected.size(); ++i) {
const NodePtr& ex = *expected[i];
const NodePtr& ac = *actual[i];
EXPECT_TRUE(ex.Equals(ac)) << i << ": " << message;
}
}
} // namespace
TEST_F(ReaperAppTest, CreateAndRead) {
reaper_->CreateReference(1u, 2u);
Ping(&reaper_);
mojo::Array<NodePtr> nodes;
DumpNodes(&diagnostics_, &nodes);
ASSERT_EQ(2u, nodes.size());
// TODO(aa): Need to build STL iteration into mojo::Array.
std::vector<Node*> lookup;
for (size_t i = 0; i < nodes.size(); ++i)
lookup.push_back(nodes[i].get());
// TODO(aa): It would be good to test complete URL, but we don't have it yet
// because native apps aren't implemented via the ContentHandler interface.
auto is_self = [](const std::string& url) {
return EndsWith(url, "/reaper_apptests.mojo", false);
};
EXPECT_EQ(1,
std::count_if(lookup.begin(), lookup.end(), [&is_self](Node* node) {
return is_self(node->app_url) && node->node_id == 1u &&
is_self(node->other_app_url) && node->other_id == 2u &&
node->is_source;
}));
EXPECT_EQ(1,
std::count_if(lookup.begin(), lookup.end(), [&is_self](Node* node) {
return is_self(node->app_url) && node->node_id == 2u &&
is_self(node->other_app_url) && node->other_id == 1u &&
!node->is_source;
}));
}
TEST_F(ReaperAppTest, CreateDuplicate) {
reaper_->CreateReference(1u, 2u);
// The duplicate nodes should be silently ignored.
reaper_->CreateReference(1u, 2u);
Ping(&reaper_);
mojo::Array<NodePtr> nodes;
DumpNodes(&diagnostics_, &nodes);
ASSERT_EQ(2u, nodes.size());
}
TEST_F(ReaperAppTest, DropOneNode) {
reaper_->CreateReference(1u, 2u);
reaper_->DropNode(1u);
Ping(&reaper_);
mojo::Array<NodePtr> nodes;
DumpNodes(&diagnostics_, &nodes);
// The other node gets dropped immediately.
ASSERT_EQ(0u, nodes.size());
}
TEST_F(ReaperAppTest, GetApplicationSecret) {
uint64 secret1 = 0u;
reaper_->GetApplicationSecret(SecretCatcher(&secret1));
Ping(&reaper_);
EXPECT_NE(0u, secret1);
uint64 secret2 = 0u;
reaper_->GetApplicationSecret(SecretCatcher(&secret2));
Ping(&reaper_);
EXPECT_EQ(secret1, secret2);
diagnostics_->Reset();
Ping(&diagnostics_);
uint64 secret3 = 0u;
reaper_->GetApplicationSecret(SecretCatcher(&secret3));
Ping(&reaper_);
EXPECT_NE(0u, secret3);
EXPECT_NE(secret1, secret3);
EXPECT_NE(secret2, secret3);
}
TEST_F(ReaperAppTest, Transfer) {
std::string app1_url("http://a.com/1");
std::string app2_url("http://a.com/2");
std::string reaper_url("mojo:reaper");
ReaperPtr reaper1;
ReaperPtr reaper2;
diagnostics_->GetReaperForApp(app1_url, GetProxy(&reaper1));
diagnostics_->GetReaperForApp(app2_url, GetProxy(&reaper2));
reaper1->CreateReference(1u, 2u);
Ping(&reaper1);
mojo::Array<NodePtr> expected;
AddReference(&expected, app1_url, 1u, app1_url, 2u);
mojo::Array<NodePtr> actual;
DumpNodes(&diagnostics_, &actual);
ExpectEqual(GetNodeVec(expected), GetNodeVec(actual), "before start");
uint64 app2_secret = 0u;
reaper2->GetApplicationSecret(SecretCatcher(&app2_secret));
Ping(&reaper2);
ASSERT_NE(0u, app2_secret);
TransferPtr transfer;
reaper1->StartTransfer(1u, GetProxy(&transfer));
Ping(&reaper1);
expected.reset();
AddReference(&expected, reaper_url, 1u, app1_url, 2u);
DumpNodes(&diagnostics_, &actual);
ExpectEqual(GetNodeVec(expected), GetNodeVec(actual), "after start");
transfer->Complete(app2_secret, 1u);
Ping(&transfer);
expected.reset();
AddReference(&expected, app2_url, 1u, app1_url, 2u);
DumpNodes(&diagnostics_, &actual);
ExpectEqual(GetNodeVec(expected), GetNodeVec(actual), "after complete");
// Shouldn't crash or change anything on second call.
uint64 app1_secret = 0u;
reaper1->GetApplicationSecret(SecretCatcher(&app1_secret));
Ping(&reaper1);
ASSERT_NE(0u, app1_secret);
transfer->Complete(app1_secret, 3u);
Ping(&transfer);
DumpNodes(&diagnostics_, &actual);
ExpectEqual(GetNodeVec(expected), GetNodeVec(actual), "after reuse transfer");
}
} // namespace reaper