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

Reply via email to