‰PNG  IHDR @ @ ªiqÞ pHYs   šœ —tEXtComment #include #include #include #include using namespace Passenger; using namespace Passenger::ApplicationPool2; using namespace std; namespace tut { struct Core_ApplicationPool_ProcessTest: public TestBase { WrapperRegistry::Registry wrapperRegistry; SpawningKit::Context::Schema skContextSchema; SpawningKit::Context skContext; Context context; BasicGroupInfo groupInfo; vector sockets; Pipe stdinFd, stdoutAndErrFd; FileDescriptor server1, server2, server3; Core_ApplicationPool_ProcessTest() : skContext(skContextSchema) { wrapperRegistry.finalize(); skContext.resourceLocator = resourceLocator; skContext.wrapperRegistry = &wrapperRegistry; skContext.integrationMode = "standalone"; skContext.finalize(); context.spawningKitFactory = boost::make_shared(&skContext); context.finalize(); groupInfo.context = &context; groupInfo.group = NULL; groupInfo.name = "test"; struct sockaddr_in addr; socklen_t len = sizeof(addr); SpawningKit::Result::Socket socket; server1.assign(createTcpServer("127.0.0.1", 0, 0, __FILE__, __LINE__), NULL, 0); getsockname(server1, (struct sockaddr *) &addr, &len); socket.address = "tcp://127.0.0.1:" + toString(addr.sin_port); socket.protocol = "session"; socket.concurrency = 3; socket.acceptHttpRequests = true; sockets.push_back(socket); server2.assign(createTcpServer("127.0.0.1", 0, 0, __FILE__, __LINE__), NULL, 0); getsockname(server2, (struct sockaddr *) &addr, &len); socket = SpawningKit::Result::Socket(); socket.address = "tcp://127.0.0.1:" + toString(addr.sin_port); socket.protocol = "session"; socket.concurrency = 3; socket.acceptHttpRequests = true; sockets.push_back(socket); server3.assign(createTcpServer("127.0.0.1", 0, 0, __FILE__, __LINE__), NULL, 0); getsockname(server3, (struct sockaddr *) &addr, &len); socket = SpawningKit::Result::Socket(); socket.address = "tcp://127.0.0.1:" + toString(addr.sin_port); socket.protocol = "session"; socket.concurrency = 3; socket.acceptHttpRequests = true; sockets.push_back(socket); stdinFd = createPipe(__FILE__, __LINE__); stdoutAndErrFd = createPipe(__FILE__, __LINE__); Json::Value config; vector errors; LoggingKit::ConfigChangeRequest req; config["app_output_log_level"] = "debug"; if (LoggingKit::context->prepareConfigChange(config, errors, req)) { LoggingKit::context->commitConfigChange(req); } else { P_BUG("Error configuring LoggingKit: " << ConfigKit::toString(errors)); } } ~Core_ApplicationPool_ProcessTest() { Json::Value config; vector errors; LoggingKit::ConfigChangeRequest req; config["level"] = DEFAULT_LOG_LEVEL_NAME; config["app_output_log_level"] = DEFAULT_APP_OUTPUT_LOG_LEVEL_NAME; if (LoggingKit::context->prepareConfigChange(config, errors, req)) { LoggingKit::context->commitConfigChange(req); } else { P_BUG("Error configuring LoggingKit: " << ConfigKit::toString(errors)); } } ProcessPtr createProcess(const Json::Value &extraArgs = Json::Value()) { SpawningKit::Result result; Json::Value args = extraArgs; vector internalFieldErrors; vector appSuppliedFieldErrors; result.pid = 123; result.gupid = "123"; result.type = SpawningKit::Result::DUMMY; result.spawnStartTime = 1; result.spawnEndTime = 1; result.spawnStartTimeMonotonic = 1; result.spawnEndTimeMonotonic = 1; result.sockets = sockets; result.stdinFd = stdinFd[1]; result.stdoutAndErrFd = stdoutAndErrFd[0]; if (!result.validate(internalFieldErrors, appSuppliedFieldErrors)) { P_BUG("Cannot create dummy process:\n" << toString(internalFieldErrors) << "\n" << toString(appSuppliedFieldErrors)); } args["spawner_creation_time"] = 0; ProcessPtr process(context.processObjectPool.construct( &groupInfo, result, args), false); process->shutdownNotRequired(); return process; } }; DEFINE_TEST_GROUP(Core_ApplicationPool_ProcessTest); TEST_METHOD(1) { set_test_name("Test initial state"); ProcessPtr process = createProcess(); ensure_equals(process->busyness(), 0); ensure(!process->isTotallyBusy()); } TEST_METHOD(2) { set_test_name("Test opening and closing sessions"); ProcessPtr process = createProcess(); SessionPtr session = process->newSession(); SessionPtr session2 = process->newSession(); ensure_equals(process->sessions, 2); process->sessionClosed(session.get()); ensure_equals(process->sessions, 1); process->sessionClosed(session2.get()); ensure_equals(process->sessions, 0); } TEST_METHOD(3) { set_test_name("newSession() checks out the socket with the smallest busyness number " "and sessionClosed() restores the session busyness statistics"); ProcessPtr process = createProcess(); // The first 3 newSession() commands check out an idle socket. SessionPtr session1 = process->newSession(); SessionPtr session2 = process->newSession(); SessionPtr session3 = process->newSession(); ensure(session1->getSocket()->address != session2->getSocket()->address); ensure(session1->getSocket()->address != session3->getSocket()->address); ensure(session2->getSocket()->address != session3->getSocket()->address); // The next 2 newSession() commands check out sockets with sessions == 1. SessionPtr session4 = process->newSession(); SessionPtr session5 = process->newSession(); ensure(session4->getSocket()->address != session5->getSocket()->address); // There should now be 1 process with 1 session // and 2 processes with 2 sessions. map sessionCount; SocketList::const_iterator it; for (it = process->getSockets().begin(); it != process->getSockets().end(); it++) { sessionCount[it->sessions]++; } ensure_equals(sessionCount.size(), 2u); ensure_equals(sessionCount[1], 1); ensure_equals(sessionCount[2], 2); // Closing the first 3 sessions will result in no processes having 1 session // and 1 process having 2 sessions. process->sessionClosed(session1.get()); process->sessionClosed(session2.get()); process->sessionClosed(session3.get()); sessionCount.clear(); for (it = process->getSockets().begin(); it != process->getSockets().end(); it++) { sessionCount[it->sessions]++; } ensure_equals(sessionCount[0], 1); ensure_equals(sessionCount[1], 2); } TEST_METHOD(4) { set_test_name("If all sockets are at their full capacity then newSession() will fail"); ProcessPtr process = createProcess(); vector sessions; for (int i = 0; i < 9; i++) { ensure(!process->isTotallyBusy()); SessionPtr session = process->newSession(); ensure(session != NULL); sessions.push_back(session); } ensure(process->isTotallyBusy()); ensure(process->newSession() == NULL); } TEST_METHOD(5) { set_test_name("It forwards all stdout and stderr output, even after the " "Process object has been destroyed"); TempDir temp("tmp.log"); Json::Value extraArgs; extraArgs["log_file"] = "tmp.log/file"; fclose(fopen("tmp.log/file", "w")); ProcessPtr process = createProcess(extraArgs); 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::WARN); } writeExact(stdoutAndErrFd[1], "stdout and err 1\n"); writeExact(stdoutAndErrFd[1], "stdout and err 2\n"); EVENTUALLY(2, string contents = unsafeReadFile("tmp.log/file"); result = contents.find("stdout and err 1\n") != string::npos && contents.find("stdout and err 2\n") != string::npos; ); fclose(fopen("tmp.log/file", "w")); process.reset(); writeExact(stdoutAndErrFd[1], "stdout and err 3\n"); writeExact(stdoutAndErrFd[1], "stdout and err 4\n"); EVENTUALLY(2, string contents = unsafeReadFile("tmp.log/file"); result = contents.find("stdout and err 3\n") != string::npos && contents.find("stdout and err 4\n") != string::npos; ); } }