Commit: 0eefbd1d6a964c952cce10a44a85855fa3a4272f Author: Matt Ficken <v-maf...@microsoft.com> Tue, 30 Oct 2012 11:12:04 -0700 Parents: 24866464cc1112328b3379f850abb410936e4811 Branches: master
Link: http://git.php.net/?p=pftt2.git;a=commitdiff;h=0eefbd1d6a964c952cce10a44a85855fa3a4272f Log: interim partially working http testing support Former-commit-id: 2e15bedb15bc91931ef84e20f4d57e0aee361ee8 Changed paths: M .classpath A lib/winp-1.14.jar A lib/winp.x64.dll M src/com/mostc/pftt/host/Host.java M src/com/mostc/pftt/host/LocalHost.java M src/com/mostc/pftt/model/phpt/EPhptTestStatus.java D src/com/mostc/pftt/model/sapi/BuiltinWebServerInstance.java M src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java M src/com/mostc/pftt/model/sapi/WebServerInstance.java M src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner.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/AbstractSAPIScenario.java M src/com/mostc/pftt/scenario/AbstractWebServerScenario.java M src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java M src/com/mostc/pftt/scenario/CLIScenario.java M src/com/mostc/pftt/telemetry/PhptTelemetryReader.java M src/com/mostc/pftt/telemetry/PhptTelemetryWriter.java M src/com/mostc/pftt/telemetry/PhptTestResult.java M src/com/mostc/pftt/ui/ExpectedActualDiffPHPTDisplay.java M src/com/mostc/pftt/ui/PhptHostTab.java
diff --git a/.classpath b/.classpath index 4bf0572..02cf268 100644 --- a/.classpath +++ b/.classpath @@ -24,5 +24,6 @@ <classpathentry kind="lib" path="lib/asm-tree-3.2.jar"/> <classpathentry kind="lib" path="lib/asm-util-3.2.jar"/> <classpathentry kind="lib" path="lib/j2ssh-core-0.2.9.jar"/> + <classpathentry kind="lib" path="lib/winp-1.14.jar"/> <classpathentry kind="output" path="build"/> </classpath> diff --git a/lib/winp-1.14.jar b/lib/winp-1.14.jar new file mode 100644 index 0000000..240382c Binary files /dev/null and b/lib/winp-1.14.jar differ diff --git a/lib/winp.x64.dll b/lib/winp.x64.dll new file mode 100644 index 0000000..f28aa19 Binary files /dev/null and b/lib/winp.x64.dll differ diff --git a/src/com/mostc/pftt/host/Host.java b/src/com/mostc/pftt/host/Host.java index 443bb71..9bb148e 100644 --- a/src/com/mostc/pftt/host/Host.java +++ b/src/com/mostc/pftt/host/Host.java @@ -615,5 +615,35 @@ public abstract class Host { public String getLocalhostServableAddress() { return isWindows() ? getAddress() : "127.0.0.1"; } + + /** returns number of CPUs on this host + * + * if exception, returns 1, always returns at least 1 + * + * @return + */ + public int getCPUCount() { + try { + if (isWindows()) + return Integer.parseInt(getEnvValue("NUMBER_OF_PROCESSORS")); + + int proc = 0; + ByLineReader r = readFile("/proc/cpuinfo"); + String line; + while (r.hasMoreLines()) { + line = r.readLine(); + if (line==null) + break; + + if (line.startsWith("processor")) + proc++; + } + return Math.max(1, proc); + } catch ( Exception ex ) { + ex.printStackTrace(); + + return 1; + } + } } // end public abstract class Host diff --git a/src/com/mostc/pftt/host/LocalHost.java b/src/com/mostc/pftt/host/LocalHost.java index 1b8f94b..efcc2e1 100644 --- a/src/com/mostc/pftt/host/LocalHost.java +++ b/src/com/mostc/pftt/host/LocalHost.java @@ -20,6 +20,8 @@ import java.util.Timer; import java.util.TimerTask; import java.util.regex.Pattern; +import org.jvnet.winp.WinProcess; + import com.github.mattficken.io.AbstractDetectingCharsetReader; import com.github.mattficken.io.ByLineReader; import com.github.mattficken.io.CharsetDeciderDecoder; @@ -30,6 +32,9 @@ import com.github.mattficken.io.NoCharsetByLineReader; import com.mostc.pftt.model.phpt.PhptTestCase; import com.mostc.pftt.runner.AbstractTestPackRunner.TestPackRunnerThread; import com.mostc.pftt.util.StringUtil; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinNT.HANDLE; /** Represents the local Host that the program is currently running on. * @@ -194,12 +199,11 @@ public class LocalHost extends Host { protected Charset charset; protected StringBuilder output_sb; - public LocalExecHandle(Process process, OutputStream stdin, InputStream stdout, InputStream stderr, ExitMonitorTask task, String cmdline) { + public LocalExecHandle(Process process, OutputStream stdin, InputStream stdout, InputStream stderr, String cmdline) { this.process = process; this.stdin = stdin; this.stdout = stdout; this.stderr = stderr; - this.task = task; this.cmdline = cmdline; } @@ -213,16 +217,97 @@ public class LocalHost extends Host { } } + boolean run = true; @Override public void close() { - for ( int i=0 ; i < 10 ; i++ ) { + 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.destroy(); - } catch ( Throwable ex ) { - ex.printStackTrace(); - } - } - } + process.exitValue(); + } catch ( Throwable t ) { + // hasn't exited yet + if (tries==0) { + // try closing streams to encourage it to exit + try { + stdin.close(); + } catch ( Throwable t2 ) { + t2.printStackTrace(); + } + try { + stdout.close(); + } catch ( Throwable t2 ) { + t2.printStackTrace(); + } + try { + stderr.close(); + } catch ( Throwable t2 ) { + t2.printStackTrace(); + } + } + + // kill it + if (isLocalhostWindows()) { + + try { + WinProcess wproc = new WinProcess(process); + wproc.killRecursively(); + } 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 + // + // kind of a hack to get the process id: + // look through hidden fields to find a field like java.lang.ProcessImpl#handle (long) + for (java.lang.reflect.Field f : process.getClass().getDeclaredFields() ) { + if (f.getType()==long.class) { // ProcessImpl#handle + // this is a private field. without this, #getLong will throw an IllegalAccessException + f.setAccessible(true); + + long handle = f.getLong(process); + + 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"); + + break; + } + } // end for + } catch ( Throwable t2 ) { + t2.printStackTrace(); + } // end try + } // end try + } // end if + // + + + // terminate through java Process API + // this is works on Linux and is a fallback on Windows + try { + process.destroy(); + } catch ( Throwable t2 ) { + t2.printStackTrace(); + } + // + + } // end try + } // end for + } // end public void close protected void run(Charset charset) throws IOException, InterruptedException { output_sb = new StringBuilder(1024); @@ -247,7 +332,7 @@ public class LocalHost extends Host { ByLineReader reader = charset == null ? new NoCharsetByLineReader(in) : new MultiCharsetByLineReader(in, d); String line; try { - while (reader.hasMoreLines()) { + while (reader.hasMoreLines()&&run) { line = reader.readLine(); if (line==null) break; @@ -356,95 +441,27 @@ public class LocalHost extends Host { InputStream stdout = process.getInputStream(); InputStream stderr = process.getErrorStream(); - ExitMonitorTask task = null; + LocalExecHandle h = new LocalExecHandle(process, stdin, stdout, stderr, StringUtil.toString(cmd_array)); + if (timeout>NO_TIMEOUT) { - task = new ExitMonitorTask(process,stdin, stdout, stderr); - timer.schedule(task, 5*1000); + h.task = new ExitMonitorTask(h); + timer.schedule(h.task, 5*1000); } - return new LocalExecHandle(process, stdin, stdout, stderr, task, StringUtil.toString(cmd_array)); + return h; } // end protected static LocalExecHandle exec_impl protected static class ExitMonitorTask extends TimerTask { - protected Process process; - protected OutputStream stdin; - protected InputStream stdout, stderr; + protected final LocalExecHandle h; - protected ExitMonitorTask(Process process, OutputStream stdin, InputStream stdout, InputStream stderr) { - this.process = process; - this.stdin = stdin; - this.stdout = stdout; - this.stderr = stderr; + protected ExitMonitorTask(LocalExecHandle h) { + this.h = h; } @Override public void run() { - // may take multiple tries to make it exit (lots of processes, certain OSes, etc...) - for ( int tries = 0 ; tries < 10 ; tries++ ) { - try { - process.exitValue(); - } catch ( Throwable t ) { - // hasn't exited yet - if (tries==0) { - // try closing streams to encourage it to exit - try { - stdin.close(); - } catch ( Throwable t2 ) { - t2.printStackTrace(); - } - try { - stdout.close(); - } catch ( Throwable t2 ) { - t2.printStackTrace(); - } - try { - stderr.close(); - } catch ( Throwable t2 ) { - t2.printStackTrace(); - } - } - - // kill it - if (isLocalhostWindows()) { - // process.destory doesn't always work on windows, but TASKKILL does - try { - // process.getClass() != Process.class - // - // kind of a hack to get the process id: - // look through hidden fields to find a field like java.lang.ProcessImpl#handle (long) - for (java.lang.reflect.Field f : process.getClass().getDeclaredFields() ) { - if (f.getType()==long.class) { - // this is a private field. without this, #getLong will throw an IllegalAccessException - f.setAccessible(true); - - long process_id = f.getLong(process); - - // 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"); - - break; - } - } - } catch ( Throwable t2 ) { - t2.printStackTrace(); - } - } - try { - process.destroy(); - } catch ( Throwable t2 ) { - t2.printStackTrace(); - } - - - - } // end try - } - } // end public void run + h.close(); + } } // end protected static class ExitMonitorTask diff --git a/src/com/mostc/pftt/model/phpt/EPhptTestStatus.java b/src/com/mostc/pftt/model/phpt/EPhptTestStatus.java index e90514c..6ca1afd 100644 --- a/src/com/mostc/pftt/model/phpt/EPhptTestStatus.java +++ b/src/com/mostc/pftt/model/phpt/EPhptTestStatus.java @@ -18,5 +18,5 @@ public enum EPhptTestStatus { /** test is malformed */ BORK, /** an internal PFTT error */ - INTERNAL_EXCEPTION + EXCEPTION } diff --git a/src/com/mostc/pftt/model/sapi/BuiltinWebServerInstance.java b/src/com/mostc/pftt/model/sapi/BuiltinWebServerInstance.java deleted file mode 100644 index c447057..0000000 --- a/src/com/mostc/pftt/model/sapi/BuiltinWebServerInstance.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mostc.pftt.model.sapi; - -import com.mostc.pftt.host.Host.ExecHandle; -import com.mostc.pftt.model.phpt.PhpIni; - -/** running instance of PHP's builtin web server. - * - * m - * - * @author Matt Ficken - * - */ - -public 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); - this.process = process; - this.hostname = hostname; - this.port = port; - } - - @Override - public String hostname() { - return hostname; - } - - @Override - public int port() { - return port; - } - - @Override - public void close() { - process.close(); - } - - @Override - public boolean isRunning() { - return process.isRunning(); - } - -} // end public class BuiltinWebServerInstance diff --git a/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java b/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java index e8b88a2..d7d3013 100644 --- a/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java +++ b/src/com/mostc/pftt/model/sapi/BuiltinWebServerManager.java @@ -9,6 +9,7 @@ import javax.annotation.concurrent.ThreadSafe; import com.mostc.pftt.host.Host; import com.mostc.pftt.host.LocalHost; +import com.mostc.pftt.host.Host.ExecHandle; import com.mostc.pftt.model.phpt.PhpBuild; import com.mostc.pftt.model.phpt.PhpIni; import com.mostc.pftt.util.ErrorUtil; @@ -34,6 +35,16 @@ public class BuiltinWebServerManager extends WebServerManager { public BuiltinWebServerManager() { timer = new Timer(); + + if (LocalHost.isLocalhostWindows()) { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // Windows BN: may leave `php.exe -S` running if they aren't terminated here + close(); + } + }); + } } @Override @@ -76,8 +87,7 @@ public class BuiltinWebServerManager extends WebServerManager { handle = host.execThread(cmd, docroot); final Host.ExecHandle handlef = handle; - new Exception().printStackTrace(); // TODO temp - + // ensure server can be connected to Socket sock = new Socket(hostname, last_port); if (!sock.isConnected()) @@ -125,7 +135,47 @@ public class BuiltinWebServerManager extends WebServerManager { // return this failure message to client code return new CrashedWebServerInstance(ini, sapi_output); } // end protected synchronized WebServerInstance createWebServerInstance + + public 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); + this.process = process; + this.hostname = hostname; + this.port = port; + } + + @Override + public String hostname() { + return hostname; + } + + @Override + public int port() { + return port; + } + @Override + public void close() { + try { + process.close(); + } finally { + synchronized(BuiltinWebServerManager.this.instances) { + BuiltinWebServerManager.this.instances.remove(this); + } + } + } + + @Override + public boolean isRunning() { + return process.isRunning(); + } + + } // end public class BuiltinWebServerInstance + @Override public boolean allowConcurrentWebServerSAPIInstances() { return true; diff --git a/src/com/mostc/pftt/model/sapi/WebServerInstance.java b/src/com/mostc/pftt/model/sapi/WebServerInstance.java index d1ee66f..f115cb9 100644 --- a/src/com/mostc/pftt/model/sapi/WebServerInstance.java +++ b/src/com/mostc/pftt/model/sapi/WebServerInstance.java @@ -82,14 +82,7 @@ public abstract class WebServerInstance extends SAPIInstance { StringBuilder sb = new StringBuilder(1024); sb.append("PFTT: web server crashed with exit code: "+exit_code); - synchronized(active_test_cases) { - sb.append("PFTT: while running these tests("+active_test_cases.size()+"):\n"); - for (PhptTestCase test_case : active_test_cases ) { - sb.append("PFTT: "); - sb.append(test_case.getName()); - sb.append('\n'); - } - } + getActiveTestListString(sb); if (StringUtil.isEmpty(output)) { sb.append("PFTT: web server returned no output when it exited.\n"); } else { @@ -101,6 +94,23 @@ public abstract class WebServerInstance extends SAPIInstance { } // end sync } // end protected void notifyCrash + protected 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 ) { + sb.append("PFTT: "); + sb.append(test_case.getName()); + sb.append('\n'); + } + } + } + + public String getActiveTestListString() { + StringBuilder sb = new StringBuilder(512); + getActiveTestListString(sb); + return sb.toString(); + } + /** called before HTTP request made to server for given test_case * * @param test_case diff --git a/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner.java b/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner.java index cd55764..019b4fc 100644 --- a/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner.java +++ b/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner.java @@ -27,11 +27,14 @@ public abstract class AbstractPhptTestCaseRunner { public abstract void runTest() throws IOException, Exception, Throwable; + static PhpIni _ini; public static PhpIni createIniForTest(Host host, PhpBuild build, PhptTestPack test_pack, PhptTestCase test_case) { - PhpIni ini = PhpIni.createDefaultIniCopy(host); - ini.replaceAll(test_case.getINI(test_pack, host)); - ini.addToIncludePath(host, test_pack.getTestPack()); - return ini; + if (_ini!=null) + return _ini; // TODO + _ini = PhpIni.createDefaultIniCopy(host); + _ini.replaceAll(test_case.getINI(test_pack, host)); + _ini.addToIncludePath(host, test_pack.getTestPack()); + return _ini; } /** TODO diff --git a/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java b/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java index df4e987..041d692 100644 --- a/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java +++ b/src/com/mostc/pftt/runner/AbstractPhptTestCaseRunner2.java @@ -118,6 +118,18 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu return false; } + + // + if (host.isWindows() && test_case.containsSection(EPhptSection.SKIPIF)) { + // do an early quick check + // 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)); + + return false; + } + } + // selected_php_exe = build.getPhpExe(); @@ -469,10 +481,6 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu */ protected abstract String getCrashedSAPIOutput(); - private boolean check() { - return !test_case.isNamed("ext/phar/tests/tar/phar_setsignaturealgo2.phpt"); - } - /** evaluates the output of the executed test and reports the result * * @param output @@ -504,7 +512,7 @@ public abstract class AbstractPhptTestCaseRunner2 extends AbstractPhptTestCaseRu twriter.show_exception(test_case, ex, expected); throw ex; } - if (expected_re_match||check()) { + if (expected_re_match) { 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)); @@ -516,7 +524,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)||check()) { + if (output_trim.equals(expected)||output_trim.contains(expected)||expected.contains(output_trim)) { 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)); diff --git a/src/com/mostc/pftt/runner/HttpTestCaseRunner.java b/src/com/mostc/pftt/runner/HttpTestCaseRunner.java index 8f4d47c..03aa9f2 100644 --- a/src/com/mostc/pftt/runner/HttpTestCaseRunner.java +++ b/src/com/mostc/pftt/runner/HttpTestCaseRunner.java @@ -96,31 +96,55 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { * @throws Exception */ protected String http_execute(String path, boolean is_test) throws Exception { - // "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); - } catch ( IOException ex1 ) { // SocketTimeoutException or ConnectException - web.close(); - // stop web server, try again - web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); + // "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); - } catch ( IOException ex2 ) { // SocketTimeoutException or ConnectException + } catch ( IOException ex1 ) { // 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 + } catch ( IOException ex2 ) { // SocketTimeoutException or ConnectException web.close(); // stop web server, try again web = smgr.getWebServerInstance(host, build, ini, test_pack.getTestPack(), web); - return do_http_execute(path, is_test); + 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(); + } + } } } + } 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 + + // TODO comment + + web.notifyCrash("", 0); + + 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); } - } + } // end protected String http_execute protected String do_http_execute(String path, boolean is_test) throws Exception { // make sure a web server is running @@ -165,8 +189,7 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { Socket socket = new Socket(http_host.getHostName(), http_host.getPort()); //socket.setSoTimeout(6*1000); conn.bind(socket, params); - conn.setSocketTimeout(2*1000); - // TODO support POST for some tests that do that + conn.setSocketTimeout(60*1000); // path = Host.toUnixPath(path); @@ -221,7 +244,7 @@ public class HttpTestCaseRunner extends AbstractPhptTestCaseRunner2 { Socket socket = new Socket(http_host.getHostName(), http_host.getPort()); //socket.setSoTimeout(6*1000); conn.bind(socket, params); - conn.setSocketTimeout(2*1000); + conn.setSocketTimeout(60*1000); // path = Host.toUnixPath(path); diff --git a/src/com/mostc/pftt/runner/PhptTestPackRunner.java b/src/com/mostc/pftt/runner/PhptTestPackRunner.java index a0cdb61..52e1ccd 100644 --- a/src/com/mostc/pftt/runner/PhptTestPackRunner.java +++ b/src/com/mostc/pftt/runner/PhptTestPackRunner.java @@ -9,11 +9,14 @@ import java.util.concurrent.atomic.AtomicInteger; import com.mostc.pftt.host.Host; import com.mostc.pftt.model.phpt.PhpBuild; +import com.mostc.pftt.model.phpt.PhpIni; import com.mostc.pftt.model.phpt.PhptTestCase; import com.mostc.pftt.model.phpt.PhptTestPack; import com.mostc.pftt.model.sapi.SAPIInstance; import com.mostc.pftt.model.sapi.TestCaseGroupKey; +import com.mostc.pftt.model.sapi.WebServerInstance; import com.mostc.pftt.scenario.AbstractSAPIScenario; +import com.mostc.pftt.scenario.AbstractWebServerScenario; import com.mostc.pftt.scenario.ScenarioSet; import com.mostc.pftt.telemetry.PhptTelemetryWriter; import com.mostc.pftt.util.HostEnvUtil; @@ -28,8 +31,7 @@ import com.mostc.pftt.util.HostEnvUtil; */ public class PhptTestPackRunner extends AbstractTestPackRunner { - protected static final int INIT_THREAD_COUNT = 8; // TODO 16; - protected static final int MAX_THREAD_COUNT = 32; + protected static final int MAX_THREAD_COUNT = 48; protected final PhptTestPack test_pack; protected final PhptTelemetryWriter twriter; protected ETestPackRunnerState runner_state; @@ -67,10 +69,8 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { } public void close() { - for ( TestCaseGroupKey group_key : group_keys ) { - if (group_key instanceof SAPIInstance) - ((SAPIInstance)group_key).close(); - } + ((AbstractWebServerScenario)sapi_scenario).smgr.close(); + sapi_scenario.close(); } protected void groupTestCases(List<PhptTestCase> test_cases) throws InterruptedException { @@ -137,17 +137,22 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { } // end protected void groupTestCases protected void parallelSAPIInstance_executeTestCases() throws InterruptedException { - int thread_count = INIT_THREAD_COUNT; + int thread_count = Math.min(MAX_THREAD_COUNT, sapi_scenario.getTestThreadCount(host)); test_count = new AtomicInteger(0); active_thread_count = new AtomicInteger(thread_count); - thread_count = INIT_THREAD_COUNT; + long start_time = System.currentTimeMillis(); + for ( int i=0 ; i < thread_count ; i++ ) { start_thread(); } // wait until done int c ; while ( ( c = active_thread_count.get() ) > 0 ) { Thread.sleep(c>3?1000:50); } + + long run_time = Math.abs(System.currentTimeMillis() - start_time); + + System.out.println((run_time/1000)+" seconds"); } // end protected void parallelSAPIInstance_executeTestCases /*protected void serialSAPIInstance_executeTestCases() throws InterruptedException { @@ -225,8 +230,15 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { if (jobs.isEmpty()) group_it.remove(); } - if (group_key!=null) + if (group_key!=null && !jobs.isEmpty()) { + // TODO temp + group_key = ((AbstractWebServerScenario)sapi_scenario).smgr.getWebServerInstance(host, build, (PhpIni)group_key, test_pack.getTestPack(), null); + exec_jobs(group_key, jobs, test_count); + + // TODO temp + ((WebServerInstance)group_key).close(); + } } } // end protected void runThreadSafe @@ -267,6 +279,7 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { protected void exec_jobs(TestCaseGroupKey ini, LinkedBlockingQueue<PhptTestCase> jobs, AtomicInteger test_count) { PhptTestCase test_case; + int counter = 0; while ( ( test_case = jobs.poll() ) != null && @@ -280,6 +293,19 @@ 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; + } + + counter++; + test_count.incrementAndGet(); //Thread.yield() @@ -316,4 +342,4 @@ public class PhptTestPackRunner extends AbstractTestPackRunner { return runner_state; } -} // end class TestPackRunner +} // end public class PhptTestPackRunner diff --git a/src/com/mostc/pftt/scenario/AbstractSAPIScenario.java b/src/com/mostc/pftt/scenario/AbstractSAPIScenario.java index 8475fcc..ccb1cb4 100644 --- a/src/com/mostc/pftt/scenario/AbstractSAPIScenario.java +++ b/src/com/mostc/pftt/scenario/AbstractSAPIScenario.java @@ -38,10 +38,20 @@ public abstract class AbstractSAPIScenario extends AbstractSerialScenario { // TODO rename ini public abstract AbstractPhptTestCaseRunner createPhptTestCaseRunner(PhptThread thread, TestCaseGroupKey ini, PhptTestCase test_case, PhptTelemetryWriter twriter, Host host, ScenarioSet scenario_set, PhpBuild build, PhptTestPack test_pack); - public abstract TestCaseGroupKey createTestGroupKey(Host host, PhpBuild build, PhptTestPack test_pack, PhptTestCase test_case); - public boolean willSkip(PhptTelemetryWriter twriter, Host host, PhpBuild build, PhptTestCase test_case) throws Exception { return AbstractPhptTestCaseRunner.willSkip(twriter, host, build, test_case); } + + public TestCaseGroupKey createTestGroupKey(Host host, PhpBuild build, PhptTestPack test_pack, PhptTestCase test_case) { + return AbstractPhptTestCaseRunner.createIniForTest(host, build, test_pack, test_case); + } + + public void close() { + + } + + public int getTestThreadCount(Host host) { + return 8; + } } diff --git a/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java b/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java index 4f3fa99..f0e4afd 100644 --- a/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java +++ b/src/com/mostc/pftt/scenario/AbstractWebServerScenario.java @@ -20,7 +20,7 @@ import com.mostc.pftt.telemetry.PhptTelemetryWriter; */ public abstract class AbstractWebServerScenario extends AbstractSAPIScenario { - protected final WebServerManager smgr; + public final WebServerManager smgr; protected AbstractWebServerScenario(WebServerManager smgr) { this.smgr = smgr; @@ -31,14 +31,19 @@ public abstract class AbstractWebServerScenario extends AbstractSAPIScenario { return new HttpTestCaseRunner(smgr, (WebServerInstance)ini, thread, test_case, twriter, host, scenario_set, build, test_pack); } - WebServerInstance web; // TODO temp + @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 55b9466..c685de0 100644 --- a/src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java +++ b/src/com/mostc/pftt/scenario/BuiltinWebServerScenario.java @@ -43,5 +43,11 @@ public class BuiltinWebServerScenario extends AbstractWebServerScenario { public boolean isImplemented() { return true; } + + @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); + } } diff --git a/src/com/mostc/pftt/scenario/CLIScenario.java b/src/com/mostc/pftt/scenario/CLIScenario.java index 7844719..d709b42 100644 --- a/src/com/mostc/pftt/scenario/CLIScenario.java +++ b/src/com/mostc/pftt/scenario/CLIScenario.java @@ -37,9 +37,6 @@ public class CLIScenario extends AbstractSAPIScenario { return new CliPhptTestCaseRunner((PhpIni)ini, thread, test_case, twriter, host, scenario_set, build, test_pack); } - @Override - public TestCaseGroupKey createTestGroupKey(Host host, PhpBuild build, PhptTestPack test_pack, PhptTestCase test_case) { - return AbstractPhptTestCaseRunner.createIniForTest(host, build, test_pack, test_case); - } + } diff --git a/src/com/mostc/pftt/telemetry/PhptTelemetryReader.java b/src/com/mostc/pftt/telemetry/PhptTelemetryReader.java index b044a15..71e8e1e 100644 --- a/src/com/mostc/pftt/telemetry/PhptTelemetryReader.java +++ b/src/com/mostc/pftt/telemetry/PhptTelemetryReader.java @@ -98,7 +98,7 @@ public class PhptTelemetryReader extends PhptTelemetry { return tally.unsupported; case BORK: return tally.bork; - case INTERNAL_EXCEPTION: + case EXCEPTION: return tally.exception; } return 0; diff --git a/src/com/mostc/pftt/telemetry/PhptTelemetryWriter.java b/src/com/mostc/pftt/telemetry/PhptTelemetryWriter.java index 1eeb2c0..517fb5a 100644 --- a/src/com/mostc/pftt/telemetry/PhptTelemetryWriter.java +++ b/src/com/mostc/pftt/telemetry/PhptTelemetryWriter.java @@ -93,7 +93,7 @@ public class PhptTelemetryWriter extends PhptTelemetry { tally.xfail_works = counts.get(EPhptTestStatus.XFAIL_WORKS).get(); tally.unsupported = counts.get(EPhptTestStatus.UNSUPPORTED).get(); tally.bork = counts.get(EPhptTestStatus.BORK).get(); - tally.exception = counts.get(EPhptTestStatus.INTERNAL_EXCEPTION).get(); + tally.exception = counts.get(EPhptTestStatus.EXCEPTION).get(); FileWriter fw = new FileWriter(new File(telem_dir, "tally.xml")); PhptTallyFile.write(tally, fw); fw.close(); @@ -160,7 +160,7 @@ public class PhptTelemetryWriter extends PhptTelemetry { String ex_str = ErrorUtil.toString(ex); // count exceptions as a result (the worst kind of failure, a pftt failure) - addResult(new PhptTestResult(host, EPhptTestStatus.INTERNAL_EXCEPTION, test_case, ex_str, null, null, null, null, null, null, null, null, null, null)); + addResult(new PhptTestResult(host, EPhptTestStatus.EXCEPTION, test_case, ex_str, null, null, null, null, null, null, null, null, null, null)); // TODO show count of exceptions in gui } int completed = 0; // XXX diff --git a/src/com/mostc/pftt/telemetry/PhptTestResult.java b/src/com/mostc/pftt/telemetry/PhptTestResult.java index edfcee0..0c9d2e1 100644 --- a/src/com/mostc/pftt/telemetry/PhptTestResult.java +++ b/src/com/mostc/pftt/telemetry/PhptTestResult.java @@ -101,9 +101,15 @@ public class PhptTestResult { } protected void write(File file, String text) throws IOException { - FileWriter fw = new FileWriter(file); - fw.write(text, 0, text.length()); - fw.close(); + try { + file.mkdirs(); + + FileWriter fw = new FileWriter(file); + fw.write(text, 0, text.length()); + fw.close(); + } catch ( Exception ex ) { + ex.printStackTrace(); + } } protected void writeDiffFile(File telem_dir) throws IOException { diff --git a/src/com/mostc/pftt/ui/ExpectedActualDiffPHPTDisplay.java b/src/com/mostc/pftt/ui/ExpectedActualDiffPHPTDisplay.java index abe3f7f..438016d 100644 --- a/src/com/mostc/pftt/ui/ExpectedActualDiffPHPTDisplay.java +++ b/src/com/mostc/pftt/ui/ExpectedActualDiffPHPTDisplay.java @@ -53,8 +53,8 @@ public class ExpectedActualDiffPHPTDisplay extends JScrollPane { horizontal_panel = new JPanel(new BorderLayout()); horizontal_jsp.getViewport().setView(horizontal_panel); - horizontal_button_panel = new JPanel(new GridLayout(1, 5, 2, 2)); - vertical_panel = new JPanel(new GridLayout(1, 5, 2, 2)); + horizontal_button_panel = new JPanel(new GridLayout(1, 7, 2, 2)); + vertical_panel = new JPanel(new GridLayout(1, 7, 2, 2)); horizontal_panel.add(vertical_jsp = new JScrollPane(vertical_panel), BorderLayout.CENTER); vertical_jsp = this; @@ -94,7 +94,7 @@ public class ExpectedActualDiffPHPTDisplay extends JScrollPane { JPanel prepared_panel = new JPanel(new GridLayout(5, 1, 2, 2)); - vertical_panel.add(new JScrollPane(sapi_output_textarea)); + prepared_panel.add(new JScrollPane(sapi_output_textarea)); prepared_panel.add(new JScrollPane(stdin_data_textarea = new JTextArea())); stdin_data_textarea.setLineWrap(true); prepared_panel.add(new JScrollPane(shell_script_textarea = new JTextArea())); @@ -125,8 +125,10 @@ public class ExpectedActualDiffPHPTDisplay extends JScrollPane { } } cmd_array_list_model.clear(); - for (String cmd : test.cmd_array ) { - cmd_array_list_model.addElement(cmd); + if (test.cmd_array!=null) { + for (String cmd : test.cmd_array ) { + cmd_array_list_model.addElement(cmd); + } } if (test.stdin_data!=null) stdin_data_textarea.setText(new String(test.stdin_data)); diff --git a/src/com/mostc/pftt/ui/PhptHostTab.java b/src/com/mostc/pftt/ui/PhptHostTab.java index e112b70..fafd770 100644 --- a/src/com/mostc/pftt/ui/PhptHostTab.java +++ b/src/com/mostc/pftt/ui/PhptHostTab.java @@ -298,6 +298,7 @@ public class PhptHostTab extends JSplitPane { progress_bar.setMaximum(total); // + // count crashes - crash != test status though (crashed test will be counted as fail, pass, xfail, etc...) if (StringUtil.isNotEmpty(result.getSAPIOutput())) { crash++; crash_label.setText(Integer.toString(crash)); @@ -347,6 +348,12 @@ public class PhptHostTab extends JSplitPane { bork_list_model.addElement(result); break; + case EXCEPTION: + exceptions++; + exceptions_label.setText(Integer.toString(exceptions)); + + exceptions_list_model.addElement(result); + break; case PASS: pass++; pass_bar.setString(Float.toString(PhptTelemetry.round1( (float)( (double)pass / ((double)( pass + fail )) )))+"%"); // 1 decimal places nn.y