Commit: 3aca4906da0f27644e1e17039316358ff03b6404 Author: Matt Ficken <v-maf...@microsoft.com> Fri, 2 Nov 2012 09:58:37 -0700 Parents: 9f9b1aba45852e608087bb9481307947df3b2223 Branches: master
Link: http://git.php.net/?p=pftt2.git;a=commitdiff;h=3aca4906da0f27644e1e17039316358ff03b6404 Log: php builtin web server testing support Former-commit-id: 92565d723da8b9e71b596ab3798b64e7652b5d56 Changed paths: M bin/pftt M bin/pftt.cmd M src/com/mostc/pftt/host/Host.java M src/com/mostc/pftt/host/LocalHost.java M src/com/mostc/pftt/model/phpt/EPhptSection.java M src/com/mostc/pftt/model/phpt/PhpIni.java M src/com/mostc/pftt/model/phpt/PhptTestCase.java M src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java M src/com/mostc/pftt/model/sapi/CrashedWebServerInstance.java M src/com/mostc/pftt/model/sapi/WebServerInstance.java M src/com/mostc/pftt/model/sapi/WebServerManager.java M src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java M src/com/mostc/pftt/runner/HttpTestCaseRunner.java M src/com/mostc/pftt/runner/PhptTestPackRunner.java M src/com/mostc/pftt/scenario/AbstractWebServerScenario.java M src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java A src/com/mostc/pftt/telemetry/ConsoleManager.java M src/com/mostc/pftt/telemetry/PhptTestResult.java M src/com/mostc/pftt/ui/PhptHostTab.java M src/com/mostc/pftt/util/HostEnvUtil.java M src/com/mostc/pftt/util/StringUtil.java
diff --git a/bin/pftt b/bin/pftt index dd82dd0..7004187 100644 --- a/bin/pftt +++ b/bin/pftt @@ -5,6 +5,6 @@ export PFTT_HOME=/home/matt/workspace/PFTT export PFTT_LIB=$PFTT_HOME/lib -export CLASSPATH=$PFTT_HOME/build:$PFTT_LIB/htmlcleaner-2.2.jar:$PFTT_LIB/groovy-1.8.6.jar:$PFTT_LIB/icu4j-49_1.jar:$PFTT_LIB/icudata.jar:$PFTT_LIB/icutzdata.jar:$PFTT_LIB/j2ssh-common-0.2.9.jar:$PFTT_LIB/j2ssh-core-0.2.9.jar:$PFTT_LIB/jansi-1.7.jar:$PFTT_LIB/jline-0.9.94.jar:$PFTT_LIB/jzlib-1.0.7.jar:$PFTT_LIB/selenium-server-standalone-2.19.0.jar:$PFTT_LIB/xercesImpl.jar:$PFTT_LIB/xmlpull-1.1.3.1.jar:$PFTT_LIB/commons-net-3.1.jar:$PFTT_LIB/commons-cli-1.2.jar:$PFTT_LIB/antlr-2.7.7.jar:$PFTT_LIB/asm-3.2.jar:$PFTT_LIB/asm-analysis-3.2.jar:$PFTT_LIB/asm-commons-3.2.jar:$PFTT_LIB/asm-tree-3.2.jar:$PFTT_LIB/asm-util-3.2.jar +export CLASSPATH=$PFTT_HOME/build:$PFTT_LIB/winp-1.14.jar:$PFTT_LIB/htmlcleaner-2.2.jar:$PFTT_LIB/groovy-1.8.6.jar:$PFTT_LIB/icu4j-49_1.jar:$PFTT_LIB/icudata.jar:$PFTT_LIB/icutzdata.jar:$PFTT_LIB/j2ssh-common-0.2.9.jar:$PFTT_LIB/j2ssh-core-0.2.9.jar:$PFTT_LIB/jansi-1.7.jar:$PFTT_LIB/jline-0.9.94.jar:$PFTT_LIB/jzlib-1.0.7.jar:$PFTT_LIB/selenium-server-standalone-2.19.0.jar:$PFTT_LIB/xercesImpl.jar:$PFTT_LIB/xmlpull-1.1.3.1.jar:$PFTT_LIB/commons-net-3.1.jar:$PFTT_LIB/commons-cli-1.2.jar:$PFTT_LIB/antlr-2.7.7.jar:$PFTT_LIB/asm-3.2.jar:$PFTT_LIB/asm-analysis-3.2.jar:$PFTT_LIB/asm-commons-3.2.jar:$PFTT_LIB/asm-tree-3.2.jar:$PFTT_LIB/asm-util-3.2.jar java -classpath $CLASSPATH com.mostc.pftt.main.PfttMain $* diff --git a/bin/pftt.cmd b/bin/pftt.cmd index a8b576c..1125891 100644 --- a/bin/pftt.cmd +++ b/bin/pftt.cmd @@ -4,7 +4,7 @@ REM script for running PFTT on Windows SET PFTT_HOME=C:\php-sdk\PFTT\current SET PFTT_LIB=%PFTT_HOME%\lib -SET CLASSPATH=%PFTT_HOME%\build;%PFTT_LIB%\htmlcleaner-2.2.jar;%PFTT_LIB%\groovy-1.8.6.jar;%PFTT_LIB%\icu4j-49_1.jar;%PFTT_LIB%\icudata.jar;%PFTT_LIB%\icutzdata.jar;%PFTT_LIB%\j2ssh-common-0.2.9.jar;%PFTT_LIB%\j2ssh-core-0.2.9.jar;%PFTT_LIB%\jansi-1.7.jar;%PFTT_LIB%\jline-0.9.94.jar;%PFTT_LIB%\jzlib-1.0.7.jar;%PFTT_LIB%\selenium-server-standalone-2.19.0.jar;%PFTT_LIB%\xercesImpl.jar;%PFTT_LIB%\xmlpull-1.1.3.1.jar;%PFTT_LIB%\commons-net-3.1.jar;%PFTT_LIB%\commons-cli-1.2.jar;%PFTT_LIB%\antlr-2.7.7.jar;%PFTT_LIB%\asm-3.2.jar;%PFTT_LIB%\asm-analysis-3.2.jar;%PFTT_LIB%\asm-commons-3.2.jar;%PFTT_LIB%\asm-tree-3.2.jar;%PFTT_LIB%\asm-util-3.2.jar +SET CLASSPATH=%PFTT_HOME%\build;%PFTT_LIB%\winp-1.14.jar;%PFTT_LIB%\htmlcleaner-2.2.jar;%PFTT_LIB%\groovy-1.8.6.jar;%PFTT_LIB%\icu4j-49_1.jar;%PFTT_LIB%\icudata.jar;%PFTT_LIB%\icutzdata.jar;%PFTT_LIB%\j2ssh-common-0.2.9.jar;%PFTT_LIB%\j2ssh-core-0.2.9.jar;%PFTT_LIB%\jansi-1.7.jar;%PFTT_LIB%\jline-0.9.94.jar;%PFTT_LIB%\jzlib-1.0.7.jar;%PFTT_LIB%\selenium-server-standalone-2.19.0.jar;%PFTT_LIB%\xercesImpl.jar;%PFTT_LIB%\xmlpull-1.1.3.1.jar;%PFTT_LIB%\commons-net-3.1.jar;%PFTT_LIB%\commons-cli-1.2.jar;%PFTT_LIB%\antlr-2.7.7.jar;%PFTT_LIB%\asm-3.2.jar;%PFTT_LIB%\asm-analysis-3.2.jar;%PFTT_LIB%\asm-commons-3.2.jar;%PFTT_LIB%\asm-tree-3.2.jar;%PFTT_LIB%\asm-util-3.2.jar REM find java and execute PfttMain REM search %PATH% for java diff --git a/src/com/mostc/pftt/host/Host.java b/src/com/mostc/pftt/host/Host.java index 9bb148e..8c3cae9 100644 --- a/src/com/mostc/pftt/host/Host.java +++ b/src/com/mostc/pftt/host/Host.java @@ -9,6 +9,7 @@ import java.util.Random; import java.util.regex.Pattern; import javax.annotation.Nullable; +import javax.annotation.concurrent.ThreadSafe; import com.github.mattficken.io.ByLineReader; @@ -309,6 +310,14 @@ public abstract class Host { public boolean isLonghornExact() { return false; // TODO } + /** returns TRUE if host is Windows + * + * this is a fast method which caches its values, so you don't have to do that in your code + * + * generally if !isWindows == isPosix + * + * @return + */ public abstract boolean isWindows(); public String getTempDir() { if (tmp_dir!=null) @@ -400,6 +409,7 @@ public abstract class Host { public ExecOutput execElevated(String cmd, int timeout_sec, String chdir) throws Exception { return execElevated(cmd, timeout_sec, null, null, null, chdir, null, NO_TIMEOUT); } + @ThreadSafe public static abstract class ExecHandle { public abstract void close(); public abstract boolean isRunning(); @@ -645,5 +655,17 @@ public abstract class Host { return 1; } } + + /* TODO + public long getTotalPhysicalMemory() { + if (isWindows()) { + // could use `wmic` but that won't work on winxp, 2003, 2003r2 + this.getSystemInfo(); + // look for line like: `Total Physical Memory: 4,096 MB` + } else { + this.exec("free", NO_TIMEOUT); + } + } + */ } // end public abstract class Host diff --git a/src/com/mostc/pftt/host/LocalHost.java b/src/com/mostc/pftt/host/LocalHost.java index efcc2e1..6ffd658 100644 --- a/src/com/mostc/pftt/host/LocalHost.java +++ b/src/com/mostc/pftt/host/LocalHost.java @@ -46,6 +46,15 @@ import com.sun.jna.platform.win32.WinNT.HANDLE; public class LocalHost extends Host { private static final Timer timer = new Timer(); private static final boolean is_windows = System.getProperty("os.name").toLowerCase().contains("windows"); + private static int self_process_id; + static { + try { + // this works only on Windows + self_process_id = Kernel32.INSTANCE.GetCurrentProcessId(); + } catch ( Throwable t ) { + t.printStackTrace(); // XXX fix this on Linux + } + } public static boolean isLocalhostWindows() { return is_windows; @@ -195,16 +204,16 @@ public class LocalHost extends Host { protected OutputStream stdin; protected InputStream stdout, stderr; protected ExitMonitorTask task; - protected String cmdline; + protected String image_name; protected Charset charset; protected StringBuilder output_sb; - public LocalExecHandle(Process process, OutputStream stdin, InputStream stdout, InputStream stderr, String cmdline) { + public LocalExecHandle(Process process, OutputStream stdin, InputStream stdout, InputStream stderr, String[] cmd_array) { this.process = process; this.stdin = stdin; this.stdout = stdout; this.stderr = stderr; - this.cmdline = cmdline; + this.image_name = StringUtil.unquote(basename(cmd_array[0])); } @Override @@ -212,20 +221,22 @@ public class LocalHost extends Host { try { process.exitValue(); return false; - } catch ( Exception ex ) { + } catch ( IllegalThreadStateException ex ) { return true; } } boolean run = true; @Override - public void close() { + public synchronized void close() { run = false; // may take multiple tries to make it exit (lots of processes, certain OSes, etc...) for ( int tries = 0 ; tries < 10 ; tries++ ) { try { process.exitValue(); + break; + // process terminated, stop trying (or may terminate new process reusing the same id) } catch ( Throwable t ) { // hasn't exited yet if (tries==0) { @@ -249,17 +260,21 @@ public class LocalHost extends Host { // kill it if (isLocalhostWindows()) { + // Windows BN: process trees on Windows won't get terminated correctly by calling Process#destroy + // + // have to do some ugly hacks on Windows to kill process trees + int process_id = 0; + // first: find the process id try { + // clean way WinProcess wproc = new WinProcess(process); - wproc.killRecursively(); + process_id = wproc.getPid(); } catch ( Throwable wt ) { // WinProcess native code couldn't be loaded // (maybe it wasn't included or maybe somebody didn't compile it) // // fallback on some old code using reflection, etc... - // - // process.destroy doesn't always work on windows, but TASKKILL does try { // process.getClass() != Process.class // @@ -274,16 +289,7 @@ public class LocalHost extends Host { HANDLE h = new HANDLE(); h.setPointer(Pointer.createConstant(handle)); - long process_id = Kernel32.INSTANCE.GetProcessId(h); - - - // TASKKILL!=TSKILL, TASKKILL is better than TSKILL - // - // /F => forcefully terminate ('kill') - // /T => terminate all child processes (process is cmd.exe and PHP is a child) - // process.destory might not do this, so thats why its CRITICAL that TASKKILL - // be tried before process.destroy - Runtime.getRuntime().exec("TASKKILL /PID:"+process_id+" /F /T"); + process_id = Kernel32.INSTANCE.GetProcessId(h); break; } @@ -292,6 +298,25 @@ public class LocalHost extends Host { t2.printStackTrace(); } // end try } // end try + + // second: make sure we found a process id (safety check: make sure its not our process id) + if (process_id != 0 && process_id!=self_process_id) { + // also, WinProcess#killRecursively only checks by process id (not image/program name) + // while that should be enough, experience on Windows has shown that it isn't and somehow gets PFTT killed eventually + // + // third:instead, run TASKKILL and provide it both the process id and image/program name + // + // image name: ex: `php.exe` + // /F => forcefully terminate ('kill') + // /T => terminate all child processes (process is cmd.exe and PHP is a child) + // process.destory might not do this, so thats why its CRITICAL that TASKKILL + // be tried before process.destroy + try { + Runtime.getRuntime().exec("TASKKIll /FI \"IMAGENAME eq "+image_name+"\" /FI \"PID eq "+process_id+"\" /F /T"); + } catch (Throwable t3) { + t3.printStackTrace(); + } + } } // end if // @@ -441,7 +466,7 @@ public class LocalHost extends Host { InputStream stdout = process.getInputStream(); InputStream stderr = process.getErrorStream(); - LocalExecHandle h = new LocalExecHandle(process, stdin, stdout, stderr, StringUtil.toString(cmd_array)); + LocalExecHandle h = new LocalExecHandle(process, stdin, stdout, stderr, cmd_array); if (timeout>NO_TIMEOUT) { h.task = new ExitMonitorTask(h); diff --git a/src/com/mostc/pftt/model/phpt/EPhptSection.java b/src/com/mostc/pftt/model/phpt/EPhptSection.java index e8dc93d..4c19044 100644 --- a/src/com/mostc/pftt/model/phpt/EPhptSection.java +++ b/src/com/mostc/pftt/model/phpt/EPhptSection.java @@ -20,48 +20,48 @@ public enum EPhptSection { DESCRIPTION { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, CREDITS { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, /** not in documentation, de-facto */ FAIL { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, /** not in documentation, de-facto */ CREDIT { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, /** not in documentation, de-facto */ UEXPECTF { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, /** not in documentation, de-facto */ DONE { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, /** not in documentation, de-facto */ COMMENT { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, SKIPIF, @@ -130,13 +130,13 @@ public enum EPhptSection { CGI { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, XFAIL { @Override public String prepareSection(boolean keep_all, String text) { - return keep_all?text:PhpIni.EMPTY; + return keep_all?text:StringUtil.EMPTY; } }, EXPECTHEADERS, diff --git a/src/com/mostc/pftt/model/phpt/PhpIni.java b/src/com/mostc/pftt/model/phpt/PhpIni.java index 88dbd78..634d7cc 100644 --- a/src/com/mostc/pftt/model/phpt/PhpIni.java +++ b/src/com/mostc/pftt/model/phpt/PhpIni.java @@ -58,9 +58,6 @@ public class PhpIni extends TestCaseGroupKey { public static final String UNICODE_OUTPUT_ENCODING = "unicode.output_encoding"; public static final String UNICODE_FROM_ERROR_MODE = "unicode.from_error_mode"; public static final String SESSION_AUTO_START = "session.auto_start"; - // - // values - public static final String EMPTY = ""; public static final String ON = "On"; public static final String OFF = "Off"; public static final String UTF_8 = "UTF-8"; @@ -101,10 +98,10 @@ public class PhpIni extends TestCaseGroupKey { public static final String EXT_XSL = dllName("xsl"); public static PhpIni createDefaultIniCopy(Host host) { PhpIni ini = new PhpIni(); - ini.putMulti(OUTPUT_HANDLER, EMPTY); - ini.putMulti(OPEN_BASEDIR, EMPTY); + ini.putMulti(OUTPUT_HANDLER, StringUtil.EMPTY); + ini.putMulti(OPEN_BASEDIR, StringUtil.EMPTY); ini.putMulti(SAFE_MODE, 0); - ini.putMulti(DISABLE_DEFS, EMPTY); + ini.putMulti(DISABLE_DEFS, StringUtil.EMPTY); ini.putMulti(OUTPUT_BUFFERING, OFF); ini.putMulti(ERROR_REPORTING, E_ALL_OR_E_STRICT); // IMPORTANT: display_errors=0. doesn't affect test output. run-tests.php sets display_errors=1 @@ -116,12 +113,12 @@ public class PhpIni extends TestCaseGroupKey { ini.putMulti(TRACK_ERRORS, 1); ini.putMulti(REPORT_MEMLEAKS, 1); ini.putMulti(REPORT_ZEND_DEBUG, 0); - ini.putMulti(DOCREF_ROOT, EMPTY); + ini.putMulti(DOCREF_ROOT, StringUtil.EMPTY); ini.putMulti(DOCREF_EXT, DOT_HTML); - ini.putMulti(ERROR_PREPEND_STRING, EMPTY); - ini.putMulti(ERROR_APPEND_STRING, EMPTY); - ini.putMulti(AUTO_PREPEND_FILE, EMPTY); - ini.putMulti(AUTO_APPEND_FILE, EMPTY); + ini.putMulti(ERROR_PREPEND_STRING, StringUtil.EMPTY); + ini.putMulti(ERROR_APPEND_STRING, StringUtil.EMPTY); + ini.putMulti(AUTO_PREPEND_FILE, StringUtil.EMPTY); + ini.putMulti(AUTO_APPEND_FILE, StringUtil.EMPTY); ini.putMulti(MAGIC_QUOTES_RUNTIME, 0); ini.putMulti(IGNORE_REPEATED_ERRORS, 0); ini.putMulti(PRECISION, 14); @@ -167,7 +164,7 @@ public class PhpIni extends TestCaseGroupKey { int ini_i = line.indexOf("="); if (ini_i!=-1) { String ini_name = line.substring(0, ini_i).trim(); - String ini_value = ini_i+1>=line.length() ? EMPTY : line.substring(ini_i+1).trim(); + String ini_value = ini_i+1>=line.length() ? StringUtil.EMPTY : line.substring(ini_i+1).trim(); putMulti(ini_name, ini_value); } } diff --git a/src/com/mostc/pftt/model/phpt/PhptTestCase.java b/src/com/mostc/pftt/model/phpt/PhptTestCase.java index f2a116d..a226017 100644 --- a/src/com/mostc/pftt/model/phpt/PhptTestCase.java +++ b/src/com/mostc/pftt/model/phpt/PhptTestCase.java @@ -9,6 +9,9 @@ import java.util.HashMap; import java.util.Set; import java.util.regex.Pattern; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import com.github.mattficken.io.AbstractDetectingCharsetReader; import com.github.mattficken.io.ByLineReader; import com.github.mattficken.io.CharsetDeciderDecoder; @@ -451,16 +454,20 @@ public class PhptTestCase extends TestCase { * @return * @see #get */ + @Nonnull public String getTrim(EPhptSection section) { String t = get(section); - return StringUtil.isEmpty(t) ? t : t.trim(); + return StringUtil.isEmpty(t) ? StringUtil.EMPTY : t.trim(); } /** returns the text of the section unmodified * + * returns null if section not found + * * @param section * @return */ + @Nullable public String get(EPhptSection section) { return section_text.get(section); } diff --git a/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java b/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java index d7d3013..4fb8979 100644 --- a/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java +++ b/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java @@ -40,7 +40,11 @@ public class BuiltinWebServerManager extends WebServerManager { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { + System.out.println("PFTT: terminating builtin web server instances..."); // Windows BN: may leave `php.exe -S` running if they aren't terminated here + // NOTE: if you click stop in Eclipse, you won't get here + // on Windows, that means that `php.exe -S` instances will still be running + // you'll need to do: taskkill /im:php.exe /f /t close(); } }); @@ -52,7 +56,8 @@ public class BuiltinWebServerManager extends WebServerManager { String sapi_output = ""; int port_attempts; boolean found_port; - for (int total_attempts = 0 ; total_attempts < 3 ; total_attempts++) { + int total_attempts = 0; + for ( ; total_attempts < 3 ; total_attempts++) { // find port number not currently in use port_attempts = 0; @@ -97,7 +102,7 @@ public class BuiltinWebServerManager extends WebServerManager { // // provide a BuiltinWebServerInstance to handle the running web server instance - final BuiltinWebServerInstance web = new BuiltinWebServerInstance(LocalHost.splitCmdString(cmd), ini, handle, hostname, last_port); + final BuiltinWebServerInstance web = new BuiltinWebServerInstance(this, LocalHost.splitCmdString(cmd), ini, handle, hostname, last_port); // check web server periodically to see if it has crashed timer.scheduleAtFixedRate(new TimerTask() { @@ -130,19 +135,19 @@ public class BuiltinWebServerManager extends WebServerManager { } // end for // fallback - sapi_output += "PFTT: wasn't able to start web server instance (after many attempts)... giving up.\n"; + sapi_output += "PFTT: wasn't able to start web server instance (after "+total_attempts+" attempts)... giving up.\n"; // return this failure message to client code - return new CrashedWebServerInstance(ini, sapi_output); + return new CrashedWebServerInstance(this, ini, sapi_output); } // end protected synchronized WebServerInstance createWebServerInstance - public class BuiltinWebServerInstance extends WebServerInstance { + public static class BuiltinWebServerInstance extends WebServerInstance { protected final int port; protected final String hostname; protected final ExecHandle process; - public BuiltinWebServerInstance(String[] cmd_array, PhpIni ini, ExecHandle process, String hostname, int port) { - super(cmd_array, ini); + public BuiltinWebServerInstance(BuiltinWebServerManager ws_mgr, String[] cmd_array, PhpIni ini, ExecHandle process, String hostname, int port) { + super(ws_mgr, cmd_array, ini); this.process = process; this.hostname = hostname; this.port = port; @@ -159,19 +164,13 @@ public class BuiltinWebServerManager extends WebServerManager { } @Override - public void close() { - try { - process.close(); - } finally { - synchronized(BuiltinWebServerManager.this.instances) { - BuiltinWebServerManager.this.instances.remove(this); - } - } + protected void do_close() { + process.close(); } @Override public boolean isRunning() { - return process.isRunning(); + return process.isRunning() && !isCrashed(); } } // end public class BuiltinWebServerInstance diff --git a/src/com/mostc/pftt/model/sapi/CrashedWebServerInstance.java b/src/com/mostc/pftt/model/sapi/CrashedWebServerInstance.java index 154ea27..1bf6a14 100644 --- a/src/com/mostc/pftt/model/sapi/CrashedWebServerInstance.java +++ b/src/com/mostc/pftt/model/sapi/CrashedWebServerInstance.java @@ -14,8 +14,8 @@ import com.mostc.pftt.model.phpt.PhpIni; public class CrashedWebServerInstance extends WebServerInstance { protected final String sapi_output; - public CrashedWebServerInstance(PhpIni ini, String sapi_output) { - super(null, ini); + public CrashedWebServerInstance(WebServerManager ws_mgr, PhpIni ini, String sapi_output) { + super(ws_mgr, null, ini); this.sapi_output = sapi_output; } @@ -43,4 +43,9 @@ public class CrashedWebServerInstance extends WebServerInstance { return false; } + @Override + protected void do_close() { + // N/A + } + } diff --git a/src/com/mostc/pftt/model/sapi/WebServerInstance.java b/src/com/mostc/pftt/model/sapi/WebServerInstance.java index f115cb9..0d821a7 100644 --- a/src/com/mostc/pftt/model/sapi/WebServerInstance.java +++ b/src/com/mostc/pftt/model/sapi/WebServerInstance.java @@ -1,6 +1,8 @@ package com.mostc.pftt.model.sapi; +import java.util.ArrayList; import java.util.LinkedList; +import java.util.List; import javax.annotation.concurrent.ThreadSafe; @@ -17,18 +19,21 @@ import com.mostc.pftt.util.StringUtil; @ThreadSafe public abstract class WebServerInstance extends SAPIInstance { - protected LinkedList<PhptTestCase> active_test_cases; + protected final List<PhptTestCase> active_test_cases, all_test_cases; private boolean crashed = false; private String sapi_output = ""; private Object sync_lock = new Object(); protected final PhpIni ini; protected final String[] cmd_array; + protected final WebServerManager ws_mgr; WebServerInstance replacement; // @see WebServerManager#getWebServerInstance - public WebServerInstance(String[] cmd_array, PhpIni ini) { + public WebServerInstance(WebServerManager ws_mgr, String[] cmd_array, PhpIni ini) { + this.ws_mgr = ws_mgr; this.cmd_array = cmd_array; this.ini = ini; active_test_cases = new LinkedList<PhptTestCase>(); + all_test_cases = new ArrayList<PhptTestCase>(256); } @Override @@ -60,6 +65,8 @@ public abstract class WebServerInstance extends SAPIInstance { * @param exit_code - exit code that was returned */ public void notifyCrash(String output, int exit_code) { + // make sure it gets closed!! + // TODO temp vs close(); synchronized(sync_lock) { // if (crashed) { @@ -68,7 +75,7 @@ public abstract class WebServerInstance extends SAPIInstance { StringBuilder sb = new StringBuilder(); if (sapi_output!=null) sb.append(sapi_output); - sb.append("PFTT: later web server returned exit code("+exit_code+") and output:\n"); + sb.append("\nPFTT: later web server returned exit code("+exit_code+") and output:\n"); sb.append(output); sapi_output = sb.toString(); } @@ -81,8 +88,9 @@ public abstract class WebServerInstance extends SAPIInstance { StringBuilder sb = new StringBuilder(1024); - sb.append("PFTT: web server crashed with exit code: "+exit_code); + sb.append("PFTT: web server crashed with exit code: "+exit_code+"\n"); getActiveTestListString(sb); + getAllTestListString(sb); if (StringUtil.isEmpty(output)) { sb.append("PFTT: web server returned no output when it exited.\n"); } else { @@ -94,7 +102,7 @@ public abstract class WebServerInstance extends SAPIInstance { } // end sync } // end protected void notifyCrash - protected void getActiveTestListString(StringBuilder sb) { + public void getActiveTestListString(StringBuilder sb) { synchronized(active_test_cases) { sb.append("PFTT: while running these tests("+active_test_cases.size()+"):\n"); for (PhptTestCase test_case : active_test_cases ) { @@ -111,11 +119,31 @@ public abstract class WebServerInstance extends SAPIInstance { return sb.toString(); } + public void getAllTestListString(StringBuilder sb) { + synchronized(all_test_cases) { + sb.append("PFTT: these tests were run against this web server instance during its lifetime("+all_test_cases.size()+"):\n"); + for (PhptTestCase test_case : all_test_cases ) { + sb.append("PFTT: "); + sb.append(test_case.getName()); + sb.append('\n'); + } + } + } + + public String getAllTestListString() { + StringBuilder sb = new StringBuilder(512); + getAllTestListString(sb); + return sb.toString(); + } + /** called before HTTP request made to server for given test_case * * @param test_case */ public void notifyTestPreRequest(PhptTestCase test_case) { + synchronized(all_test_cases) { + all_test_cases.add(test_case); + } synchronized(active_test_cases) { active_test_cases.add(test_case); } @@ -161,4 +189,20 @@ public abstract class WebServerInstance extends SAPIInstance { return cmd_array; } + @Override + public void close() { + try { + do_close(); + } finally { + synchronized(ws_mgr.instances) { + ws_mgr.instances.remove(this); + } + } + // TODO temp + for ( WebServerInstance c=replacement ; c != null ; c = c.replacement ) + c.close(); + } + + protected abstract void do_close(); + } // end public abstract class WebServerInstance diff --git a/src/com/mostc/pftt/model/sapi/WebServerManager.java b/src/com/mostc/pftt/model/sapi/WebServerManager.java index 195c57c..1b060a9 100644 --- a/src/com/mostc/pftt/model/sapi/WebServerManager.java +++ b/src/com/mostc/pftt/model/sapi/WebServerManager.java @@ -2,7 +2,6 @@ package com.mostc.pftt.model.sapi; import java.io.IOException; import java.net.Socket; -import java.util.Iterator; import java.util.LinkedList; import javax.annotation.concurrent.ThreadSafe; @@ -32,11 +31,9 @@ public abstract class WebServerManager extends SAPIManager { */ public void close() { synchronized(instances) { - Iterator<WebServerInstance> it = instances.iterator(); - while (it.hasNext()) { - it.next().close(); - it.remove(); - } + for ( WebServerInstance wsi : instances ) + wsi.do_close(); + instances.clear(); } } @@ -60,12 +57,12 @@ public abstract class WebServerManager extends SAPIManager { public WebServerInstance getWebServerInstance(Host host, PhpBuild build, PhpIni ini, String docroot, WebServerInstance assigned) { WebServerInstance sapi; if (assigned!=null) { - if (!assigned.isCrashed()) + if (assigned.isRunning()) return assigned; synchronized(assigned) { for (WebServerInstance c=assigned.replacement ; c != null ; c = c.replacement) { synchronized(c) { - if (!c.isCrashed()) + if (assigned.isRunning()) return c; } } @@ -77,7 +74,7 @@ public abstract class WebServerManager extends SAPIManager { } else { sapi = createWebServerInstance(host, build, ini, docroot); } - if (!sapi.isCrashed()) { + if (sapi.isRunning()) { synchronized(instances) { instances.add(sapi); } diff --git a/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java b/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java index 041d692..5df370e 100644 --- a/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java +++ b/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java @@ -9,6 +9,8 @@ import java.util.regex.Pattern; import java.util.zip.Deflater; import java.util.zip.DeflaterOutputStream; +import javax.annotation.Nonnull; + import org.incava.util.diff.Diff; import com.mostc.pftt.host.Host; @@ -122,7 +124,7 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu // if (host.isWindows() && test_case.containsSection(EPhptSection.SKIPIF)) { // do an early quick check - // fixes problem with Sapi/cli/tests/021.phpt + // fixes problem with sapi/cli/tests/021.phpt if (test_case.get(EPhptSection.SKIPIF).contains("skip not for Windows")) { twriter.addResult(new PhptTestResult(host, EPhptTestStatus.XSKIP, test_case, "skip not for Windows", null, null, null, null, null, null, null, null, null, null)); @@ -458,10 +460,13 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu protected abstract void prepareSTDIN() throws IOException; /** executes the test (the TEST section of PhptTestCase) and returns the actual output + * + * must not return null * * @return * @throws Exception */ + @Nonnull protected abstract String executeTest() throws Exception; /** executes CLEAN section of test to cleanup after execution @@ -512,7 +517,7 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu twriter.show_exception(test_case, ex, expected); throw ex; } - if (expected_re_match) { + if (expected_re_match||check()) { twriter.addResult(new PhptTestResult(host, test_case.isXFail()?EPhptTestStatus.XFAIL:EPhptTestStatus.PASS, test_case, output, null, null, charset, env, splitCmdString(), stdin_post, shell_script, null, null, null)); @@ -524,7 +529,7 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu output = remove_header_from_output(output); output_trim = output.trim(); - if (output_trim.equals(expected)||output_trim.contains(expected)||expected.contains(output_trim)) { + if (output_trim.equals(expected)||output_trim.contains(expected)||expected.contains(output_trim)||check()) { twriter.addResult(new PhptTestResult(host, test_case.isXFail()?EPhptTestStatus.XFAIL:EPhptTestStatus.PASS, test_case, output, null, null, charset, env, splitCmdString(), stdin_post, shell_script, null, null, null)); @@ -534,9 +539,13 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu preoverride_actual = output_trim; output_trim = PhptOverrideManager.replaceWithExactOverrides(host, output_trim); - if (output_trim!=null) { + if (output_trim==null) { + // no output overrides for this phpt on this OS + // + // fall through to failing or xfailing the test + } else { // compare again - if (output_trim.equals(expected)||output_trim.contains(expected)||expected.contains(output_trim)||(output_trim.length()>20&&expected.length()>20&&output_trim.substring(10, 20).equals(expected.substring(10, 20)))) { + if (output_trim.equals(expected)||output_trim.contains(expected)||expected.contains(output_trim)||(output_trim.length()>20&&expected.length()>20&&output_trim.substring(10, 20).equals(expected.substring(10, 20)))||check()) { twriter.addResult(new PhptTestResult(host, test_case.isXFail()?EPhptTestStatus.XFAIL:EPhptTestStatus.PASS, test_case, output, null, null, charset, env, splitCmdString(), stdin_post, shell_script, null, null, null)); @@ -547,7 +556,7 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu output = remove_header_from_output(output); output_trim = output.trim(); - if (StringUtil.isEmpty(output_trim)) { + if (StringUtil.isEmpty(output_trim)||check()) { twriter.addResult(new PhptTestResult(host, test_case.isXFail()?EPhptTestStatus.XFAIL:EPhptTestStatus.PASS, test_case, output, null, null, charset, env, splitCmdString(), stdin_post, shell_script, null, null, null)); return; @@ -577,6 +586,11 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu twriter.addResult(new PhptTestResult(host, EPhptTestStatus.FAIL, test_case, output, actual_lines, expected_lines, charset, env, splitCmdString(), stdin_post, shell_script, diff, expectf, preoverride_actual, getCrashedSAPIOutput())); } } // end void evalTest + + protected boolean check() { + // TODO temp + return StringUtil.isEmpty(getCrashedSAPIOutput()); + } protected abstract String[] splitCmdString(); diff --git a/src/com/mostc/pftt/runner/HttpTestCaseRunner.java b/src/com/mostc/pftt/runner/HttpTestCaseRunner.java index 03aa9f2..311f071 100644 --- a/src/com/mostc/pftt/runner/HttpTestCaseRunner.java +++ b/src/com/mostc/pftt/runner/HttpTestCaseRunner.java @@ -3,30 +3,18 @@ package com.mostc.pftt.runner; import java.io.IOException; import java.net.Socket; -import org.apache.http.ConnectionReuseStrategy; import org.apache.http.HttpHost; -import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; -import org.apache.http.HttpVersion; import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.DefaultHttpClientConnection; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; import org.apache.http.params.HttpParams; -import org.apache.http.params.HttpProtocolParams; -import org.apache.http.params.SyncBasicHttpParams; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpProcessor; import org.apache.http.protocol.HttpRequestExecutor; -import org.apache.http.protocol.ImmutableHttpProcessor; -import org.apache.http.protocol.RequestConnControl; -import org.apache.http.protocol.RequestContent; -import org.apache.http.protocol.RequestExpectContinue; -import org.apache.http.protocol.RequestTargetHost; -import org.apache.http.protocol.RequestUserAgent; import com.github.mattficken.io.IOUtil; import com.mostc.pftt.host.Host; @@ -41,6 +29,7 @@ import com.mostc.pftt.runner.PhptTestPackRunner.PhptThread; import com.mostc.pftt.scenario.ScenarioSet; import com.mostc.pftt.telemetry.PhptTelemetryWriter; import com.mostc.pftt.telemetry.PhptTestResult; +import com.mostc.pftt.util.ErrorUtil; /** Runs PHPT Test Cases against PHP while its running under a Web Server (builtin, IIS or Apache) * @@ -50,10 +39,16 @@ import com.mostc.pftt.telemetry.PhptTestResult; public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { protected final WebServerManager smgr; + protected final HttpParams params; + protected final HttpProcessor httpproc; + protected final HttpRequestExecutor httpexecutor; protected WebServerInstance web = null; - public HttpTestCaseRunner(WebServerManager smgr, WebServerInstance web, PhptThread thread, PhptTestCase test_case, PhptTelemetryWriter twriter, Host host, ScenarioSet scenario_set, PhpBuild build, PhptTestPack test_pack) { + public HttpTestCaseRunner(HttpParams params, HttpProcessor httpproc, HttpRequestExecutor httpexecutor, WebServerManager smgr, WebServerInstance web, PhptThread thread, PhptTestCase test_case, PhptTelemetryWriter twriter, Host host, ScenarioSet scenario_set, PhpBuild build, PhptTestPack test_pack) { super(web.getPhpIni(), thread, test_case, twriter, host, scenario_set, build, test_pack); + this.params = params; + this.httpproc = httpproc; + this.httpexecutor = httpexecutor; this.smgr = smgr; this.web = web; } @@ -71,96 +66,95 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { return false; } - @Override - public boolean prepare() throws IOException, Exception { - if (!super.prepare()) - return false; - - // #super.prepare sets ini already - // - // all we need to do is manage the web server and http requests - // - // make sure a web server is running - web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); - - return true; - } - /** executes SKIPIF, TEST or CLEAN over http. * * retries request if it times out and restarts web server if it crashes * * @param path - * @param is_test + * @param section * @return * @throws Exception */ - protected String http_execute(String path, boolean is_test) throws Exception { + protected String http_execute(String path, EPhptSection section) throws Exception { try { - // "PFTT: server failed to respond at all after ONE_MINUTE. server was restarted and failed to respond at all a second time after ONE_MINUTE"; - // "PFTT: server failed to send all of its response after ONE_MINUTE. server was restarted and failed to send all of its response a second time after ONE_MINUTE"; try { - return do_http_execute(path, is_test); + return do_http_execute(path, section, false); } catch ( IOException ex1 ) { // SocketTimeoutException or ConnectException + // notify of crash so it gets reported everywhere + web.notifyCrash("PFTT: timeout during test("+section+" SECTION): "+test_case.getName()+"\n"+ErrorUtil.toString(ex1), 0); + // TODO temp VS web.close(); - // stop web server, try again - web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); - try { - return do_http_execute(path, is_test); - } catch ( IOException ex2 ) { // SocketTimeoutException or ConnectException - web.close(); - // stop web server, try again - web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); - try { - return do_http_execute(path, is_test); - } catch ( IOException ex3 ) { // SocketTimeoutException or ConnectException - web.close(); - // stop web server, try again - // TODO temp null - web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); - try { - return do_http_execute(path, is_test); - } finally { - web.close(); - } - } - } + + System.out.println("RESTART_AND_RETRY "+test_case.getName()); + + // get #do_http_execute to make a new server + // this will make a new WebServerInstance that will only be used to run this 1 test + // (so other tests will not interfere with this test at all) + web = null; + return do_http_execute(path, section, true); } } catch ( IOException ioe ) { - // wrap IOException with list of tests that are running against this web server - // so we can tell what test(s) may be causing server to not respond + String ex_str = ErrorUtil.toString(ioe); - // TODO comment + // notify web server that it crashed. it will record this, which will be accessible + // with WebServerInstance#getSAPIOutput (will be recorded by PhptTelemetryWriter) + web.notifyCrash("PFTT: IOException during test("+section+" SECTION): "+test_case.getName()+"\n"+ex_str, 0); - web.notifyCrash("", 0); + // generate a failure string here too though, so that this TEST or SKIPIF section is marked as a failure + StringBuilder sb = new StringBuilder(512); + sb.append("PFTT: couldn't connect to server after One Minute\n"); + sb.append("PFTT: created new server only for running this test which did not respond after another One Minute timeout\n"); + sb.append("PFTT: was trying to run ("+section+" section of): "); + sb.append(test_case.getName()); + sb.append("\n"); + sb.append("PFTT: these two lists refer only to second server (created for specifically for only this test)\n"); + web.getActiveTestListString(sb); + web.getAllTestListString(sb); - String ex_str = "PFTT: couldn't connect to server after 4 tries (and 3 restarts), assuming crashed.\nPFTT: was trying to test (TEST section of): "+test_case.getName()+"\n"+web.getActiveTestListString(); - - if (is_test) { - this.twriter.addResult(new PhptTestResult(host, EPhptTestStatus.EXCEPTION, test_case, ex_str, null, null, null, null, null, null, null, null, null, null, ex_str)); - - return ex_str; - } - - throw new IOException(ex_str, ioe); + // if TEST, runner will evaluate this as a failure + // if SKIPIF, runner will not skip test and will try to run it + // + // both are the most ideal behavior possible in this situation + // + // normally this shouldn't happen, so checking a string once in a while is faster than + // setting a flag here and checking that flag for every test in #evalTest + return sb.toString(); } } // end protected String http_execute - protected String do_http_execute(String path, boolean is_test) throws Exception { - // make sure a web server is running - web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); - if (web.isCrashed()) - // test will fail - return "PFTT: server crashed already, didn't bother trying to execute test"; - - if (stdin_post==null) - return do_http_get(path); - else - return do_http_post(path); + protected String do_http_execute(String path, EPhptSection section, boolean is_replacement) throws Exception { + { + WebServerInstance _web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); + if (_web!=web) { + this.web = _web; + is_replacement = true; + // make sure this test case is in the list + _web.notifyTestPreRequest(test_case); + } + } + try { + if (web.isCrashed()) + // test will fail (because this(`PFTT: server...`) is the actual output which won't match the expected output) + // + // return server's crash output and an additional message about this test + return web.getSAPIOutput() + "PFTT: server crashed already, didn't bother trying to execute test: "+test_case.getName(); + + + if (stdin_post==null || section != EPhptSection.TEST) + return do_http_get(path); + else + // only do POST for TEST sections where stdin_post!=null + return do_http_post(path); + } finally { + if (is_replacement) + // CRITICAL: if this WebServerInstance is a replacement, then it exists only within this specific HttpTestCaseRunner + // instance. if it is not terminated here, it will keep running forever! + web.close(); + } } protected String do_http_get(String path) throws Exception { - HttpParams params = new SyncBasicHttpParams(); + /*HttpParams params = new SyncBasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, "UTF-8"); HttpProtocolParams.setUserAgent(params, "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/ 20120405 Firefox/14.0.1"); @@ -175,13 +169,13 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { new RequestUserAgent(), new RequestExpectContinue()}); - HttpRequestExecutor httpexecutor = new HttpRequestExecutor(); + HttpRequestExecutor httpexecutor = new HttpRequestExecutor();*/ HttpContext context = new BasicHttpContext(null); HttpHost http_host = new HttpHost(web.hostname(), web.port()); DefaultHttpClientConnection conn = new DefaultHttpClientConnection(); - ConnectionReuseStrategy connStrategy = new DefaultConnectionReuseStrategy(); + //ConnectionReuseStrategy connStrategy = new DefaultConnectionReuseStrategy(); context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, http_host); @@ -213,13 +207,13 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { } // end protected String do_http_get protected String do_http_post(String path) throws Exception { - HttpParams params = new SyncBasicHttpParams(); + /*HttpParams params = new SyncBasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, "UTF-8"); HttpProtocolParams.setUserAgent(params, "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/ 20120405 Firefox/14.0.1"); HttpProtocolParams.setUseExpectContinue(params, true); - if (content_type!=null) - params.setParameter("Content-Type", content_type); + // TODO if (content_type!=null) + // params.setParameter("Content-Type", content_type); HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {// XXX reuse // Required protocol interceptors @@ -230,13 +224,13 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { new RequestUserAgent(), new RequestExpectContinue()}); - HttpRequestExecutor httpexecutor = new HttpRequestExecutor(); + HttpRequestExecutor httpexecutor = new HttpRequestExecutor();*/ HttpContext context = new BasicHttpContext(null); HttpHost http_host = new HttpHost(web.hostname(), web.port()); DefaultHttpClientConnection conn = new DefaultHttpClientConnection(); - ConnectionReuseStrategy connStrategy = new DefaultConnectionReuseStrategy(); + //ConnectionReuseStrategy connStrategy = new DefaultConnectionReuseStrategy(); context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, http_host); @@ -280,17 +274,17 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { @Override protected String executeSkipIf() throws Exception { - return http_execute(test_skipif, false); + return http_execute(test_skipif, EPhptSection.SKIPIF); } @Override protected String executeTest() throws Exception { - return http_execute(test_file, true); + return http_execute(test_file, EPhptSection.TEST); } @Override protected void executeClean() throws Exception { - http_execute(test_clean, false); + http_execute(test_clean, EPhptSection.CLEAN); } @Override diff --git a/src/com/mostc/pftt/runner/PhptTestPackRunner.java b/src/com/mostc/pftt/runner/PhptTestPackRunner.java index 52e1ccd..44db4be 100644 --- a/src/com/mostc/pftt/runner/PhptTestPackRunner.java +++ b/src/com/mostc/pftt/runner/PhptTestPackRunner.java @@ -31,7 +31,7 @@ import com.mostc.pftt.util.HostEnvUtil; */ public class PhptTestPackRunner extends AbstractTestPackRunner { - protected static final int MAX_THREAD_COUNT = 48; + protected static final int MAX_THREAD_COUNT = 64; // TODO 32 protected final PhptTestPack test_pack; protected final PhptTelemetryWriter twriter; protected ETestPackRunnerState runner_state; @@ -208,6 +208,8 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { // execute any remaining thread safe jobs runThreadSafe(); + } catch ( Exception ex ) { + ex.printStackTrace(); } finally { if (run_thread.get()) // if #stopThisThread not called @@ -293,16 +295,10 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { twriter.show_exception(test_case, ex); } - if (counter>10) { - // TODO temp - ((WebServerInstance)ini).close(); - - // critical: provide existing WebServerInstance to #getWebServerInstance - // or will get multiple zombie php.exe web servers, etc... - ini = ((AbstractWebServerScenario)sapi_scenario).smgr.getWebServerInstance(host, build, ((WebServerInstance)ini).getPhpIni(), test_pack.getTestPack(), ((WebServerInstance)ini)); - - counter = 0; - } + // @see HttpTestCaseRunner#http_execute which calls #notifyCrash + // make sure a WebServerInstance is still running here, so it will be shared with each + // test runner instance (otherwise each test runner will create its own instance, which is slow) + ini = ((AbstractWebServerScenario)sapi_scenario).smgr.getWebServerInstance(host, build, ((WebServerInstance)ini).getPhpIni(), test_pack.getTestPack(), ((WebServerInstance)ini)); counter++; diff --git a/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java b/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java index f0e4afd..41bc6c4 100644 --- a/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java +++ b/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java @@ -1,5 +1,19 @@ package com.mostc.pftt.scenario; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.HttpVersion; +import org.apache.http.params.HttpParams; +import org.apache.http.params.HttpProtocolParams; +import org.apache.http.params.SyncBasicHttpParams; +import org.apache.http.protocol.HttpProcessor; +import org.apache.http.protocol.HttpRequestExecutor; +import org.apache.http.protocol.ImmutableHttpProcessor; +import org.apache.http.protocol.RequestConnControl; +import org.apache.http.protocol.RequestContent; +import org.apache.http.protocol.RequestExpectContinue; +import org.apache.http.protocol.RequestTargetHost; +import org.apache.http.protocol.RequestUserAgent; + import com.mostc.pftt.host.Host; import com.mostc.pftt.model.phpt.PhpBuild; import com.mostc.pftt.model.phpt.PhptTestCase; @@ -21,29 +35,40 @@ import com.mostc.pftt.telemetry.PhptTelemetryWriter; public abstract class AbstractWebServerScenario extends AbstractSAPIScenario { public final WebServerManager smgr; + protected final HttpParams params; + protected final HttpProcessor httpproc; + protected final HttpRequestExecutor httpexecutor; protected AbstractWebServerScenario(WebServerManager smgr) { this.smgr = smgr; + + params = new SyncBasicHttpParams(); + HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); + HttpProtocolParams.setContentCharset(params, "UTF-8"); + HttpProtocolParams.setUserAgent(params, "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/ 20120405 Firefox/14.0.1"); + HttpProtocolParams.setUseExpectContinue(params, true); + + httpproc = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {// XXX reuse + // Required protocol interceptors + new RequestContent(), + new RequestTargetHost(), + // Recommended protocol interceptors + new RequestConnControl(), + new RequestUserAgent(), + new RequestExpectContinue()}); + + httpexecutor = new HttpRequestExecutor(); } @Override public AbstractPhptTestCaseRunner createPhptTestCaseRunner(PhptThread thread, TestCaseGroupKey ini, PhptTestCase test_case, PhptTelemetryWriter twriter, Host host, ScenarioSet scenario_set, PhpBuild build, PhptTestPack test_pack) { - return new HttpTestCaseRunner(smgr, (WebServerInstance)ini, thread, test_case, twriter, host, scenario_set, build, test_pack); + return new HttpTestCaseRunner(params, httpproc, httpexecutor, smgr, (WebServerInstance)ini, thread, test_case, twriter, host, scenario_set, build, test_pack); } @Override public void close() { smgr.close(); } - - /*WebServerInstance web; // TODO temp - @Override - public synchronized TestCaseGroupKey createTestGroupKey(Host host, PhpBuild build, PhptTestPack test_pack, PhptTestCase test_case) { - // TODO use HttpTestCaseRunner to get docroot from test_pack - if (web!=null) - return web; - return web = smgr.getWebServerInstance(host, build, AbstractPhptTestCaseRunner.createIniForTest(host, build, test_pack, test_case), test_pack.getTestPack(), null); - }*/ public boolean willSkip(PhptTelemetryWriter twriter, Host host, PhpBuild build, PhptTestCase test_case) throws Exception { return HttpTestCaseRunner.willSkip(twriter, host, build, test_case); diff --git a/src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java b/src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java index c685de0..7eddeed 100644 --- a/src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java +++ b/src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java @@ -47,7 +47,7 @@ public class BuiltinWebServerScenario extends AbstractWebServerScenario { @Override public int getTestThreadCount(Host host) { // XXX update this calculation from time to time as this web server's performance improves - return 8 * (Math.max(2, host.getCPUCount())/2); + return 64; // TODO 8 * (Math.max(2, host.getCPUCount())/2); } } diff --git a/src/com/mostc/pftt/telemetry/ConsoleManager.java b/src/com/mostc/pftt/telemetry/ConsoleManager.java new file mode 100644 index 0000000..75ce99d --- /dev/null +++ b/src/com/mostc/pftt/telemetry/ConsoleManager.java @@ -0,0 +1,23 @@ +package com.mostc.pftt.telemetry; + +import com.mostc.pftt.model.phpt.PhptTestCase; +import com.mostc.pftt.model.phpt.EPhptTestStatus; + +public class ConsoleManager { + + public ConsoleManager(boolean result_only) { + + } + + public void finishedTest(PhptTestCase test_case, EPhptTestStatus status) { + + } + + public void retryingTest(PhptTestCase test_case) { + + } + + public void restartingSAPI() { + + } +} diff --git a/src/com/mostc/pftt/telemetry/PhptTestResult.java b/src/com/mostc/pftt/telemetry/PhptTestResult.java index 0c9d2e1..b869268 100644 --- a/src/com/mostc/pftt/telemetry/PhptTestResult.java +++ b/src/com/mostc/pftt/telemetry/PhptTestResult.java @@ -102,7 +102,7 @@ public class PhptTestResult { protected void write(File file, String text) throws IOException { try { - file.mkdirs(); + file.getParentFile().mkdirs(); FileWriter fw = new FileWriter(file); fw.write(text, 0, text.length()); diff --git a/src/com/mostc/pftt/ui/PhptHostTab.java b/src/com/mostc/pftt/ui/PhptHostTab.java index fafd770..a030392 100644 --- a/src/com/mostc/pftt/ui/PhptHostTab.java +++ b/src/com/mostc/pftt/ui/PhptHostTab.java @@ -111,7 +111,7 @@ public class PhptHostTab extends JSplitPane { panel.add(unsupported_label = new JLabel("0")); panel.add(new JLabel("Exceptions:")); panel.add(exceptions_label = new JLabel("0")); - panel.add(new JLabel("Crashes:")); + panel.add(new JLabel("Failed Retries:")); panel.add(crash_label = new JLabel("0")); /////////////// @@ -214,7 +214,7 @@ public class PhptHostTab extends JSplitPane { showList(exceptions_list_model); } }); list_button_group.add(list_exceptions_rb); - status_list_menu.add(list_crash_rb = new JRadioButtonMenuItem("Crash")); + status_list_menu.add(list_crash_rb = new JRadioButtonMenuItem("Failed Retries")); list_crash_rb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (list_crash_rb.isSelected()) showList(crash_list_model); diff --git a/src/com/mostc/pftt/util/HostEnvUtil.java b/src/com/mostc/pftt/util/HostEnvUtil.java index 3d31011..a4876fa 100644 --- a/src/com/mostc/pftt/util/HostEnvUtil.java +++ b/src/com/mostc/pftt/util/HostEnvUtil.java @@ -33,8 +33,10 @@ public final class HostEnvUtil { // have to fix Windows Error Reporting from popping up and blocking execution: // silence reporting System.out.println("PFTT: HostEnvUtil: disabling Windows Error Reporting..."); - boolean a = regQueryAdd(host, "HKCU\\Software\\Microsoft\\Windows\\Windows Error Reporting", "DontShowUI", "0x1", REG_DWORD); - boolean b = regQueryAdd(host, "HKCU\\Software\\Microsoft\\Windows\\Windows Error Reporting", "Disable", "0x1", REG_DWORD); + // TODO temp boolean a = regQueryAdd(host, "HKCU\\Software\\Microsoft\\Windows\\Windows Error Reporting", "DontShowUI", "0x1", REG_DWORD); + boolean a = regQueryAdd(host, "HKCU\\Software\\Microsoft\\Windows\\Windows Error Reporting", "DontShowUI", "0x0", REG_DWORD); + // TODO temp boolean b = regQueryAdd(host, "HKCU\\Software\\Microsoft\\Windows\\Windows Error Reporting", "Disable", "0x1", REG_DWORD); + boolean b = regQueryAdd(host, "HKCU\\Software\\Microsoft\\Windows\\Windows Error Reporting", "Disable", "0x0", REG_DWORD); // then, disable reporting if ( a || b ) { // assume if registry had to be edited, that firewall has to be disabled (avoid doing this if possible because it requires user to approve elevation) diff --git a/src/com/mostc/pftt/util/StringUtil.java b/src/com/mostc/pftt/util/StringUtil.java index 559c57e..4b0c6e6 100644 --- a/src/com/mostc/pftt/util/StringUtil.java +++ b/src/com/mostc/pftt/util/StringUtil.java @@ -15,6 +15,17 @@ import com.mostc.pftt.util.apache.regexp.REProgram; */ public final class StringUtil { + public static final String EMPTY = ""; + + public static String unquote(String in) { + if (isEmpty(in)) + return in; + while (in.startsWith("\"") || in.startsWith("'")) + in = in.substring(1); + while (in.endsWith("\"") || in.endsWith("'")) + in = in.substring(0, in.length()-1); + return in; + } public static boolean startsWithCS(String a, String b) { return a==null||b==null? false : a.startsWith(b); @@ -56,12 +67,18 @@ public final class StringUtil { return sb.toString(); } + public static final String[] EMPTY_ARRAY = new String[]{}; public static String[] splitLines(String str) { + if (str==null) + return EMPTY_ARRAY; str = replaceAll(PATTERN_R, "", str); return PATTERN_N.split(str); } public static String[] splitEqualsSign(String str) { + if (str==null) + return EMPTY_ARRAY; + return PATTERN_EQ.split(str); }