Arquivos
hhvm/hphp/test/test_debugger.cpp
T
Edwin Smith f29ee5314d Remove String::operator const char*().
Too many ways to shoot self in foot with this gem.
2013-04-25 11:34:21 -07:00

373 linhas
11 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <test/test_debugger.h>
#include <test/test_server.h>
#include <runtime/ext/ext_curl.h>
#include <runtime/ext/ext_preg.h>
#include <runtime/ext/ext_options.h>
#include <runtime/base/zend/zend_math.h>
#include <util/async_func.h>
#include <util/process.h>
///////////////////////////////////////////////////////////////////////////////
static int get_random_port() {
const int BasePort = 20000;
const int PortRange = 3000;
return BasePort + rand() % PortRange;
}
TestDebugger::TestDebugger() {
srand(math_generate_seed());
m_serverPort = get_random_port();
do {
m_adminPort = get_random_port();
} while (m_adminPort == m_serverPort);
do {
m_debugPort = get_random_port();
} while (m_debugPort == m_serverPort ||
m_debugPort == m_adminPort);
}
///////////////////////////////////////////////////////////////////////////////
bool TestDebugger::RunTests(const std::string &which) {
bool ret = true;
unlink("/tmp/hphpd_test_error.log");
AsyncFunc<TestDebugger> func(this, &TestDebugger::runServer);
func.start();
{
// To make sure TestSanity always get run
std::string which = "TestSanity";
RUN_TEST(TestSanity);
}
RUN_TEST(TestBasic);
RUN_TEST(TestBreak);
RUN_TEST(TestFlow);
RUN_TEST(TestStack);
RUN_TEST(TestEval);
RUN_TEST(TestException);
RUN_TEST(TestInfo);
RUN_TEST(TestWebRequest);
stopServer();
func.waitForEnd();
return ret;
}
bool TestDebugger::getResponse(const string& path, string& result,
int port /* = -1 */,
const string& host /* = "" */) {
String server = "http://";
if (host.empty()) {
server += f_php_uname("n");
} else {
server += host;
}
server += ":" + boost::lexical_cast<string>(port > 0 ? port : m_serverPort);
server += "/" + path;
printf("\n Getting URL '%s'...\n", server.get()->data());
Variant c = f_curl_init();
f_curl_setopt(c, k_CURLOPT_URL, server);
f_curl_setopt(c, k_CURLOPT_RETURNTRANSFER, true);
f_curl_setopt(c, CURLOPT_TIMEOUT, 120);
Variant res = f_curl_exec(c);
if (same(res, false)) {
printf(" Request failed\n");
return false;
}
result = (std::string) res.toString();
printf(" Request succeeded, returning '%s'\n", result.c_str());
return true;
}
///////////////////////////////////////////////////////////////////////////////
bool TestDebugger::TestSanity() {
// first test, server might not be ready yet
bool ret = false;
for (int i = 0; i < 20; i++) {
string result;
if (!getResponse("hello.php", result)) {
sleep(2);
continue;
}
if (result == "Hello, World!") {
ret = true;
}
break;
}
return Count(ret);
}
#define RUN_FILE(php) { \
string result; \
bool ret = false; \
if (getResponse(php, result)) { \
ret = result == "pass"; \
} else { \
printf("failed to get response\n"); \
} \
return Count(ret); \
} \
bool TestDebugger::TestBasic() {
RUN_FILE("basic.php");
}
bool TestDebugger::TestBreak() {
RUN_FILE("break.php");
}
bool TestDebugger::TestFlow() {
RUN_FILE("flow.php");
}
bool TestDebugger::TestStack() {
RUN_FILE("stack.php");
}
bool TestDebugger::TestEval() {
RUN_FILE("eval.php");
}
bool TestDebugger::TestException() {
RUN_FILE("exception.php");
}
bool TestDebugger::TestInfo() {
RUN_FILE("info.php");
}
static std::string getSandboxHostFormat() {
// Assume dev servers host name are in following format:
// <host>.<cluster>.<domain>.com
// and we need to change to the following to match sandbox format:
// www.<sandbox>.<host>.<domain>.com
String hostName = f_php_uname("n");
Array fields = f_split("\\.", hostName);
if (fields.size() != 4) {
return "";
}
String host = fields.rvalAt(0).toString();
String domain = fields.rvalAt(1).toString() + "." +
fields.rvalAt(2).toString();
String suffix = fields.rvalAt(3).toString();
string sandboxHost = "hphpd.debugger_tests." + host->toCPPString() +
"." + domain->toCPPString() +
"." + suffix->toCPPString();
return sandboxHost;
}
bool TestDebugger::recvFromTests(char& flag) {
const int TIMEOUT_SEC = 10;
int fifoFd = open(m_fname.c_str(), O_RDONLY | O_NONBLOCK);
if (fifoFd < 0) {
return false;
}
while (true) {
pollfd fds[1];
fds[0].fd = fifoFd;
fds[0].events = POLLIN|POLLERR|POLLHUP;
int ret = poll(fds, 1, TIMEOUT_SEC * 1000);
if (ret == 0) {
// timed out
return false;
}
if (ret == -1) {
if (errno == EINTR) {
continue;
} else {
close(fifoFd);
return false;
}
}
if (fds[0].events & POLLIN) {
read(fifoFd, &flag, 1);
break;
}
close(fifoFd);
return false;
}
close(fifoFd);
// slightly wait here since the server side will send out the flag _before_
// going to block, but we should take action _after_ it is blocked.
usleep(30000);
return true;
}
bool TestDebugger::TestWebRequest() {
bool ret = false;
// If we can't get sandbox host format right, fail early
string sandboxHost = getSandboxHostFormat();
if (sandboxHost.empty()) {
return CountSkip();
}
string result;
if (!getResponse("hello.php", result, -1, sandboxHost) ||
result != "Hello, World!") {
printf("Sanity check didn't pass on sandbox host %s, skip test\n",
sandboxHost.c_str());
return CountSkip();
}
m_fname = "/tmp/hphpd_test_fifo." +
boost::lexical_cast<string>(Process::GetProcessId());
if (mkfifo(m_fname.c_str(), 0666)) {
printf("Can not make fifo %s, skip test\n", m_fname.c_str());
return CountSkip();
}
m_tempResult = false;
AsyncFunc<TestDebugger> func(this, &TestDebugger::testWebRequestHelper);
func.start();
char flag;
// wait for "web_request.php" to connect and wait
if (!recvFromTests(flag) || flag != '1') {
printf("failed to receive from test\n");
} else if (!getResponse("web_request_t.php", result, -1, sandboxHost) ||
result != "request done") {
printf("failed on web_request_t.php\n");
} else {
ret = true;
}
func.waitForEnd();
unlink(m_fname.c_str());
// testWebRequestHelper() should flag m_tempResult to true if succeed
ret = ret && m_tempResult;
return Count(ret);
}
void TestDebugger::testWebRequestHelper() {
string result;
if (getResponse("web_request.php", result)) {
m_tempResult = result == "pass";
} else {
printf("failed to get response for web_request.php\n");
}
if (!m_tempResult) {
return;
}
m_tempResult = false;
// test interrupt: again starting a new thread first wait on interrupts
AsyncFunc<TestDebugger> func(this,
&TestDebugger::testWebRequestHelperSignal);
func.start();
char flag;
// wait for "web_request_signal.php" to connect and wait
if (!recvFromTests(flag) || flag != '2') {
printf("failed to receive from test\n");
return;
}
if (!getResponse("web_request_interrupt.php", result) ||
result != "interrupt done") {
printf("failed on web_request_interrupt.php\n");
return;
}
// wait for "web_request_t.php" to finish
if (!recvFromTests(flag) || flag != '3') {
printf("failed to receive from test\n");
return;
}
if (!getResponse("web_request_interrupt.php", result) ||
result != "interrupt done") {
printf("failed on web_request_interrupt.php\n");
return;
}
func.waitForEnd();
}
void TestDebugger::testWebRequestHelperSignal() {
string result;
if (getResponse("web_request_signal.php", result)) {
m_tempResult = result == "pass";
} else {
printf("failed to get response for web_request_signal.php\n");
}
}
///////////////////////////////////////////////////////////////////////////////
void TestDebugger::runServer() {
string out, err;
string portConfig = "-vServer.Port=" +
boost::lexical_cast<string>(m_serverPort);
string srcRootConfig = "-vServer.SourceRoot=" +
Process::GetCurrentDirectory() +
"/test/debugger_tests";
string includePathConfig = "-vServer.IncludeSearchPaths.0=" +
Process::GetCurrentDirectory() +
"/test/debugger_tests";
string adminPortConfig = "-vAdminServer.Port=" +
boost::lexical_cast<string>(m_adminPort);
string debugPortConfig = "-vEval.Debugger.Port=" +
boost::lexical_cast<string>(m_debugPort);
string jitConfig = "-vEval.Jit=" +
boost::lexical_cast<string>(RuntimeOption::EvalJit);
string jitUseIRConfig = "-vEval.JitUseIR=" +
boost::lexical_cast<string>(
RuntimeOption::EvalJitUseIR);
// To emulate sandbox setup, let home to be "hphp/test", and user name to be
// "debugger_tests", so that it can find the sandbox_conf there
string sandboxHomeConfig = "-vSandbox.Home=" +
Process::GetCurrentDirectory() +
"/test";
const char *argv[] = {"hphpd_test", "--mode=server",
"--config=test/config-debugger-server.hdf",
portConfig.c_str(),
srcRootConfig.c_str(),
includePathConfig.c_str(),
sandboxHomeConfig.c_str(),
adminPortConfig.c_str(),
debugPortConfig.c_str(),
jitConfig.c_str(),
jitUseIRConfig.c_str(),
nullptr};
printf("Running server with arguments:\n");
for (unsigned i = 1; i < array_size(argv) - 1; ++i) {
printf("%s ", argv[i]);
}
printf("\n");
Process::Exec(HHVM_PATH, argv, nullptr, out, &err);
}
void TestDebugger::stopServer() {
for (int i = 0; i < 10; i++) {
string result;
if (getResponse("stop", result, m_adminPort)) {
break;
}
sleep(1);
}
}