‰PNG  IHDR @ @ ªiqÞ pHYs   šœ —tEXtComment #include #include #include #include #include #include #include #include #include #include #include #include using namespace Passenger; using namespace Passenger::ServerKit; using namespace Passenger::MemoryKit; using namespace std; using namespace oxt; namespace tut { struct ServerKit_ServerTest: public TestBase { typedef ClientRef, Client> ClientRefType; BackgroundEventLoop bg; Json::Value config; ServerKit::Schema skSchema; ServerKit::Context context; ServerKit::BaseServerSchema schema; boost::shared_ptr< Server > server; int serverSocket1, serverSocket2; ServerKit_ServerTest() : bg(false, true), context(skSchema) { if (defaultLogLevel == (LoggingKit::Level) DEFAULT_LOG_LEVEL) { // If the user did not customize the test's log level, // then we'll want to tone down the noise. LoggingKit::setLevel(LoggingKit::CRIT); } context.libev = bg.safe; context.libuv = bg.libuv_loop; context.initialize(); serverSocket1 = createUnixServer("tmp.server1"); serverSocket2 = createUnixServer("tmp.server2"); } ~ServerKit_ServerTest() { if (!bg.isStarted()) { bg.start(); } if (server != NULL) { bg.safe->runSync(boost::bind(&Server::shutdown, server.get(), true)); while (getServerState() != Server::FINISHED_SHUTDOWN) { syscalls::usleep(10000); } } bg.safe->runSync(boost::bind(&ServerKit_ServerTest::destroyServer, this)); safelyClose(serverSocket1); safelyClose(serverSocket2); unlink("tmp.server1"); unlink("tmp.server2"); bg.stop(); } void init() { server = boost::make_shared< Server >(&context, schema, config); server->initialize(); server->listen(serverSocket1); } template void initWithServerClass() { server = boost::make_shared(&context, schema, config); server->initialize(); server->listen(serverSocket1); } void startServer() { bg.start(); } void destroyServer() { server.reset(); } FileDescriptor connectToServer1() { return FileDescriptor(connectToUnixServer("tmp.server1", __FILE__, __LINE__), NULL, 0); } FileDescriptor connectToServer2() { return FileDescriptor(connectToUnixServer("tmp.server2", __FILE__, __LINE__), NULL, 0); } Server::State getServerState() { Server::State result; bg.safe->runSync(boost::bind(&ServerKit_ServerTest::_getServerState, this, &result)); return result; } void _getServerState(Server::State *state) { *state = server->serverState; } unsigned int getActiveClientCount() { int result; bg.safe->runSync(boost::bind(&ServerKit_ServerTest::_getActiveClientCount, this, &result)); return result; } void _getActiveClientCount(int *result) { *result = server->activeClientCount; } unsigned int getDisconnectedClientCount() { int result; bg.safe->runSync(boost::bind(&ServerKit_ServerTest::_getDisconnectedClientCount, this, &result)); return result; } void _getDisconnectedClientCount(int *result) { *result = server->disconnectedClientCount; } unsigned int getFreeClientCount() { int result; bg.safe->runSync(boost::bind(&ServerKit_ServerTest::_getFreeClientCount, this, &result)); return result; } void _getFreeClientCount(int *result) { *result = server->freeClientCount; } vector getActiveClients() { vector result; bg.safe->runSync(boost::bind(&ServerKit_ServerTest::_getActiveClients, this, &result)); return result; } void _getActiveClients(vector *result) { *result = server->getActiveClients(); } bool clientIsConnected(Client *client) { bool result; bg.safe->runSync(boost::bind(&ServerKit_ServerTest::_clientIsConnected, this, client, &result)); return result; } void _clientIsConnected(Client *client, bool *result) { *result = client->connected(); } }; DEFINE_TEST_GROUP(ServerKit_ServerTest); /***** Initial state *****/ TEST_METHOD(1) { set_test_name("It has no clients at startup"); init(); ensure_equals(server->activeClientCount, 0u); ensure_equals(server->disconnectedClientCount, 0u); ensure_equals(server->freeClientCount, 0u); } /***** Client object management *****/ TEST_METHOD(5) { set_test_name("Accepting a new client works"); init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); } TEST_METHOD(6) { set_test_name("When a client is accepted, and the freelist is non-empty, " "the object is checked out from the freelist"); config["min_spare_clients"] = 1; init(); server->createSpareClients(); startServer(); ensure_equals(getActiveClientCount(), 0u); ensure_equals(getDisconnectedClientCount(), 0u); ensure_equals(getFreeClientCount(), 1u); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); ensure_equals(getDisconnectedClientCount(), 0u); ensure_equals(getFreeClientCount(), 0u); } TEST_METHOD(7) { set_test_name("When a client is accepted, and the freelist is empty, " "the object is allocated"); init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); ensure_equals(getDisconnectedClientCount(), 0u); ensure_equals(getFreeClientCount(), 0u); } TEST_METHOD(8) { set_test_name("Once a client has been disconnected, and the freelist has not " "yet reached the limit, the client object is put on the freelist"); config["client_freelist_limit"] = 10; init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); fd.close(); EVENTUALLY(5, result = getActiveClientCount() == 0u; ); ensure_equals(getDisconnectedClientCount(), 0u); ensure_equals(getFreeClientCount(), 1u); } TEST_METHOD(9) { set_test_name("Once a client has been disconnected, and the freelist has " "reached the limit, the client object is destroyed"); config["client_freelist_limit"] = 0; init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); fd.close(); EVENTUALLY(5, result = getActiveClientCount() == 0u; ); ensure_equals(getDisconnectedClientCount(), 0u); ensure_equals(getFreeClientCount(), 0u); } TEST_METHOD(10) { set_test_name("Once a client has been disconnected, the freelist has not yet reached " "the limit, and there are still references to the client, then the client is first " "put in the disconnecting list, then in the freelist when the last references disappear"); vector clients; config["client_freelist_limit"] = 10; init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); clients = getActiveClients(); fd.close(); EVENTUALLY(5, result = getActiveClientCount() == 0u; ); ensure_equals(getDisconnectedClientCount(), 1u); ensure_equals(getFreeClientCount(), 0u); clients.clear(); EVENTUALLY(5, result = getDisconnectedClientCount() == 0u; ); ensure_equals(getActiveClientCount(), 0u); ensure_equals(getFreeClientCount(), 1u); } TEST_METHOD(11) { set_test_name("Once a client has been disconnected, the freelist has reached the limit, " "and there are still references to the client, then the client is first put in the " "disconnecting list, then destroyed when the last references disappear"); vector clients; config["client_freelist_limit"] = 0; init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); clients = getActiveClients(); fd.close(); EVENTUALLY(5, result = getActiveClientCount() == 0u; ); ensure_equals(getDisconnectedClientCount(), 1u); ensure_equals(getFreeClientCount(), 0u); clients.clear(); EVENTUALLY(5, result = getDisconnectedClientCount() == 0u; ); ensure_equals(getActiveClientCount(), 0u); ensure_equals(getFreeClientCount(), 0u); } /****** Multiple listen endpoints *****/ TEST_METHOD(20) { set_test_name("It can listen on multiple endpoints"); init(); server->listen(serverSocket2); startServer(); FileDescriptor fd1 = connectToServer1(); FileDescriptor fd2 = connectToServer2(); EVENTUALLY(5, result = getActiveClientCount() == 2u; ); } /****** Input and output *****/ class Test25Server: public Server { protected: virtual Channel::Result onClientDataReceived(Client *client, const MemoryKit::mbuf &buffer, int errcode) { if (errcode != 0 || buffer.empty()) { disconnect(&client); } else { boost::lock_guard l(syncher); data.append(buffer.start, buffer.size()); } return Channel::Result(buffer.size(), false); } public: boost::mutex syncher; string data; Test25Server(Context *ctx, const ServerKit::BaseServerSchema &schema, const Json::Value &initialConfig) : Server(ctx, schema, initialConfig) { } }; TEST_METHOD(25) { set_test_name("Input is made available through client->input"); initWithServerClass(); startServer(); FileDescriptor fd(connectToServer1()); writeExact(fd, "hello", 5); EVENTUALLY(5, Test25Server *s = (Test25Server *) server.get(); boost::lock_guard l(s->syncher); result = s->data == "hello"; ); } class Test26Server: public Server { protected: virtual Channel::Result onClientDataReceived(Client *client, const MemoryKit::mbuf &buffer, int errcode) { if (errcode != 0 || buffer.empty()) { disconnect(&client); } else { client->output.feed(buffer); } return Channel::Result(buffer.size(), false); } public: Test26Server(Context *ctx, const ServerKit::BaseServerSchema &schema, const Json::Value &initialConfig) : Server(ctx, schema, initialConfig) { } }; TEST_METHOD(26) { set_test_name("Output is made available through client->output"); initWithServerClass(); startServer(); FileDescriptor fd(connectToServer1()); writeExact(fd, "hello", 5); syscalls::shutdown(fd, SHUT_WR); ensure_equals(readAll(fd, 1024).first, "hello"); } TEST_METHOD(27) { set_test_name("The file descriptor can be obtained through client->getFd()"); init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); ClientRefType client = getActiveClients()[0]; writeExact(client.get()->getFd(), "hello", 5); char buf[5]; readExact(fd, buf, 5); ensure_equals(StaticString(buf, 5), StaticString("hello")); } TEST_METHOD(28) { set_test_name("When a client is disconnected, client->connected() becomes false"); init(); startServer(); FileDescriptor fd(connectToServer1()); EVENTUALLY(5, result = getActiveClientCount() == 1u; ); ClientRefType client = getActiveClients()[0]; ensure(clientIsConnected(client.get())); fd.close(); EVENTUALLY(5, result = !clientIsConnected(client.get()); ); } }