blob: 61b0a5e71ddb1479c070ff81044da37f9124edbf [file] [log] [blame]
// 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 "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "mojo/edk/embedder/channel_init.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/shell/external_application_registrar_connection.h"
#include "mojo/shell/incoming_connection_listener_posix.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/socket/socket_descriptor.h"
#include "net/socket/unix_domain_client_socket_posix.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace shell {
namespace {
// Delegate implementation that expects success.
class TestDelegate : public IncomingConnectionListenerPosix::Delegate {
public:
TestDelegate() {}
~TestDelegate() override {}
void OnListening(int rv) override { EXPECT_EQ(net::OK, rv); }
void OnConnection(net::SocketDescriptor incoming) override {
EXPECT_NE(net::kInvalidSocket, incoming);
}
};
// Delegate implementation that expects a (configurable) failure to listen.
class ListeningFailsDelegate
: public IncomingConnectionListenerPosix::Delegate {
public:
explicit ListeningFailsDelegate(int expected) : expected_error_(expected) {}
~ListeningFailsDelegate() override {}
void OnListening(int rv) override { EXPECT_EQ(expected_error_, rv); }
void OnConnection(net::SocketDescriptor incoming) override {
FAIL() << "No connection should be attempted.";
}
private:
const int expected_error_;
};
// For ExternalApplicationRegistrarConnection::Connect() callbacks.
void OnConnect(base::Closure quit_callback, int rv) {
EXPECT_EQ(net::OK, rv);
base::MessageLoop::current()->PostTask(FROM_HERE, quit_callback);
}
} // namespace
class IncomingConnectionListenerTest : public testing::Test {
public:
IncomingConnectionListenerTest() {}
virtual ~IncomingConnectionListenerTest() {}
virtual void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
socket_path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("socket"));
}
protected:
base::MessageLoopForIO loop_;
base::RunLoop run_loop_;
base::ScopedTempDir temp_dir_;
base::FilePath socket_path_;
};
TEST_F(IncomingConnectionListenerTest, CleanupCheck) {
TestDelegate delegate;
{
IncomingConnectionListenerPosix cleanup_check(socket_path_, &delegate);
cleanup_check.StartListening();
ASSERT_TRUE(base::PathExists(socket_path_));
}
ASSERT_FALSE(base::PathExists(socket_path_));
}
TEST_F(IncomingConnectionListenerTest, ConnectSuccess) {
TestDelegate delegate;
IncomingConnectionListenerPosix listener(socket_path_, &delegate);
ASSERT_FALSE(base::PathExists(socket_path_));
listener.StartListening();
ASSERT_TRUE(base::PathExists(socket_path_));
ExternalApplicationRegistrarConnection connection(socket_path_);
connection.Connect(base::Bind(&OnConnect, run_loop_.QuitClosure()));
run_loop_.Run();
}
TEST_F(IncomingConnectionListenerTest, ConnectSuccess_SocketFileExists) {
TestDelegate delegate;
IncomingConnectionListenerPosix listener(socket_path_, &delegate);
ASSERT_EQ(1, base::WriteFile(socket_path_, "1", 1));
ASSERT_TRUE(base::PathExists(socket_path_));
listener.StartListening();
ExternalApplicationRegistrarConnection connection(socket_path_);
connection.Connect(base::Bind(&OnConnect, run_loop_.QuitClosure()));
run_loop_.Run();
}
TEST_F(IncomingConnectionListenerTest, ConnectFails_SocketFileUndeletable) {
ListeningFailsDelegate fail_delegate(net::ERR_FILE_EXISTS);
IncomingConnectionListenerPosix listener(socket_path_, &fail_delegate);
// Create the socket file.
ASSERT_EQ(1, base::WriteFile(socket_path_, "1", 1));
ASSERT_TRUE(base::PathExists(socket_path_));
// Render it undeletable, but in a way that the test harness can recover
// later.
int temp_dir_perms = 0;
ASSERT_TRUE(base::GetPosixFilePermissions(temp_dir_.path(), &temp_dir_perms));
ASSERT_TRUE(
base::SetPosixFilePermissions(temp_dir_.path(),
base::FILE_PERMISSION_READ_BY_USER |
base::FILE_PERMISSION_WRITE_BY_USER));
// The listener should fail to start up.
listener.StartListening();
ASSERT_TRUE(base::SetPosixFilePermissions(temp_dir_.path(), temp_dir_perms));
}
TEST_F(IncomingConnectionListenerTest, ConnectFails_SocketDirNonexistent) {
base::FilePath nonexistent_dir(temp_dir_.path().Append("dir").Append("file"));
ListeningFailsDelegate fail_delegate(net::ERR_FILE_NOT_FOUND);
IncomingConnectionListenerPosix listener(nonexistent_dir, &fail_delegate);
// The listener should fail to start up.
listener.StartListening();
}
} // namespace shell
} // namespace mojo