Commit: c20cec1c0a4eea3ea4edfa2c22552f7d4aa57ee5 Author: Matt Ficken <v-maf...@microsoft.com> Wed, 20 Nov 2013 12:36:19 -0800 Parents: 12b8407317b4abd66116e9c5ccd1f796d9818096 Branches: master
Link: http://git.php.net/?p=pftt2.git;a=commitdiff;h=c20cec1c0a4eea3ea4edfa2c22552f7d4aa57ee5 Log: Time Travel Tracing integration (Windows only) Former-commit-id: 1c507b3440740050eb6a11e8f07b4b42646d478f Changed paths: M conf/task/snap_test.groovy M src/com/mostc/pftt/host/AHost.java M src/com/mostc/pftt/host/ExecOutput.java A src/com/mostc/pftt/host/ICrashDetector.java M src/com/mostc/pftt/host/LocalHost.java M src/com/mostc/pftt/host/PosixLocalHost.java M src/com/mostc/pftt/host/WindowsLocalHost.java A src/com/mostc/pftt/main/CmpReport.java A src/com/mostc/pftt/main/CmpReport2.java A src/com/mostc/pftt/main/CmpReport2G.groovy A src/com/mostc/pftt/main/PBCReportGen.groovy M src/com/mostc/pftt/model/sapi/CliSAPIInstance.java M src/com/mostc/pftt/results/AbstractPhpUnitRW.java M src/com/mostc/pftt/results/AbstractPhptRW.java M src/com/mostc/pftt/results/AbstractTestResultRW.java M src/com/mostc/pftt/results/AbstractUITestRW.java M src/com/mostc/pftt/results/LocalConsoleManager.java M src/com/mostc/pftt/results/PhptResultWriter.java M src/com/mostc/pftt/runner/CliPhptTestCaseRunner.java M src/com/mostc/pftt/runner/LocalPhptTestPackRunner.java M src/com/mostc/pftt/scenario/CLIScenario.java M src/com/mostc/pftt/util/DebuggerManager.java M src/com/mostc/pftt/util/GDBDebugManager.java A src/com/mostc/pftt/util/TimeTravelTraceDebugManager.java M src/com/mostc/pftt/util/TimerUtil.java M src/com/mostc/pftt/util/ValgrindMemoryCheckManager.java M src/com/mostc/pftt/util/WinDebugManager.java A src/com/mostc/pftt/util/WindowsDebuggerToolsManager.java
diff --git a/conf/task/snap_test.groovy b/conf/task/snap_test.groovy index 06e5468..08a94e4 100644 --- a/conf/task/snap_test.groovy +++ b/conf/task/snap_test.groovy @@ -8,7 +8,7 @@ def processConsoleOptions(List options) { // options.add("-c") // test these SAPIs - options.add("apache,cli")//,builtin_web") + options.add("apache,cli") options.add("-c") // test with and without opcache options.add("opcache,no_code_cache,not_opcache_builtin_web") diff --git a/src/com/mostc/pftt/host/AHost.java b/src/com/mostc/pftt/host/AHost.java index e9adfc4..e37a3ba 100644 --- a/src/com/mostc/pftt/host/AHost.java +++ b/src/com/mostc/pftt/host/AHost.java @@ -15,6 +15,7 @@ import com.github.mattficken.io.ByLineReader; import com.github.mattficken.io.CharsetDeciderDecoder; import com.github.mattficken.io.IOUtil; import com.github.mattficken.io.StringUtil; +import com.mostc.pftt.results.AbstractTestResultRW; import com.mostc.pftt.results.ConsoleManager; import com.mostc.pftt.results.EPrintType; import com.mostc.pftt.runner.AbstractTestPackRunner.TestPackRunnerThread; @@ -390,8 +391,18 @@ public abstract class AHost extends Host implements IProgramRunner { public ExecOutput execElevatedOut(String cmd, int timeout_sec, String chdir) throws Exception { return execElevatedOut(cmd, timeout_sec, chdir, false); } + public interface IExecHandleCleanupNotify { + public void cleanupNotify(ExecHandle eh, AbstractTestResultRW rw); + } @ThreadSafe - public abstract class ExecHandle { + public abstract class ExecHandle implements ICrashDetector { + public IExecHandleCleanupNotify cleanup_notify; + public void cleanup(AbstractTestResultRW rw) { + if (cleanup_notify==null) + return; + + cleanup_notify.cleanupNotify(this, rw); + } public abstract InputStream getSTDOUT(); public abstract OutputStream getSTDIN(); /** KILLs process @@ -423,6 +434,7 @@ public abstract class AHost extends Host implements IProgramRunner { * * @return */ + @Override public boolean isCrashed() { return isCrashExitCode(AHost.this, getExitCode(), false); } diff --git a/src/com/mostc/pftt/host/ExecOutput.java b/src/com/mostc/pftt/host/ExecOutput.java index b2d49fb..a7f5b9b 100644 --- a/src/com/mostc/pftt/host/ExecOutput.java +++ b/src/com/mostc/pftt/host/ExecOutput.java @@ -7,7 +7,7 @@ import com.github.mattficken.io.StringUtil; import com.mostc.pftt.results.ConsoleManager; import com.mostc.pftt.results.EPrintType; -public class ExecOutput { +public class ExecOutput implements ICrashDetector { /** output of executed program */ public String output; /** character the program used for its output */ @@ -36,6 +36,7 @@ public class ExecOutput { public boolean isSuccess() { return exit_code == 0; } + @Override public boolean isCrashed() { return exit_code < -1; } diff --git a/src/com/mostc/pftt/host/ICrashDetector.java b/src/com/mostc/pftt/host/ICrashDetector.java new file mode 100644 index 0000000..3851c64 --- /dev/null +++ b/src/com/mostc/pftt/host/ICrashDetector.java @@ -0,0 +1,5 @@ +package com.mostc.pftt.host; + +public interface ICrashDetector { + public boolean isCrashed(); +} diff --git a/src/com/mostc/pftt/host/LocalHost.java b/src/com/mostc/pftt/host/LocalHost.java index 21e26fd..3ef655f 100644 --- a/src/com/mostc/pftt/host/LocalHost.java +++ b/src/com/mostc/pftt/host/LocalHost.java @@ -350,9 +350,9 @@ public abstract class LocalHost extends AHost { return; // @see WindowsLocalHost#exec_copy_lines run.set(false); - synchronized(run) { + /*synchronized(run) { run.notifyAll(); - } + }*/ // sometimes it can take a while to #close a process(especially on Windows)... do it in a thread // to avoid blocking for too long. however, we don't want to have too many threads @@ -431,9 +431,13 @@ public abstract class LocalHost extends AHost { // read process' output (block until #close or exit) exec_copy_lines(output_sb, max_chars, stdout, charset); // ignores STDERR - + for(;;) { + if (!doIsRunning(p)) + break; + Thread.sleep(50); + } // wait for process exit (shouldn't get here until exit or #close though) - for (int time = 50;wait.get();) { + /*for (int time = 50;wait.get();) { try { exit_code = p.exitValue(); break; @@ -458,7 +462,7 @@ public abstract class LocalHost extends AHost { break; } } - } + }*/ // active_proc_counter.decrementAndGet(); @@ -511,10 +515,7 @@ public abstract class LocalHost extends AHost { return exit_code; } - public int getProcessID() { - final Process p = process.get(); - return p!=null && isLocalhostWindows() ? getWindowsProcessID(p) : 0; - } + public abstract int getProcessID(); @Override public InputStream getSTDOUT() { @@ -532,7 +533,8 @@ public abstract class LocalHost extends AHost { public void run(ConsoleManager cm, StringBuilder output_sb, Charset charset, int timeout_sec, @SuppressWarnings("rawtypes") TestPackRunnerThread thread, int thread_slow_sec, int suspend_seconds, int max_chars) throws IOException, InterruptedException { TimerThread a = null, b = null; if (thread!=null && thread_slow_sec>NO_TIMEOUT) { - b = TimerUtil.waitSeconds(thread_slow_sec, new ThreadSlowTask(thread)); + // TODO get rid of thread_slow_sec feature - just use AbstractLocalTestPackRunner + //b = TimerUtil.waitSeconds(thread_slow_sec, new ThreadSlowTask(thread)); } if (timeout_sec>NO_TIMEOUT) { @@ -554,17 +556,6 @@ public abstract class LocalHost extends AHost { } // end public class LocalExecHandle - // TODO temp - public static int getWindowsProcessID(Process process) { - try { - // clean way - WinProcess wproc = new WinProcess(process); - return wproc.getPid(); - } catch ( Throwable wt ) { - return getWindowsProcessIDReflection(process); - } - } - protected static int getWindowsProcessIDReflection(Process process) { // WinProcess native code couldn't be loaded // (maybe it wasn't included or maybe somebody didn't compile it) diff --git a/src/com/mostc/pftt/host/PosixLocalHost.java b/src/com/mostc/pftt/host/PosixLocalHost.java index 0b14b30..60e59c3 100644 --- a/src/com/mostc/pftt/host/PosixLocalHost.java +++ b/src/com/mostc/pftt/host/PosixLocalHost.java @@ -40,6 +40,11 @@ public class PosixLocalHost extends LocalHost { return doIsRunning(p); } + public int getProcessID() { + final Process p = process.get(); + return 0; // TODO + } + protected void exec_copy_lines(final StringBuilder sb, final int max_chars, final InputStream in, final Charset charset) throws IOException { do_exec_copy_lines(sb, max_chars, in, charset); } diff --git a/src/com/mostc/pftt/host/WindowsLocalHost.java b/src/com/mostc/pftt/host/WindowsLocalHost.java index 0c160bd..225936c 100644 --- a/src/com/mostc/pftt/host/WindowsLocalHost.java +++ b/src/com/mostc/pftt/host/WindowsLocalHost.java @@ -13,7 +13,6 @@ import org.jvnet.winp.WinProcess; import com.github.mattficken.io.StringUtil; import com.mostc.pftt.host.CommonCommandManager.Win32ProcessInfo; -import com.mostc.pftt.main.PfttMain; import com.mostc.pftt.runner.AbstractTestPackRunner.TestPackRunnerThread; import com.mostc.pftt.util.TimerUtil; import com.mostc.pftt.util.TimerUtil.ObjectRunnable; @@ -93,11 +92,16 @@ public class WindowsLocalHost extends LocalHost { // IMPORTANT: this is how its identified in the Windows process table this.image_name = "cmd.exe"; } else { - this.image_name = this.image_name.toLowerCase(); - if (this.image_name.equals("cmd")) - this.image_name = "cmd.exe"; + this.image_name = basename(this.image_name).toLowerCase(); + if (image_name.indexOf('.')==-1) + this.image_name += ".exe"; } } + + public int getProcessID() { + final Process p = process.get(); + return p!=null ? getWindowsProcessID(p) : 0; + } @Override public boolean isRunning() { @@ -147,24 +151,31 @@ public class WindowsLocalHost extends LocalHost { try { do_exec_copy_lines(sb, max_chars, in, charset); copy_thread_lock.set(false); - synchronized(run) { + run.set(false); + /*synchronized(run) { run.notifyAll(); - } - } catch (IOException e) { - e.printStackTrace(); + }*/ + } catch (Throwable e) { + //e.printStackTrace(); } } }); copy_thread.setUncaughtExceptionHandler(IGNORE_EXCEPTION_HANDLER); while (wait.get()) { - synchronized(run) { + //synchronized(run) { try { - run.wait(30000); + //run.wait(30000); + Thread.sleep(300); } catch ( InterruptedException ex ) {} - } + //} if (!run.get()) { // try killing copy thread since its still running after it was supposed to stop - copy_thread.stop(new RuntimeException()); + copy_thread.stop(new RuntimeException() { + @Override + public void printStackTrace() { + + } + }); break; } else if (!copy_thread_lock.get()) { // stopped normally @@ -208,7 +219,9 @@ public class WindowsLocalHost extends LocalHost { // Windows BN?: if TerminateProcess() called with a PID that doesn't exist anymore (but might again soon) // does TerminateProcess() block forever (or does it appear that way because of Windows slow process // management?(Windows is optimized to run a few processes only (because thats what users did with it in the '90s))) - if (image_name.equals("taskkill.exe")||image_name.equals("handle.exe")||image_name.equals("pskill.exe")) { + String image_name = basename(this.image_name); + if (image_name.equals("taskkill.exe")||image_name.equals("handle.exe")||image_name.equals("pskill.exe") + ||image_name.equals("taskkill")||image_name.equals("handle")||image_name.equals("pskill")) { // can't use taskkill, could create an infinite loop // // @see https://github.com/kohsuke/winp @@ -220,12 +233,16 @@ public class WindowsLocalHost extends LocalHost { int pid = getWindowsProcessIDReflection(p); // NOT WinProcess#getPID? // for closing handles and other special stuff to work here, must get the actual process // not just cmd.exe (it won't have problems with stray handles, etc...) - if (image_name.equals("cmd.exe")) { + // + // IMPORTANT: #doClose may be called multiple times, so don't change this#doClose + if (image_name.equals("cmd.exe")||image_name.equals("cmd")) { List<Win32ProcessInfo> table = ccm.getWindowsProcessTable(WindowsLocalHost.this); for ( Win32ProcessInfo info : table ) { if (info.parent_pid==pid) { // found child - image_name = info.exe_path; + // + // IMPORTANT: basename() because TaskKill doesn't take paths for image name + image_name = basename(info.exe_path); pid = info.pid; } } @@ -250,28 +267,23 @@ public class WindowsLocalHost extends LocalHost { // @see AbstractPhptTestCaseRunner2#doRunTestClean ccm.winCloseAllHandles(WindowsLocalHost.this, pid); } - // can kill off windebug if running under PUTS (windebug shouldn't be running at all, sometimes does) - if (!PfttMain.is_puts && ccm.ensureWinDebugIsNotRunning(WindowsLocalHost.this, pid)) { - - // do nothing, wait for windebug - } else { - // provide TASKKILL the image name of the process to try avoiding killing the wrong process - try { - // /T => terminate child processes too - exec("TASKKILL /FI \"IMAGENAME eq "+image_name+"\" /FI \"PID eq "+pid+"\" /F /T", 20); - // also, WinProcess#killRecursively only checks by process id (not image/program name) - // while that should be enough, experience on Windows has shown that it isn't and somehow gets PFTT killed eventually - // - // Windows Note: Windows does NOT automatically terminate child processes when the parent gets killed - // the only way that happens is if you search for the child processes FIRST yourself, - // (and then their children, etc...) and then kill them. - } catch ( Exception ex ) { - // fallback - // - // @see https://github.com/kohsuke/winp - WinProcess wprocess = new WinProcess(p); - wprocess.kill(); - } + // provide TASKKILL the image name of the process to try avoiding killing the wrong process + try { + // /T => terminate child processes too + exec("TASKKILL /FI \"IMAGENAME eq "+image_name+"\" /FI \"PID eq "+pid+"\" /F /T", 20); + // also, WinProcess#killRecursively only checks by process id (not image/program name) + // while that should be enough, experience on Windows has shown that it isn't and somehow gets PFTT killed eventually + // + // Windows Note: Windows does NOT automatically terminate child processes when the parent gets killed + // the only way that happens is if you search for the child processes FIRST yourself, + // (and then their children, etc...) and then kill them. + } catch ( Exception ex ) { + ex.printStackTrace(); + // fallback + // + // @see https://github.com/kohsuke/winp + WinProcess wprocess = new WinProcess(p); + wprocess.kill(); } } } // end protected void doClose @@ -346,4 +358,14 @@ public class WindowsLocalHost extends LocalHost { return true; } // end public boolean mkdirs + public static int getWindowsProcessID(Process process) { + try { + // clean way + WinProcess wproc = new WinProcess(process); + return wproc.getPid(); + } catch ( Throwable wt ) { + return getWindowsProcessIDReflection(process); + } + } + } // end public class WindowsLocalHost diff --git a/src/com/mostc/pftt/main/CmpReport.java b/src/com/mostc/pftt/main/CmpReport.java new file mode 100644 index 0000000..2202046 --- /dev/null +++ b/src/com/mostc/pftt/main/CmpReport.java @@ -0,0 +1,553 @@ +package com.mostc.pftt.main; + +import groovy.xml.MarkupBuilder; + +import java.awt.Desktop; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.util.Collection; +import java.util.LinkedList; + +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPSClient; +import org.apache.commons.net.util.TrustManagerUtils; +import org.columba.ristretto.message.Address; +import org.columba.ristretto.parser.AddressParser; +import org.columba.ristretto.parser.ParserException; +import org.columba.ristretto.smtp.SMTPException; +import org.columba.ristretto.smtp.SMTPProtocol; + +import com.github.mattficken.io.ArrayUtil; +import com.github.mattficken.io.StringUtil; +import com.mostc.pftt.host.AHost; +import com.mostc.pftt.host.LocalHost; +import com.mostc.pftt.model.core.EPhptTestStatus; +import com.mostc.pftt.model.core.PhpBuildInfo; +import com.mostc.pftt.results.AbstractPhpUnitRW; +import com.mostc.pftt.results.AbstractPhptRW; +import com.mostc.pftt.results.AbstractTestResultRW; +import com.mostc.pftt.results.ConsoleManager; +import com.mostc.pftt.results.LocalConsoleManager; +import com.mostc.pftt.results.PhpResultPack; +import com.mostc.pftt.results.PhpResultPackReader; +import com.mostc.pftt.results.TextBuilder; +import com.mostc.pftt.util.EMailUtil; +import com.mostc.pftt.util.EMailUtil.ESMTPAuthMethod; +import com.mostc.pftt.util.EMailUtil.ESMTPSSL; + +public class CmpReport { + static String generateFileName(AbstractPhptRW base, AbstractPhptRW test) { + String file_name = "PHPT_CMP_" + +base.getBuildInfo().getBuildBranch()+"-"+base.getBuildInfo().getVersionRevision()+"-"+base.getBuildInfo().getBuildType()+"-"+base.getBuildInfo().getCPUArch()+"-"+base.getBuildInfo().getCompiler()+"_"+base.getScenarioSetNameWithVersionInfo()+ + "_v_" + +test.getBuildInfo().getBuildBranch()+"-"+test.getBuildInfo().getVersionRevision()+"-"+test.getBuildInfo().getBuildType()+"-"+test.getBuildInfo().getCPUArch()+"-"+test.getBuildInfo().getCompiler()+"_"+test.getScenarioSetNameWithVersionInfo(); + file_name = StringUtil.max(file_name, 100); + + return file_name + ".html"; + } + static String generateFileName(AbstractPhpUnitRW base, AbstractPhpUnitRW test) { + String file_name = "PhpUnit_CMP_"+test.getTestPackNameAndVersionString()+"_" + +base.getBuildInfo().getBuildBranch()+"-"+base.getBuildInfo().getVersionRevision()+"-"+base.getBuildInfo().getBuildType()+"-"+base.getBuildInfo().getCPUArch()+"-"+base.getBuildInfo().getCompiler()+"_"+base.getScenarioSetNameWithVersionInfo()+ + "_v_" + +test.getBuildInfo().getBuildBranch()+"-"+test.getBuildInfo().getVersionRevision()+"-"+test.getBuildInfo().getBuildType()+"-"+test.getBuildInfo().getCPUArch()+"-"+test.getBuildInfo().getCompiler()+"_"+test.getScenarioSetNameWithVersionInfo(); + file_name = StringUtil.max(file_name, 80); + + return file_name + ".html"; + } + static class Verify implements IRecvr { + + @Override + public void recv(AbstractPhptRW base, AbstractPhptRW test, PHPTMultiHostTwoBuildSingleScenarioSetReportGen phpt_report, String html_str) throws IOException { + File html_file = new File("c:\\php-sdk\\"+generateFileName(base, test)); + FileWriter fw = new FileWriter(html_file); + fw.write(html_str); + fw.close(); + + Desktop.getDesktop().browse(html_file.toURI()); + } + + @Override + public void recv(AbstractPhpUnitRW base, AbstractPhpUnitRW test, PhpUnitMultiHostTwoBuildSingleScenarioSetReportGen php_unit_report, String html_str) throws IOException { + File html_file = new File("c:\\php-sdk\\"+generateFileName(base, test)); + FileWriter fw = new FileWriter(html_file); + fw.write(html_str); + fw.close(); + Desktop.getDesktop().browse(html_file.toURI()); + } + + @Override + public void start(PhpResultPack test_pack) throws Exception {} + + @Override + public void stop(PhpResultPack test_pack) throws Exception {} + + } + static class Mailer { + final Address from, puts_admin; + final Address[] puts_users; + SMTPProtocol smtp; + final boolean debug, force; + LinkedList<String> message_bodies = new LinkedList<String>(); + final String phpt_prefix; + + Mailer(boolean debug, boolean force, Address[] puts_users) throws ParserException { + this("Core", debug, force, puts_users); + } + + Mailer(String phpt_prefix, boolean debug, boolean force, Address[] puts_users) throws ParserException { + this.phpt_prefix = phpt_prefix; + this.debug = debug; + this.force = force; + from = AddressParser.parseAddress("ostcp...@outlook.com"); + this.puts_users = puts_users; + puts_admin = AddressParser.parseAddress("v-maf...@microsoft.com"); + } + + protected void connect() throws IOException, SMTPException { + smtp = EMailUtil.connect("smtp.live.com", 587, from, ESMTPSSL.IMPLICIT_SSL, ESMTPAuthMethod.PLAIN, "ostcp...@outlook.com", "php868841432".toCharArray()); + } + + protected String getSubjectPrefix(Address to) { + String ma = to.getMailAddress(); + int i = ma.indexOf('@'); + if (i==-1) + return ""; + ma = ma.substring(0, i); + ma = ma.trim(); + if (ma.length()==0) + return ""; + else + return "["+ma.toUpperCase()+"] "; + } + protected void sendMail(boolean too_much_change, String subject, String html_str) throws IOException, SMTPException, Exception { + // prevent sending duplicate messages + if (message_bodies.contains(html_str)) { + return; + } + message_bodies.add(html_str); + Collection<Address> recipients; + if (!force && too_much_change) { + System.err.println("Debug: too much change, sending to admin only"); + recipients = ArrayUtil.toList(puts_admin); + subject = "[PUTS] Review " + subject; + } else { + // this is probably going to a mailing list + // a common convention for mailing lists is for automated mail to + // have a subject prefixed with the mailing list's name + //subject = getSubjectPrefix(report_to)+subject; + // + // instead, promote the `PUTS` name + subject = "[PUTS] "+subject; + if (debug) + recipients = ArrayUtil.toList(puts_admin); + else + recipients = ArrayUtil.toList(puts_users); + } + try { + EMailUtil.sendHTMLMessage( + smtp, + from, + recipients, + subject, + html_str + ); + } catch ( Exception ex ) { + // errors seen: + // 5.3.4 Requested action not taken; We noticed some unusual activity in your Hotmail account. To help protect you, we've temporarily blocked your account. + // -can't send email to puts_admin in this case (can't send email) + // TODO should log these errors - especially 5.3.4 + ex.printStackTrace(); + + // reconnect and retry once + smtp.dropConnection(); + + connect(); + + EMailUtil.sendHTMLMessage( + smtp, + from, + recipients, + subject, + html_str + ); + } + } + + public String createSubject(PhpBuildInfo info) { + return info.getBuildBranch()+" "+info.getVersionRevision()+"-"+info.getBuildType()+"-"+info.getCPUArch(); + } + } + static class Mail extends Mailer implements IRecvr { + LinkedList<String> message_bodies = new LinkedList<String>(); + + Mail(boolean debug, boolean force, Address[] puts_users) throws ParserException { + super(debug, force, puts_users); + } + + Mail(String phpt_prefix, boolean debug, boolean force, Address[] puts_users) throws ParserException { + super(phpt_prefix, debug, force, puts_users); + } + + @Override + public void recv(AbstractPhptRW base, AbstractPhptRW test, PHPTMultiHostTwoBuildSingleScenarioSetReportGen phpt_report, String html_str) throws IOException, SMTPException, Exception { + if (test.count(EPhptTestStatus.PASS)<100) + return; + sendMail( + !force && test.isTooMuchChange(base), + phpt_prefix+" PHPT Report "+createSubject(test.getBuildInfo()), + html_str + ); + } + + @Override + public void recv(AbstractPhpUnitRW base, AbstractPhpUnitRW test, PhpUnitMultiHostTwoBuildSingleScenarioSetReportGen php_unit_report, String html_str) throws IOException, SMTPException, Exception { + sendMail( + !force && test.isTooMuchChange(base), + "Application PhpUnit Report "+createSubject(test.getBuildInfo()), + html_str + ); + } + + @Override + public void start(PhpResultPack test_pack) throws Exception { + connect(); + } + + @Override + public void stop(PhpResultPack test_pack) throws Exception { + message_bodies.clear(); + } + } + enum EFTPSSL { + NO_SSL, + REQUIRE_SSL, + DETECT_SSL + } + static class Upload implements IRecvr { + static FTPClient configureClient(FTPSClient ftps) { + ftps.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager()); + return ftps; + } + + FTPClient ftp; + + @Override + public void start(PhpResultPack test_pack) throws IOException { + connect(); + } + + protected void connect() throws IOException { + EFTPSSL use_ssl = EFTPSSL.NO_SSL; + switch(use_ssl) { + case REQUIRE_SSL: + ftp = configureClient(new FTPSClient(true)); + break; + case DETECT_SSL: + ftp = configureClient(new FTPSClient(false)); + break; + default: + ftp = new FTPClient(); + } + + ftp.connect("131.107.220.66", 21); // TODO + ftp.login("pftt", "1nter0pLAb!!"); + ftp.setFileType(FTP.BINARY_FILE_TYPE); + } + + static final String[] HOSTS86 = new String[]{"2008r2sp0", "2008r2sp1", "Win7sp0-x64", "Win7sp1-x64", "Win7sp0-x86", "Win7sp1-x86", "Vistasp2-x86", "Vistasp2-x64", "2008sp0-x86", "2008sp0-x64", "2008sp1-x86", "2008sp1-x64", "2012sp0", "8sp0-x64", "2012r2"}; + static final String[] HOSTS64 = new String[]{"2008r2sp0", "2008r2sp1", "Win7sp0-x64", "Win7sp1-x64", "Vistasp2-x64", "2008sp0-x64", "2008sp1-x64", "2012sp0", "8sp0-x64", "2012r2"}; + @Override + public void stop(PhpResultPack test_pack) throws IllegalStateException, Exception { + LocalHost host = LocalHost.getInstance(); + // copy_hosts + { + File[] fhosts = test_pack.getResultPackPath().listFiles(); + for ( File fhost : fhosts ) { + if (fhost.isDirectory()) { + // + final String prefix = Character.toString((char)( 74 + new java.util.Random().nextInt(3))); + final String[] hosts = test_pack.getBuildInfo().isX64() ? HOSTS64 : HOSTS86; + + for ( int i=0 ; i < hosts.length ; i++ ) { + final String hostname = prefix + "-" +hosts[i]; + + final File target_file = new File(fhost.getParentFile(), hostname); + System.out.println(fhost+" "+target_file+" "+i); + if (i==0) { + host.move(fhost.getAbsolutePath(), target_file.getAbsolutePath()); + fhost = target_file; + } else { + host.copy(fhost.getAbsolutePath(), target_file.getAbsolutePath()); + } + } + break; + } + } + } + + final String temp_file = host.mktempname("Uploader", ".7z"); + + System.out.println("Compressing result-pack: "+test_pack.getResultPackPath()); + + host.compress(null, host, test_pack.getResultPackPath().getAbsolutePath(), temp_file); + + final String remote_file = generateFolder(test_pack)+"/"+test_pack.getResultPackPath().getName()+".7z"; + + System.out.println("Compressed. Uploading "+temp_file+" to "+remote_file); + + retryStore(generateFolder(test_pack), remote_file, new BufferedInputStream(new FileInputStream(temp_file))); + + System.out.println("Uploaded result-pack "+test_pack.getResultPackPath()+" to "+remote_file); + + ftp.logout(); + ftp.disconnect(); + + host.delete(temp_file); + } + + protected void retryStore(String folder, String file, InputStream in) throws IOException { + for ( int i=0 ; i < 10 ; i++ ) { + try { + ftp.mkd(folder); + ftp.storeFile(file, in); + break; + } catch ( Exception ex ) { + ex.printStackTrace(); + // these methods can block forever if there was an exception + //ftp.logout(); + //ftp.disconnect(); + // this too? ftp.quit(); + ftp = null; + connect(); + } + } + } + + static String generateFolder(PhpResultPack test_pack) { + return "/PFTT-Results/"+test_pack.getBuildInfo().getBuildBranch()+"/"+test_pack.getBuildInfo().getVersionRevision(); + } + static String generateFolder(AbstractTestResultRW test) { + return "/PFTT-Results/"+test.getBuildInfo().getBuildBranch()+"/"+test.getBuildInfo().getVersionRevision(); + } + + @Override + public void recv(AbstractPhptRW base, AbstractPhptRW test, PHPTMultiHostTwoBuildSingleScenarioSetReportGen phpt_report, String html_str) throws IOException { + final String folder = generateFolder(test); + + final String file = generateFileName(base, test); + + retryStore(folder, folder+"/"+file, new ByteArrayInputStream(html_str.getBytes())); + } + + @Override + public void recv(AbstractPhpUnitRW base, AbstractPhpUnitRW test, PhpUnitMultiHostTwoBuildSingleScenarioSetReportGen php_unit_report, String html_str) throws IOException { + final String folder = generateFolder(test); + + final String file = generateFileName(base, test); + + retryStore(folder, folder+"/"+file, new ByteArrayInputStream(html_str.getBytes())); + } + + } + interface IRecvr { + void recv(AbstractPhptRW base, AbstractPhptRW test, PHPTMultiHostTwoBuildSingleScenarioSetReportGen phpt_report, String html_str) throws IOException, SMTPException, Exception; + void recv(AbstractPhpUnitRW base, AbstractPhpUnitRW test, PhpUnitMultiHostTwoBuildSingleScenarioSetReportGen php_unit_report, String html_str) throws IOException, SMTPException, Exception; + void start(PhpResultPack test_pack) throws Exception; + void stop(PhpResultPack test_pack) throws Exception; + } + static void clean_hosts(AHost host, File pack) throws IllegalStateException, IOException { + File[] hosts = pack.listFiles(); + boolean is_first = true; + for ( File fhost : hosts ) { + if ( fhost.isDirectory() ) { + if (is_first) { + // don't delete all, leave first folder found + is_first = false; + } else { + System.err.println("delete "+fhost); + host.delete(fhost.getAbsolutePath()); + } + } + } + } + public static void main(String[] args) throws Exception { + //IRecvr recvr = new Verify(); + // + // mssql uses a different test-pack so reports only for that test-pack can be mailed + // whereas wincacheu is a bunch of scenarios for both core and mssql test-packs + // therefore all reports must go to wincache + // -shows how wincacheu scenarios compare to other scenarios + // -shows the mssql driver with wincacheu + final Mailer summary_mailer = new Mailer(false, false, new Address[]{AddressParser.parseAddress("v-maf...@microsoft.com")}); + final IRecvr CORE_PRODUCTION_SNAP = new Mail(false, false, new Address[]{AddressParser.parseAddress("winca...@microsoft.com"), AddressParser.parseAddress("ostc...@microsoft.com")}); + //final IRecvr CORE_PRODUCTION_SNAP = new Mail(false, false, new Address[]{AddressParser.parseAddress("ostc...@microsoft.com")}); + // TODO final IRecvr MSSQL_PRODUCTION_SNAP = new Mail("MSSQL", false, false, AddressParser.parseAddress("winca...@microsoft.com;jayk...@microsoft.com;ostc...@microsoft.com"),AddressParser.parseAddress("shekh...@microsoft.com")); + final IRecvr MSSQL_PRODUCTION_SNAP = new Mail("MSSQL", false, true, new Address[]{AddressParser.parseAddress("ostc...@microsoft.com"), AddressParser.parseAddress("jayk...@microsoft.com")}); + // TODO final IRecvr CORE_PRODUCTION_RELEASE = new Mail(false, true, AddressParser.parseAddress("winca...@microsoft.com;jayk...@microsoft.com;ostc...@microsoft.com")); + final IRecvr CORE_PRODUCTION_RELEASE = new Mail(false, true, new Address[]{AddressParser.parseAddress("ostc...@microsoft.com")}); + final IRecvr CORE_PRODUCTION_QA = new Mail(false, true, new Address[]{AddressParser.parseAddress("php-qa@lists.php.net")}); + final IRecvr TEST = new Mail(true, false, new Address[]{AddressParser.parseAddress("v-maf...@microsoft.com")}); + //IRecvr recvr = CORE_PRODUCTION_SNAP; // TODO + //IRecvr recvr = CORE_PRODUCTION_RELEASE; + //IRecvr recvr = MSSQL_PRODUCTION_SNAP; + IRecvr recvr = new Upload(); + + LocalHost host = LocalHost.getInstance(); + LocalConsoleManager cm = new LocalConsoleManager(); + + // TODO check if a smoke test failed! + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_3-Result-Pack-5.3.27rc1-nTS-X86-VC9")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_3-Result-Pack-5.3.27-nTS-X86-VC9")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_3-Result-Pack-re2e002d-nTS-X86-VC9")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_3-Result-Pack-r7c9bb87-nTS-X86-VC9")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_3-Result-Pack-re2e002d-TS-X86-VC9")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_3-Result-Pack-r7c9bb87-TS-X86-VC9")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-5.4.20rc1-TS-X86-VC9")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-5.4.20-TS-X86-VC9")); + + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-rd487f5e-TS-X86-VC9"); + //File test_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-r6c48c6b-TS-X86-VC9"); + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-ra03f094-nTS-X86-VC9"); + //File test_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-r72aacbf-nTS-X86-VC9"); + + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rc8b0da6-TS-X64-VC11")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rc8b0da6-TS-x64-VC11")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-r82dd6b9-NTS-X64-VC11")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rc8b0da6-NTS-X64-VC11")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rb2ee1b6-NTS-X86-VC11")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rfc9d886-NTS-X86-VC11")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rc8b0da6-TS-X86-VC11")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-rfc9d886-TS-X86-VC11")); + + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack--TS-X86-VC11"); + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-r43289d6-TS-X86-VC11"); + //File test_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_6-Result-Pack-5.6.0-dev-TS-X86-VC11-keyword916"); + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-r89c4aba-NTS-X64-VC11"); + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-r82bb2a2-NTS-X86-VC11"); + //File test_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_6-Result-Pack-5.6.0-dev-NTS-X86-VC11-keyword916"); + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-r5e1ac55-NTS-X86-VC11"); + //File test_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-r82bb2a2-NTS-X86-VC11"); + //File base_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-rd515455-TS-X64-VC11"); + //File test_dir = new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-r04fcf6a-TS-X64-VC11"); + + File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-5.5.6RC1-TS-X64-VC11")); + File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_5-Result-Pack-5.5.6-TS-X64-VC11")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-5.4.22rc1-NTS-X86-VC9-SQLSVR")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-5.4.22-NTS-X86-VC9-SQLSVR")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-5.4.22rc1-TS-X86-VC9")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_4-Result-Pack-5.4.22-TS-X86-VC9")); + //File base_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_Master-Result-Pack-ra0244a6-NTS-X86-VC11")); + //File test_dir = (new File("C:\\php-sdk\\PFTT-Auto\\PHP_5_6-Result-Pack-5.6.0-50333-NTS-X86-VC11")); + + + // clean_hosts + clean_hosts(host, base_dir); + clean_hosts(host, test_dir); + PhpResultPackReader base_pack = PhpResultPackReader.open(cm, host, base_dir); + PhpResultPackReader test_pack = PhpResultPackReader.open(cm, host, test_dir); + + // TODO temp + //report("Core", cm, recvr, base_pack, test_pack); + summary(summary_mailer, cm, base_pack, test_pack); + + } + static void summary(Mailer m, ConsoleManager cm, PhpResultPackReader base_pack, PhpResultPackReader test_pack) throws IOException, SMTPException, Exception { + CmpReport2 cmp = new CmpReport2(); + cmp.add(base_pack); + cmp.add(test_pack); + + AHost host = LocalHost.getInstance(); + + { + StringWriter sw = new StringWriter(); + TextBuilder text = new TextBuilder(sw); + + new CmpReport2G().run(text, cmp, cm); + //m.connect(); + //m.sendMail(false, "Summary "+m.createSubject(test_pack.getBuildInfo()), sw.toString()); + host.saveTextFile("c:\\php-sdk\\test.txt", sw.toString()); + host.exec("notepad c:\\php-sdk\\test.txt", AHost.FOUR_HOURS); + } + { + StringWriter sw = new StringWriter(); + MarkupBuilder html = new MarkupBuilder(sw); + + new CmpReport2G().run(html, cmp, cm); + //m.connect(); + //m.sendMail(false, "Summary "+m.createSubject(test_pack.getBuildInfo()), sw.toString()); + host.saveTextFile("c:\\php-sdk\\test.html", sw.toString()); + host.exec("start c:\\php-sdk\\test.html", AHost.FOUR_HOURS); + } + } + static void report(String phpt_prefix, ConsoleManager cm, IRecvr recvr, PhpResultPack base_pack, PhpResultPack test_pack) throws IOException, SMTPException, Exception { + recvr.start(test_pack); + + // TODO turn off phpt or phpunit reports or turn off all but a specific test-pack + for ( AbstractPhpUnitRW base : base_pack.getPhpUnit() ) { + for ( AbstractPhpUnitRW test : test_pack.getPhpUnit() ) { + System.out.println("PhpUnit "+base.getScenarioSetNameWithVersionInfo()+" "+test.getScenarioSetNameWithVersionInfo()); + //if (!base.getTestPackNameAndVersionString().contains("Wordpress")) + //continue; + if (!eq(base.getTestPackNameAndVersionString(), test.getTestPackNameAndVersionString())) + continue; + // TODO mysql + if (!base.getScenarioSetNameWithVersionInfo().replace("MySQL-5.6_", "").replace("7.0.1", "7.0.2").equals(test.getScenarioSetNameWithVersionInfo().replace("MySQL-5.6_", ""))) + /* + !(base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("opcache")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("opcache") + &&( + test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("cli")|| + test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("builtin")|| + test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("apache") + ) + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("cli")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("cli") + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("builtin")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("builtin") + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("apache")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("apache") + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("local")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("local") + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("smb-dfs")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("smb-dfs") + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("smb-dedup")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("smb-dedup") + &&base.getScenarioSetNameWithVersionInfo().toLowerCase().contains("smb-basic")==test.getScenarioSetNameWithVersionInfo().toLowerCase().contains("smb-basic") + ))*/ + continue; + + PhpUnitMultiHostTwoBuildSingleScenarioSetReportGen php_unit_report = new PhpUnitMultiHostTwoBuildSingleScenarioSetReportGen(base, test); + + String html_str = php_unit_report.getHTMLString(cm, !(recvr instanceof Upload)); + + recvr.recv(base, test, php_unit_report, html_str); + + } + } + //System.exit(0); + for ( AbstractPhptRW base : base_pack.getPHPT() ) { + for ( AbstractPhptRW test : test_pack.getPHPT() ) { + System.out.println("PHPT "+base.getScenarioSetNameWithVersionInfo()+" "+test.getScenarioSetNameWithVersionInfo()+" "+base+" "+test); + if (!eq(base.getScenarioSetNameWithVersionInfo(), test.getScenarioSetNameWithVersionInfo())) + continue; + + PHPTMultiHostTwoBuildSingleScenarioSetReportGen phpt_report = new PHPTMultiHostTwoBuildSingleScenarioSetReportGen(phpt_prefix, base, test); + String html_str = phpt_report.getHTMLString(cm, !(recvr instanceof Upload)); + + recvr.recv(base, test, phpt_report, html_str); + } + } + recvr.stop(test_pack); + } + static boolean eq(String base, String test) { + if (base==null||test==null) + return false; + base = base.replace("MySQL-5.6_", ""); + base = base.replace("7.0.1", "7.0.2"); + base = base.replace("-1.3.4", ""); + test = test.replace("7.0.1", "7.0.2"); + test = test.replace("MySQL-5.6_", ""); + test = test.replace("-1.3.4", ""); + return base.startsWith(test)||test.startsWith(base); + } +} diff --git a/src/com/mostc/pftt/main/CmpReport2.java b/src/com/mostc/pftt/main/CmpReport2.java new file mode 100644 index 0000000..ed43f62 --- /dev/null +++ b/src/com/mostc/pftt/main/CmpReport2.java @@ -0,0 +1,306 @@ +package com.mostc.pftt.main; + +import groovy.lang.GroovyObject; +import groovy.xml.MarkupBuilder; + +import java.awt.Desktop; +import java.io.File; +import java.io.FileWriter; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import com.github.mattficken.io.ArrayUtil; +import com.mostc.pftt.host.AHost; +import com.mostc.pftt.host.LocalHost; +import com.mostc.pftt.model.app.EPhpUnitTestStatus; +import com.mostc.pftt.model.core.EPhptTestStatus; +import com.mostc.pftt.model.core.PhpBuildInfo; +import com.mostc.pftt.results.AbstractPhpUnitRW; +import com.mostc.pftt.results.AbstractPhptRW; +import com.mostc.pftt.results.LocalConsoleManager; +import com.mostc.pftt.results.PhpResultPackReader; +import com.mostc.pftt.scenario.ScenarioSet; + +public class CmpReport2 { + final HashMap<PhpBuildInfo,PhpResultPackReader> result_packs; + final LinkedList<String> phpt_test_packs, phpunit_test_packs; + final LinkedList<AHost> hosts; + final HashMap<String,LinkedList<ScenarioSet>> phpt_scenario_sets, phpunit_scenario_sets; + + public CmpReport2() { + result_packs = new HashMap<PhpBuildInfo,PhpResultPackReader>(); + phpt_test_packs = new LinkedList<String>(); + phpunit_test_packs = new LinkedList<String>(); + phpt_scenario_sets = new HashMap<String,LinkedList<ScenarioSet>>(); + phpunit_scenario_sets = new HashMap<String,LinkedList<ScenarioSet>>(); + hosts = new LinkedList<AHost>(); + } + + void add(PhpResultPackReader result_pack) { + PhpBuildInfo build_info = result_pack.getBuildInfo(); + if (result_packs.containsKey(build_info)) + return; + result_packs.put(build_info, result_pack); + for ( AHost host : result_pack.getHosts() ) { + if (!hosts.contains(host)) + hosts.add(host); + for ( String phpt_test_pack : result_pack.getPhptTestPacks(host)) { + // TODO temp if (!phpt_test_packs.contains(phpt_test_pack)) + phpt_test_packs.add(phpt_test_pack); + LinkedList<ScenarioSet> sets = phpt_scenario_sets.get(phpt_test_pack); + if (sets==null) { + sets = new LinkedList<ScenarioSet>(); + phpt_scenario_sets.put(phpt_test_pack, sets); + } + for ( ScenarioSet scenario_set : result_pack.getPhptScenarioSets(host, phpt_test_pack) ) { + boolean a = true; + for ( ScenarioSet other : sets ) { + if (other.toString().equals(scenario_set.toString())) { + a = false; + break; + } + } + if (a) + sets.add(scenario_set); + } + } + for ( String phpunit_test_pack : result_pack.getPhpUnitTestPacks(host)) { + if (!phpunit_test_packs.contains(phpunit_test_pack)) + phpunit_test_packs.add(phpunit_test_pack); + LinkedList<ScenarioSet> sets = phpunit_scenario_sets.get(phpunit_test_pack); + if (sets==null) { + sets = new LinkedList<ScenarioSet>(); + phpunit_scenario_sets.put(phpunit_test_pack, sets); + } + for ( ScenarioSet scenario_set : result_pack.getPhpUnitScenarioSets(host, phpunit_test_pack) ) { + boolean a = true; + for ( ScenarioSet other : sets ) { + if (other.toString().equals(scenario_set.toString())) { + a = false; + break; + } + } + if (a) + sets.add(scenario_set); + } + } + } + System.out.println("67 "+phpunit_scenario_sets); + System.out.println(phpt_test_packs); + System.out.println(phpunit_test_packs); + } + + final HashMap<String,String> color_map = new HashMap<String,String>(); + String getColor(String scenario_set_str) { + String color = color_map.get(scenario_set_str); + if (color!=null) + return color; + Collection<String> colors = color_map.values(); + switch(colors.size()%20) { + case 0: + color = "#ff1b1b"; + break; + case 1: + color = "#1bff1b"; + break; + case 2: + color = "#1bcece"; + break; + case 3: + color = "#ffce1b"; + break; + case 4: + color = "#aaff1b"; + break; + case 5: + color = "#1baaff"; + break; + case 6: + color = "#ff1baa"; + break; + case 7: + color = "#eaea1b"; + break; + case 8: + color = "#ea1bff"; + break; + case 9: + color = "#ff1bea"; + break; + case 10: + color = "#1beaff"; + break; + case 11: + color = "#86ff1b"; + break; + case 12: + color = "#ff1b86"; + break; + case 13: + color = "#1b86ff"; + break; + case 14: + color = "#861bff"; + break; + case 15: + color = "#01ff1b"; + break; + case 16: + color = "#ff011b"; + break; + case 17: + color = "#ff1b01"; + break; + case 18: + color = "#00aaff"; + break; + case 19: + color = "#aa00ff"; + break; + } + color_map.put(scenario_set_str, color); + return color; + } + + PhpResultPackReader get(PhpBuildInfo build_info) { + return result_packs.get(build_info); + } + AbstractPhptRW getPhpt(AHost host, PhpBuildInfo build_info, ScenarioSet scenario_set, String test_pack_name) { + try { + return get(build_info).getPHPT(host, scenario_set, test_pack_name); + } catch ( Exception ex ) { + ex.printStackTrace(); + return null; + } + } + AbstractPhpUnitRW getPhpUnit(AHost host, PhpBuildInfo build_info, ScenarioSet scenario_set, String test_pack_name_and_version) { + try { + return get(build_info).getPhpUnit(host, test_pack_name_and_version, scenario_set); + } catch ( Exception ex ) { + ex.printStackTrace(); + return null; + } + } + List<ScenarioSet> getPhptScenarioSets(String test_pack_name_and_version) { + return phpt_scenario_sets.get(test_pack_name_and_version); + } + List<ScenarioSet> getPhpUnitScenarioSets(String test_pack_name_and_version) { + return phpunit_scenario_sets.get(test_pack_name_and_version); + } + List<String> getUniquePhptTestNames(PhpBuildInfo build_info, String test_pack_name_and_version, ScenarioSet scenario_set, EPhptTestStatus status) { + LinkedList<String> out = new LinkedList<String>(); + for ( AHost host : hosts ) { + for ( PhpResultPackReader result_pack : result_packs.values()) { + for ( AbstractPhptRW r : result_pack.getPHPT(host) ) { + if (!r.getScenarioSetNameWithVersionInfo().equals(scenario_set.toString())) + continue; + if (!r.getTestPackVersion().equals(test_pack_name_and_version)) + continue; + List<String> n = r.getTestNames(status); + for ( String a : n ) + out.remove(a); + } + } + } + Collections.sort(out); + return out; + } + List<String> getPhptTestNames(String test_pack_name_and_version, EPhptTestStatus status) { + LinkedList<String> out = new LinkedList<String>(); + for ( PhpResultPackReader result_pack : result_packs.values()) { + for ( AHost host : hosts ) { + for ( AbstractPhptRW r : result_pack.getPHPT(host, test_pack_name_and_version) ) + ArrayUtil.copyNoDuplicates(r.getTestNames(status), out); + } + } + Collections.sort(out); + return out; + } + List<String> getPhptScenarioSets(PhpBuildInfo build_info, String test_pack_name_and_version, String test_name, EPhptTestStatus status) { + LinkedList<String> out = new LinkedList<String>(); + String scenario_set_str; + for ( AHost host : hosts ) { + for ( AbstractPhptRW r : get(build_info).getPHPT(host, test_pack_name_and_version) ) { + if (r.isTestStatus(test_name, status)) { + scenario_set_str = r.getScenarioSetNameWithVersionInfo(); + if (!out.contains(scenario_set_str)) + out.add(scenario_set_str); + } + } + } + return out; + } + List<String> getUniquePhpUnitTestNames(PhpBuildInfo build_info, String test_pack_name_and_version, ScenarioSet scenario_set, EPhpUnitTestStatus status) { + LinkedList<String> out = new LinkedList<String>(); + + for ( AHost host : hosts ) { + for ( PhpResultPackReader result_pack : result_packs.values()) { + for ( AbstractPhpUnitRW r : result_pack.getPhpUnit(host) ) { + if (!r.getScenarioSetNameWithVersionInfo().equals(scenario_set.toString())) + continue; + if (!r.getTestPackNameAndVersionString().equals(test_pack_name_and_version)) + continue; + List<String> n = r.getTestNames(status); + ArrayUtil.copyNoDuplicates(n, out); + } + } + } + Collections.sort(out); + return out; + } + List<String> getPhpUnitTestNames(String test_pack_name_and_version, EPhpUnitTestStatus status) { + LinkedList<String> out = new LinkedList<String>(); + for ( PhpResultPackReader result_pack : result_packs.values()) { + for ( AHost host : hosts ) { + for ( AbstractPhpUnitRW r : result_pack.getPhpUnit(host, test_pack_name_and_version) ) { + ArrayUtil.copyNoDuplicates(r.getTestNames(status), out); + } + } + } + Collections.sort(out); + return out; + } + List<String> getPhpUnitScenarioSets(PhpBuildInfo build_info, String test_pack_name_and_version, String test_name, EPhpUnitTestStatus status) { + LinkedList<String> out = new LinkedList<String>(); + String scenario_set_str; + for ( AHost host : hosts ) { + for ( AbstractPhpUnitRW r : get(build_info).getPhpUnit(host, test_pack_name_and_version) ) { + if (r.isTestStatus(test_name, status)) { + scenario_set_str = r.getScenarioSetNameWithVersionInfo(); + if (!out.contains(scenario_set_str)) + out.add(scenario_set_str); + } + } + } + return out; + } + public static void main(String[] args) throws Exception { + LocalConsoleManager cm = new LocalConsoleManager(); + CmpReport2 cmp = new CmpReport2(); + LocalHost localhost = LocalHost.getInstance(); + // + /*cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\WinCacheU\\PHP_5_5-Result-Pack-5.5.2RC1-NTS-X86-VC11-2"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\WinCacheU\\PHP_5_5-Result-Pack-5.5.3-NTS-X86-VC11"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\WinCacheU\\PHP_5_4-Result-Pack-5.4.18RC2-NTS-X86-VC9-2")));*/ + + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\PHP_5_5-Result-Pack-5.5.3-NTS-X86-VC11"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\PHP_5_5-Result-Pack-5.5.3-TS-X86-VC11"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\PHP_5_4-Result-Pack-5.4.19-NTS-X86-VC9"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\PHP_5_4-Result-Pack-5.4.19-TS-X86-VC9"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\PHP_5_3-Result-Pack-5.3.27-NTS-X86-VC9"))); + cmp.add(PhpResultPackReader.open(cm, localhost, new File("C:\\php-sdk\\PHP_5_3-Result-Pack-5.3.27-TS-X86-VC9"))); + + File html_file = new File("c:\\php-sdk\\temp.html"); + FileWriter fw = new FileWriter(html_file); + MarkupBuilder html = new MarkupBuilder(fw); + + new CmpReport2G().run(html, cmp, cm); + + fw.close(); + + Desktop.getDesktop().browse(html_file.toURI()); + } +} diff --git a/src/com/mostc/pftt/main/CmpReport2G.groovy b/src/com/mostc/pftt/main/CmpReport2G.groovy new file mode 100644 index 0000000..06537ab --- /dev/null +++ b/src/com/mostc/pftt/main/CmpReport2G.groovy @@ -0,0 +1,32 @@ +package com.mostc.pftt.main + +import groovy.lang.GroovyObject; +import groovy.xml.MarkupBuilder; + +import com.mostc.pftt.results.ConsoleManager; +import com.mostc.pftt.host.AHost; + +class CmpReport2G { + public void run(BuilderSupport html, CmpReport2 cmp, ConsoleManager cm) { + html.html { + html.body { + + for (AHost host : cmp.hosts) { + // TODO detect multiple hosts and make separate reports for each host + for (String test_pack_name : cmp.phpt_test_packs) { + PHPTSingleHostMultiBuildMultiScenarioSetReportGen phpt_report = new PHPTSingleHostMultiBuildMultiScenarioSetReportGen(); + phpt_report.run(host, test_pack_name, html, cmp, cmp.result_packs.keySet(), cm, false); + html.br(); + } + + for (String test_pack_name : cmp.phpunit_test_packs) { + PhpUnitSingleHostMultiBuildMultiScenarioSetReportGen phpunit_report = new PhpUnitSingleHostMultiBuildMultiScenarioSetReportGen(); + phpunit_report.run(host, test_pack_name, html, cmp, cmp.result_packs.keySet(), cm, false); + html.br() + } + } + } // body + } // html + + } +} diff --git a/src/com/mostc/pftt/main/PBCReportGen.groovy b/src/com/mostc/pftt/main/PBCReportGen.groovy new file mode 100644 index 0000000..561cf97 --- /dev/null +++ b/src/com/mostc/pftt/main/PBCReportGen.groovy @@ -0,0 +1,231 @@ +package com.mostc.pftt.main +/* +#%powershell1.0% +# +# File: results-template.ps1 +# Description: Output $data into html table. This script is meant to be called from summarize-results.ps1. +# + +Function gaincalc ( $a=0, $b=0 ) { + if ( ($a -ne 0) -and ($b -ne 0) ) { + $c = ($a / $b) - 1 + + switch ($c*100) { + {$_ -ge 0 -and $_ -le 3} {$script:gainclass="none"} + {$_ -gt 3 -and $_ -le 7} {$script:gainclass="gainpossmall"} + {$_ -ge 0 -and $_ -gt 7} {$script:gainclass="gainpos"} + {$_ -lt 0 -and $_ -ge -3} {$script:gainclass="none"} + {$_ -lt -3 -and $_ -ge -7} {$script:gainclass="gainnegsmall"} + {$_ -lt 0 -and $_ -lt -7} {$script:gainclass="gainneg"} + Default { $script:gainclass="none" } + } + + "{0:P2}" -f $c + } + else { + $script:gainclass="none" + } +} + + +write-output " + +<html> +<head> + <style type=text/css> + .data td { border:1px solid black; } + .iis { background-color:#E6B8B7; } + .iiswincache { background-color: #DA9694; } + .apache { background-color: #C5D9F1; } + .apachenoigbinary { background-color: #8DB4E2; } + .apachewithigbinary { background-color: #538DD5; } + .gainpos { background-color: #00A84C; } + .gainpossmall { background-color: #00D661; } + .gainneg { background-color: #FF2929; } + .gainnegsmall { background-color: #DA9694; } + </style> + </head> + +<body> +<table border=0 cellpadding=0 cellspacing=0 width=600> +<tr> +<td> <strong> PHP Performance </strong> </td> +<td> <strong> Hardware & Environment </strong> </td> +</tr><tr> +<td>$PHP1 - $PHP2</td> +<td>Dell R710</td> +</tr><tr> +<td> </td> +<td>CPU - Intel Quad core @ 2.26Ghz (x2) L5520</td> +</tr><tr> +<td> </td> +<td>Memory - 12GB RAM</td> +</tr><tr> +<td> </td> +<td>HD - 147GB SAS RAID 1</td> +</tr><tr> +<td> </td> +<td>NIC - 1Gbps Intel</td> +</tr><tr> +<td> </td> +<td>Windows 2008 R2 - SP1</td> +</tr><tr> +<td> </td> +<td>php-web01.ctac.nttest.microsoft.com</td> +</tr> +<table> +<p> </p> + +<table border=0 cellpadding=0 cellspacing=0 width=1628 class=data> + +<!-- Header Labels --> + <tr> + <td colspan=4></td> + <td colspan=6 class=iis>IIS 7.5</td> + <td colspan=9 class=apache>Apache 2.2</td> + </tr> + + <tr> + <td colspan=4></td> + <td colspan=3 rowspan=2 class=iis>No Cache</td> + <td colspan=3 rowspan=2 class=iiswincache>WinCache</td> + <td colspan=3 rowspan=2 class=apache>No Cache</td> + <td colspan=6 class=apachenoigbinary>APC</td> + </tr> + <tr> + <td></td> + <td colspan=3>Load Agents</td> + <td colspan=3 class=apachenoigbinary>-igbinary</td> + <td colspan=3 class=apachewithigbinary>+igbinary</td> + </tr> + <!-- Header Labels --> + + <tr> + <td>Application</td> + <td>Physical</td> + <td>Virtual</td> + <td></td> + +<!-- IIS No Cache --> + + <td>$PHP1</td> + <td>$PHP2</td> + <td>gain</td> + +<!-- IIS Cache --> + <td>$PHP1</td> + <td>$PHP2</td> + <td>gain</td> + +<!-- Apache No Cache --> + <td>$PHP1</td> + <td>$PHP2</td> + <td>gain</td> + +<!-- Apache Cache -igbinary --> + <td>$PHP1</td> + <td>$PHP2</td> + <td>gain</td> + +<!-- Apache Cache +igbinary --> + <td>$PHP1</td> + <td>$PHP2</td> + <td>gain</td> + </tr> + +<!-- Results --> +" + +Foreach ( $app in $appnames ) { + ## Notes: $data[App_Name][Apache|IIS][cache|nocache|cachenoigbinary|cachewithigbinary][php1|php2][ver|tps8|tps16|tps32] + + write-output " + <tr> + <td rowspan=3>$app</td> + " + + Foreach ( $virt in $VIRTUAL ) { + $gain = "" + $gainclass = "" + if ( $virt -ne "8" ) { + write-output "<tr>" + } + + write-output " + <td>2</td> + <td>$virt</td> + <td> </td> + + <!-- IIS - No Cache --> + <td class=iis>" $data[$app]["IIS"]["nocache"]["php1"]["tps$virt"] "</td> + <td class=iis>" $data[$app]["IIS"]["nocache"]["php2"]["tps$virt"] "</td>" + + $gain = gaincalc $data[$app]["IIS"]["nocache"]["php2"]["tps$virt"] $data[$app]["IIS"]["nocache"]["php1"]["tps$virt"] + write-output " + + <td class=$gainclass> $gain + </td> <!-- Gain --> + + <!-- IIS - Cache --> + <td class=iiswincache>" $data[$app]["IIS"]["cache"]["php1"]["tps$virt"] "</td> + <td class=iiswincache>" $data[$app]["IIS"]["cache"]["php2"]["tps$virt"] "</td>" + + + $gain = gaincalc $data[$app]["IIS"]["cache"]["php2"]["tps$virt"] $data[$app]["IIS"]["cache"]["php1"]["tps$virt"] + write-output " + + <td class=$gainclass> $gain + </td> <!-- Gain --> + + <!-- Apache - No Cache --> + <td class=apache>" $data[$app]["Apache"]["nocache"]["php1"]["tps$virt"] "</td> + <td class=apache>" $data[$app]["Apache"]["nocache"]["php2"]["tps$virt"] "</td>" + + $gain = gaincalc $data[$app]["Apache"]["nocache"]["php2"]["tps$virt"] $data[$app]["Apache"]["nocache"]["php1"]["tps$virt"] + write-output " + + <td class=$gainclass> $gain + </td> <!-- Gain --> + + <!-- Apache - Cache -igbinary --> + <td class=apachenoigbinary>" $data[$app]["Apache"]["cachenoigbinary"]["php1"]["tps$virt"] "</td> + <td class=apachenoigbinary>" $data[$app]["Apache"]["cachenoigbinary"]["php2"]["tps$virt"] "</td>" + + $gain = gaincalc $data[$app]["Apache"]["cachenoigbinary"]["php2"]["tps$virt"] $data[$app]["Apache"]["cachenoigbinary"]["php1"]["tps$virt"] + write-output " + + <td class=$gainclass> $gain + </td> <!-- Gain --> + + <!-- Apache - Cache +igbinary --> + <td class=apachewithigbinary>" $data[$app]["Apache"]["cachewithigbinary"]["php1"]["tps$virt"] "</td> + <td class=apachewithigbinary>" $data[$app]["Apache"]["cachewithigbinary"]["php2"]["tps$virt"] "</td>" + + $gain = gaincalc $data[$app]["Apache"]["cachewithigbinary"]["php2"]["tps$virt"] $data[$app]["Apache"]["cachewithigbinary"]["php1"]["tps$virt"] + write-output " + + <td class=$gainclass> $gain + </td> <!-- Gain --> + </tr> + " + } ## End Foreach +} ## End Foreach + +write-output " +</table> +<p> </p> +<p> </p> +" + +if ( $errlog -ne "" ) { + write-output "<strong>Error Log:</strong> <br/>" + write-output "$errlog" +} + +write-output " +</body> +</html> +" + + +*/ diff --git a/src/com/mostc/pftt/model/sapi/CliSAPIInstance.java b/src/com/mostc/pftt/model/sapi/CliSAPIInstance.java index 14a2555..a5ee18b 100644 --- a/src/com/mostc/pftt/model/sapi/CliSAPIInstance.java +++ b/src/com/mostc/pftt/model/sapi/CliSAPIInstance.java @@ -12,17 +12,21 @@ import com.mostc.pftt.model.core.PhpIni; import com.mostc.pftt.results.ConsoleManager; import com.mostc.pftt.scenario.ScenarioSet; import com.mostc.pftt.util.DebuggerManager; +import com.mostc.pftt.util.DebuggerManager.Debugger; public class CliSAPIInstance extends SAPIInstance { protected final PhpBuild build; protected String ini_dir; protected DebuggerManager db_mgr; + protected Debugger dbg; - public CliSAPIInstance(ConsoleManager cm, AHost host, PhpBuild build, PhpIni ini) { + public CliSAPIInstance(ConsoleManager cm, AHost host, ScenarioSet scenario_set, PhpBuild build, PhpIni ini) { super(host, ini); this.build = build; db_mgr = cm.getDebuggerManager(); + if (db_mgr!=null) + dbg = db_mgr.newDebugger(cm, host, scenario_set, build); } @Override @@ -68,17 +72,17 @@ public class CliSAPIInstance extends SAPIInstance { } public ExecOutput execute(EExecutableType exe_type, String name, String php_filename, String extra_args, int timeout_sec, Map<String,String> env, String chdir, boolean debugger_attached) throws Exception { - return db_mgr == null ? + return dbg == null ? host.execOut(createPhpCommand(exe_type, php_filename, extra_args, debugger_attached), timeout_sec, env, chdir, true) : - db_mgr.execOut(host, name, createPhpCommand(exe_type, php_filename, extra_args, debugger_attached), timeout_sec, env, chdir, true); + dbg.execOut(createPhpCommand(exe_type, php_filename, extra_args, debugger_attached), timeout_sec, env, null, null); } public ExecHandle execThread(ConsoleManager cm, String name, ScenarioSet scenario_set, String cmd, String chdir, Map<String,String> env, byte[] stdin_data, boolean debugger_attached) throws Exception { // TODO use this with CliPhpUnitTestCaseRunner - ExecHandle eh = db_mgr == null ? + ExecHandle eh = dbg == null || !debugger_attached ? host.execThread(cmd, env, chdir, stdin_data, !debugger_attached) : - db_mgr.execThread(host, name, cmd, env, chdir, stdin_data, !debugger_attached); + dbg.execThread(cmd, env, chdir, stdin_data); if (debugger_attached && eh instanceof LocalExecHandle) { db_mgr.newDebugger(cm, host, scenario_set, name, build, (LocalExecHandle)eh); } diff --git a/src/com/mostc/pftt/results/AbstractPhpUnitRW.java b/src/com/mostc/pftt/results/AbstractPhpUnitRW.java index ae3710b..bd2e977 100644 --- a/src/com/mostc/pftt/results/AbstractPhpUnitRW.java +++ b/src/com/mostc/pftt/results/AbstractPhpUnitRW.java @@ -37,6 +37,5 @@ public abstract class AbstractPhpUnitRW extends AbstractTestResultRW { public boolean isTestStatus(String test_name, EPhpUnitTestStatus status) { return getTestNames(status).contains(test_name); } - public abstract String getPath(); } // end public abstract class AbstractPhpUnitRW diff --git a/src/com/mostc/pftt/results/AbstractPhptRW.java b/src/com/mostc/pftt/results/AbstractPhptRW.java index 983801e..bd88604 100644 --- a/src/com/mostc/pftt/results/AbstractPhptRW.java +++ b/src/com/mostc/pftt/results/AbstractPhptRW.java @@ -25,7 +25,6 @@ public abstract class AbstractPhptRW extends AbstractTestResultRW { public boolean isTestStatus(String test_name, EPhptTestStatus status) { return getTestNames(status).contains(test_name); } - public abstract String getPath(); protected void check(EPhptTestStatus status, List<String> names) { /*if (status==EPhptTestStatus.FAIL) { diff --git a/src/com/mostc/pftt/results/AbstractTestResultRW.java b/src/com/mostc/pftt/results/AbstractTestResultRW.java index 3fc9af9..f3b90cf 100644 --- a/src/com/mostc/pftt/results/AbstractTestResultRW.java +++ b/src/com/mostc/pftt/results/AbstractTestResultRW.java @@ -10,4 +10,5 @@ public abstract class AbstractTestResultRW { public abstract PhpBuildInfo getBuildInfo(); public abstract void close() throws IOException; public abstract float passRate(); + public abstract String getPath(); } diff --git a/src/com/mostc/pftt/results/AbstractUITestRW.java b/src/com/mostc/pftt/results/AbstractUITestRW.java index 79c5a37..20849a6 100644 --- a/src/com/mostc/pftt/results/AbstractUITestRW.java +++ b/src/com/mostc/pftt/results/AbstractUITestRW.java @@ -31,6 +31,11 @@ public abstract class AbstractUITestRW extends AbstractTestResultRW { for (EUITestStatus status:EUITestStatus.values()) results_by_status.put(status, new LinkedList<UITestResult>()); } + + @Override + public String getPath() { + return dir.getAbsolutePath(); + } public String getWebBrowserNameAndVersion() { return web_browser_name_and_version; diff --git a/src/com/mostc/pftt/results/LocalConsoleManager.java b/src/com/mostc/pftt/results/LocalConsoleManager.java index 5173459..8675a0b 100644 --- a/src/com/mostc/pftt/results/LocalConsoleManager.java +++ b/src/com/mostc/pftt/results/LocalConsoleManager.java @@ -85,23 +85,39 @@ public class LocalConsoleManager implements ConsoleManager { this.debugger_name = debugger_name; if (LocalHost.getInstance().isWindows()) { - if (debugger_name==null) - db_mgr = isDebugAll()||isDebugList() ? new WinDebugManager() : null; - else if (debugger_name.equalsIgnoreCase("windbg")) + if (debugger_name==null) { + if (isDebugAll()||isDebugList()) { + db_mgr = new WinDebugManager(); + println(EPrintType.CLUE, "DebugManager", "Debug Using WinDbg (default)"); + } else { + db_mgr = null; + } + } else if (debugger_name.equalsIgnoreCase("windbg")) { db_mgr = new WinDebugManager(); - else if (debugger_name.equalsIgnoreCase("ttt")) + println(EPrintType.CLUE, "DebugManager", "Debug Using WinDbg"); + } else if (debugger_name.equalsIgnoreCase("ttt")) { db_mgr = new TimeTravelTraceDebugManager(); - else + println(EPrintType.CLUE, "DebugManager", "Debug Using Time Travel Tracing (TTT)"); + } else { db_mgr = null; + } } else { - if (debugger_name==null) - db_mgr = isDebugAll()||isDebugList() ? new GDBDebugManager() : null; - else if (debugger_name.equalsIgnoreCase("gdb")) + if (debugger_name==null) { + if (isDebugAll()||isDebugList()) { + db_mgr = new GDBDebugManager(); + println(EPrintType.CLUE, "DebugManager", "Debug Using GDB (default)"); + } else { + db_mgr = null; + } + } else if (debugger_name.equalsIgnoreCase("gdb")) { db_mgr = new GDBDebugManager(); - else if (debugger_name.equalsIgnoreCase("valgrind")) + println(EPrintType.CLUE, "DebugManager", "Debug Using GDB"); + } else if (debugger_name.equalsIgnoreCase("valgrind")) { db_mgr = new ValgrindMemoryCheckManager(); - else + println(EPrintType.CLUE, "DebugManager", "Debug Using Valgrind"); + } else { db_mgr = null; + } } } diff --git a/src/com/mostc/pftt/results/PhptResultWriter.java b/src/com/mostc/pftt/results/PhptResultWriter.java index 382a561..b065449 100644 --- a/src/com/mostc/pftt/results/PhptResultWriter.java +++ b/src/com/mostc/pftt/results/PhptResultWriter.java @@ -316,7 +316,7 @@ public class PhptResultWriter extends AbstractPhptRW { serial.text(ext_name); serial.endTag(null, "name"); } - reportGroups(ext.test_groups); + // TODO temp reportGroups(ext.test_groups); serial.endTag(null, "extension"); } diff --git a/src/com/mostc/pftt/runner/CliPhptTestCaseRunner.java b/src/com/mostc/pftt/runner/CliPhptTestCaseRunner.java index f65335b..bceccd3 100644 --- a/src/com/mostc/pftt/runner/CliPhptTestCaseRunner.java +++ b/src/com/mostc/pftt/runner/CliPhptTestCaseRunner.java @@ -25,6 +25,7 @@ import com.mostc.pftt.model.core.PhptTestCase; import com.mostc.pftt.model.sapi.CliSAPIInstance; import com.mostc.pftt.results.ConsoleManager; import com.mostc.pftt.results.ITestResultReceiver; +import com.mostc.pftt.results.PhpResultPack; import com.mostc.pftt.results.PhptTestResult; import com.mostc.pftt.runner.LocalPhptTestPackRunner.PhptThread; import com.mostc.pftt.runner.PhptTestPreparer.PreparedPhptTestCase; @@ -270,17 +271,16 @@ public class CliPhptTestCaseRunner extends AbstractPhptTestCaseRunner2 { is_timeout = true; } + if (running_test_handle!=null && running_test_handle.cleanup_notify!=null) { + running_test_handle.cleanup(((PhpResultPack)twriter).getPHPT(host, scenario_set, src_test_pack.getNameAndVersionString())); + } if (!is_timeout && running_test_handle != null && running_test_handle.isCrashed()) { not_crashed = false; // @see #runTest - // TODO temp ((LocalExecHandle)running_test_handle).copyTTT(); - int exit_code = running_test_handle.getExitCode(); twriter.addResult(host, scenario_set, src_test_pack, notifyNotPass(new PhptTestResult(host, EPhptTestStatus.CRASH, prep.test_case, "PFTT: exit_code="+exit_code+" status="+AHost.guessExitCodeStatus(host, exit_code)+"\n"+output_str, null, null, null, ini, env, null, stdin_post, null, null, null, null, output_str, null))); - } else if (running_test_handle!=null) { - // TODO temp ((LocalExecHandle)running_test_handle).deleteTTT(); - } + } running_test_handle = null; diff --git a/src/com/mostc/pftt/runner/LocalPhptTestPackRunner.java b/src/com/mostc/pftt/runner/LocalPhptTestPackRunner.java index 12743cf..51db3c7 100644 --- a/src/com/mostc/pftt/runner/LocalPhptTestPackRunner.java +++ b/src/com/mostc/pftt/runner/LocalPhptTestPackRunner.java @@ -47,6 +47,7 @@ import com.mostc.pftt.scenario.XDebugScenario; public class LocalPhptTestPackRunner extends AbstractLocalTestPackRunner<PhptActiveTestPack, PhptSourceTestPack, PhptTestCase> { protected final IENVINIFilter filter; protected final boolean xdebug; + protected final PhptTestPreparer preparer; public LocalPhptTestPackRunner(ConsoleManager cm, ITestResultReceiver twriter, ScenarioSet scenario_set, PhpBuild build, AHost storage_host, AHost runner_host, IENVINIFilter filter) { super(cm, twriter, scenario_set, build, storage_host, runner_host); @@ -54,6 +55,8 @@ public class LocalPhptTestPackRunner extends AbstractLocalTestPackRunner<PhptAct // check once if this is using XDebug xdebug = scenario_set.contains(XDebugScenario.class); + + preparer = new PhptTestPreparer(xdebug); } @Override @@ -161,6 +164,8 @@ public class LocalPhptTestPackRunner extends AbstractLocalTestPackRunner<PhptAct return null; } + test_case.prep = preparer.prepare(test_case, runner_host, active_test_pack); + return group_key; } // end protected TestCaseGroupKey createGroupKey @@ -237,7 +242,7 @@ public class LocalPhptTestPackRunner extends AbstractLocalTestPackRunner<PhptAct protected void reportGroups() { PhptResultWriter phpt = (PhptResultWriter) ((PhpResultPackWriter)twriter).getPHPT(runner_host, scenario_set_setup, src_test_pack.getNameAndVersionString()); - phpt.reportGroups(thread_safe_groups, non_thread_safe_exts); + // TODO temp phpt.reportGroups(thread_safe_groups, non_thread_safe_exts); } @Override @@ -256,7 +261,7 @@ public class LocalPhptTestPackRunner extends AbstractLocalTestPackRunner<PhptAct @Override protected void runTest(TestCaseGroupKey group_key, PhptTestCase test_case, boolean debugger_attached) throws IOException, Exception, Throwable { - r = sapi_scenario.createPhptTestCaseRunner(this, group_key, test_case, cm, twriter, runner_host, scenario_set_setup, build, src_test_pack, active_test_pack, xdebug, debugger_attached); + r = sapi_scenario.createPhptTestCaseRunner(this, group_key, test_case.prep, cm, twriter, runner_host, scenario_set_setup, build, src_test_pack, active_test_pack, xdebug, debugger_attached); twriter.notifyStart(runner_host, scenario_set_setup, src_test_pack, test_case); r.runTest(cm, this, LocalPhptTestPackRunner.this); } diff --git a/src/com/mostc/pftt/scenario/CLIScenario.java b/src/com/mostc/pftt/scenario/CLIScenario.java index 9cbcab9..30c81aa 100644 --- a/src/com/mostc/pftt/scenario/CLIScenario.java +++ b/src/com/mostc/pftt/scenario/CLIScenario.java @@ -118,7 +118,7 @@ public class CliScenario extends SAPIScenario { // -for WEB SERVERS, have to set ENV vars on each web server instance // @see CliPhptTestCaseRunner#prepare // - CliSAPIInstance sapi = new CliSAPIInstance(cm, host, build, ini); + CliSAPIInstance sapi = new CliSAPIInstance(cm, host, scenario_set_setup.getScenarioSet(), build, ini); return new CliTestCaseGroupKey(sapi, ini, null); } else if (group_key!=null && group_key.getPhpIni().isDefault()) { @@ -128,7 +128,7 @@ public class CliScenario extends SAPIScenario { filter.prepareIni(cm, ini); - CliSAPIInstance sapi = new CliSAPIInstance(cm, host, build, ini); + CliSAPIInstance sapi = new CliSAPIInstance(cm, host, scenario_set_setup.getScenarioSet(), build, ini); return new CliTestCaseGroupKey(sapi, ini, null); } diff --git a/src/com/mostc/pftt/util/DebuggerManager.java b/src/com/mostc/pftt/util/DebuggerManager.java index a717e80..212fe71 100644 --- a/src/com/mostc/pftt/util/DebuggerManager.java +++ b/src/com/mostc/pftt/util/DebuggerManager.java @@ -15,6 +15,7 @@ import com.mostc.pftt.model.core.PhpBuild; import com.mostc.pftt.model.core.PhptTestCase; import com.mostc.pftt.results.ConsoleManager; import com.mostc.pftt.results.EPrintType; +import com.mostc.pftt.runner.AbstractTestPackRunner.TestPackRunnerThread; import com.mostc.pftt.scenario.Scenario; import com.mostc.pftt.scenario.ScenarioSet; @@ -24,7 +25,6 @@ import com.mostc.pftt.scenario.ScenarioSet; * */ -// XXX support for GDB on Linux public abstract class DebuggerManager { protected String src_path, debug_path; @@ -200,57 +200,46 @@ public abstract class DebuggerManager { int timeout_sec, Map<String, String> env, byte[] stdin_post, Charset charset, String current_dir) throws IllegalStateException, Exception { - // TODO Auto-generated method stub - return false; + return execOut(cmd, timeout_sec, env, stdin_post, charset).isSuccess(); } @Override - public ExecHandle execThread(String commandline) throws Exception { - // TODO Auto-generated method stub - return null; + public boolean exec(ConsoleManager cm, String ctx_str, + String commandline, int timeout, Map<String, String> env, + byte[] stdin, Charset charset, String chdir, + TestPackRunnerThread thread, int thread_slow_sec) + throws Exception { + return execOut(commandline, timeout, env, stdin, charset).isSuccess(); } - + @Override - public ExecHandle execThread(String commandline, byte[] stdin_data) - throws Exception { - // TODO Auto-generated method stub - return null; + public ExecHandle execThread(String commandline) throws Exception { + return execThread(commandline, null, null, null); } @Override - public ExecHandle execThread(String commandline, String chdir) - throws Exception { - // TODO Auto-generated method stub - return null; + public ExecHandle execThread(String commandline, byte[] stdin_data) throws Exception { + return execThread(commandline, null, null, stdin_data); } @Override - public ExecHandle execThread(String commandline, String chdir, - byte[] stdin_data) throws Exception { - // TODO Auto-generated method stub - return null; + public ExecHandle execThread(String commandline, String chdir) throws Exception { + return execThread(commandline, null, chdir, null); } @Override - public ExecHandle execThread(String commandline, - Map<String, String> env, byte[] stdin_data) throws Exception { - // TODO Auto-generated method stub - return null; + public ExecHandle execThread(String commandline, String chdir, byte[] stdin_data) throws Exception { + return execThread(commandline, null, chdir, stdin_data); } @Override - public ExecHandle execThread(String commandline, - Map<String, String> env, String chdir) throws Exception { - // TODO Auto-generated method stub - return null; + public ExecHandle execThread(String commandline, Map<String, String> env, byte[] stdin_data) throws Exception { + return execThread(commandline, env, null, stdin_data); } @Override - public ExecHandle execThread(String commandline, - Map<String, String> env, String chdir, byte[] stdin_data) - throws Exception { - // TODO Auto-generated method stub - return null; + public ExecHandle execThread(String commandline, Map<String, String> env, String chdir) throws Exception { + return execThread(commandline, env, chdir, null); } @Override @@ -268,6 +257,11 @@ public abstract class DebuggerManager { return createRunRequest(cm, ctx_clazz==null?null:ctx_clazz.getSimpleName()); } + } // end public static abstract class Debugger + + public Debugger newDebugger(ConsoleManager cm, AHost host, + ScenarioSet scenario_set, PhpBuild build) { + return null; } } // end public abstract class DebuggerManager diff --git a/src/com/mostc/pftt/util/GDBDebugManager.java b/src/com/mostc/pftt/util/GDBDebugManager.java index 111a3c9..b8c833a 100644 --- a/src/com/mostc/pftt/util/GDBDebugManager.java +++ b/src/com/mostc/pftt/util/GDBDebugManager.java @@ -67,6 +67,14 @@ public class GDBDebugManager extends DebuggerManager { // TODO Auto-generated method stub return false; } + + @Override + public ExecHandle execThread(String commandline, + Map<String, String> env, String chdir, byte[] stdin_data) + throws Exception { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/com/mostc/pftt/util/TimeTravelTraceDebugManager.java b/src/com/mostc/pftt/util/TimeTravelTraceDebugManager.java new file mode 100644 index 0000000..3b78f10 --- /dev/null +++ b/src/com/mostc/pftt/util/TimeTravelTraceDebugManager.java @@ -0,0 +1,179 @@ +package com.mostc.pftt.util; + +import java.nio.charset.Charset; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import com.mostc.pftt.host.AHost; +import com.mostc.pftt.host.AHost.ExecHandle; +import com.mostc.pftt.host.AHost.IExecHandleCleanupNotify; +import com.mostc.pftt.host.ExecOutput; +import com.mostc.pftt.host.Host; +import com.mostc.pftt.host.ICrashDetector; +import com.mostc.pftt.model.core.PhpBuild; +import com.mostc.pftt.results.AbstractTestResultRW; +import com.mostc.pftt.results.ConsoleManager; +import com.mostc.pftt.results.EPrintType; +import com.mostc.pftt.scenario.ScenarioSet; + +/** Handles integration with Time-Travel-Tracing, a debugging feature on Windows. + * + * For info on how to use a TTT trace (PFTT takes care of recording them for you) + * @see http://sharepoint/sites/cse/tttwiki/ (internal Microsoft only :() + * + */ + +public class TimeTravelTraceDebugManager extends WindowsDebuggerToolsManager { + + @Override + public Debugger newDebugger(ConsoleManager cm, AHost host, ScenarioSet scenario_set, Object server_name, PhpBuild build, int process_id, ExecHandle process) { + return null; + } + + protected boolean displayed_tips = false; + protected void displayTips(ConsoleManager cm) { + if (cm==null) + return; + if (displayed_tips) + return; + displayed_tips = true; + cm.println(EPrintType.CLUE, getClass(), "PFTT is recording Time-Travel Traces of crashed processes in Result-Pack"); + cm.println(EPrintType.CLUE, getClass(), ""); + cm.println(EPrintType.CLUE, getClass(), "Common TTT Replay commands"); + cm.println(EPrintType.CLUE, getClass(), ""); + cm.println(EPrintType.CLUE, getClass(), "Open TTT in WinDebug using File > Open Crash Dump"); + cm.println(EPrintType.CLUE, getClass(), "!idna.events - shows event timeline including thread creation"); + cm.println(EPrintType.CLUE, getClass(), "gu - run selected thread until unhandled exception"); + cm.println(EPrintType.CLUE, getClass(), "k - stack trace"); + cm.println(EPrintType.CLUE, getClass(), ""); + cm.println(EPrintType.CLUE, getClass(), "Problems loading idna DLL (WinDebug extension) into WinDebug:"); + cm.println(EPrintType.CLUE, getClass(), "1. copy C:\\Program Files\\Debugging Tools to c:\\debuggers (windbg commands can't have ` ` or \")"); + cm.println(EPrintType.CLUE, getClass(), "2. .extpath + c:\\debuggers\\TTT"); + cm.println(EPrintType.CLUE, getClass(), "3. `!c:\\debuggers\\TTT\\idna` instead of `!idna`"); + cm.println(EPrintType.CLUE, getClass(), "fe !c:\\debuggers\\TTT\\idna.position"); + } + + public Debugger newDebugger(ConsoleManager cm, AHost host, ScenarioSet scenario_set, PhpBuild build) { + String ttt_exe = null; + for ( String path : getToolPaths(host, build, "TTT\\TTTracer.exe") ) { + if (host.exists(path)) { + ttt_exe = path; + break; + } + } + if (ttt_exe==null) + return null; + + displayTips(cm); + return new TTTDebugger(host, ttt_exe); + } + + protected class TTTDebugger extends Debugger { + protected final AHost host; + // c:\\Program Files (x86)\\Debugging Tools for Windows (x86)\\TTT\\TTTracer.exe + protected final String ttt_exe; + + protected TTTDebugger(AHost host, String ttt_exe) { + this.host = host; + this.ttt_exe = ttt_exe; + } + + @Override + public ExecOutput execOut(String cmd, int timeout_sec, + Map<String, String> object, byte[] stdin_post, Charset charset) + throws IllegalStateException, Exception { + final String ttt_file = createTTTFilenameFromCommand(host, cmd); + ExecOutput eo = host.execOut(cmd, timeout_sec, object, stdin_post, charset); + handleCleanup(ttt_file, eo, null); + return eo; + } + + @Override + public RunRequest createRunRequest(ConsoleManager cm, String ctx_str) { + return host.createRunRequest(cm, ctx_str); + } + + @Override + public ExecOutput execOut(RunRequest req) { + final String ttt_file = createTTTFilenameFromCommand(host, req.getCommandline()); + ExecOutput eo = host.execOut(req); + handleCleanup(ttt_file, eo, null); + return eo; + } + + @Override + public ExecHandle execThread(RunRequest req) { + final String ttt_file = createTTTFilenameFromCommand(host, req.getCommandline()); + return handleThread(ttt_file, host.execThread(req)); + } + + @Override + public void close(ConsoleManager cm) { + + } + + @Override + public boolean isRunning() { + return host.isOpen(); + } + + @Override + public ExecHandle execThread(String commandline, + Map<String, String> env, String chdir, byte[] stdin_data) + throws Exception { + final String ttt_file = createTTTFilenameFromCommand(host, commandline); + return handleThread(ttt_file, host.execThread(wrapCommand(ttt_exe, ttt_file, commandline), env, chdir, stdin_data)); + } + + protected ExecHandle handleThread(final String ttt_file, ExecHandle eh) { + eh.cleanup_notify = new IExecHandleCleanupNotify() { + @Override + public void cleanupNotify(ExecHandle eh, AbstractTestResultRW rw) { + handleCleanup(ttt_file, eh, rw); + } + }; + return eh; + } + + protected void handleCleanup(String ttt_file, ICrashDetector eh, AbstractTestResultRW rw) { + if (eh.isCrashed()) { + if (rw!=null) { + try { + // move TTT file to result-pack (we want it, its a crash) + // and rename it to .run + host.move(ttt_file, rw.getPath()+"/"+Host.basename(ttt_file)+".run"); + } catch ( Exception ex ) { + ex.printStackTrace(); + } + } + } else { + try { + host.delete(ttt_file); + } catch ( Exception ex ) {} + } + } + + } // end protected class TTTDebugger + + AtomicInteger i = new AtomicInteger(0); + protected String createTTTFilenameFromCommand(AHost host, String cmd) { + if (cmd.startsWith("\"")) { + int i = cmd.indexOf('"', 1); + cmd = cmd.substring(1, i); + } else { + int i = cmd.indexOf(' '); + cmd = cmd.substring(1, i); + } + String name = Host.basename(cmd); + + + return host.getTempDir()+"\\"+name+i.incrementAndGet()+".run"; + } + + protected String wrapCommand(String ttt_exe, String ttt_file, String cmd) { + // passThroughExit => critical to detect crash + // launch => critical to actually run the process + return "\""+ttt_exe+"\" -passThroughExit -noUI -timer 60 -autoStart -saveCrash "+ttt_file+" -launch \"" + cmd +"\""; + } + +} // end public class TimeTravelTraceDebugManager diff --git a/src/com/mostc/pftt/util/TimerUtil.java b/src/com/mostc/pftt/util/TimerUtil.java index c5d4c5c..c9675a3 100644 --- a/src/com/mostc/pftt/util/TimerUtil.java +++ b/src/com/mostc/pftt/util/TimerUtil.java @@ -73,8 +73,8 @@ public final class TimerUtil { * many handles, it will wait a long time before allocating more, which delays thread creation * (which in turn can delay things like killing off timed out processes, which in turn frees up handles). * - * Instead, a bunch of threads are preallocated in a pool at startup. This gets one of - * those threads and has it run the given Runnable. + * Instead, some threads are preallocated in a pool at startup to help ensure that a thread + * can be created when its needed. * * Note: you may not call #start or #setDaemon on the returned Thread. you will get an IllegalThreadStateException if you do. * diff --git a/src/com/mostc/pftt/util/ValgrindMemoryCheckManager.java b/src/com/mostc/pftt/util/ValgrindMemoryCheckManager.java index be952bf..ee1281a 100644 --- a/src/com/mostc/pftt/util/ValgrindMemoryCheckManager.java +++ b/src/com/mostc/pftt/util/ValgrindMemoryCheckManager.java @@ -101,6 +101,14 @@ public class ValgrindMemoryCheckManager extends DebuggerManager { return null; // TODO temp } + + @Override + public ExecHandle execThread(String commandline, + Map<String, String> env, String chdir, byte[] stdin_data) + throws Exception { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/com/mostc/pftt/util/WinDebugManager.java b/src/com/mostc/pftt/util/WinDebugManager.java index a8fb35c..d95b2b1 100644 --- a/src/com/mostc/pftt/util/WinDebugManager.java +++ b/src/com/mostc/pftt/util/WinDebugManager.java @@ -31,7 +31,7 @@ import com.mostc.pftt.scenario.ScenarioSet; * */ -public class WinDebugManager extends DebuggerManager { +public class WinDebugManager extends WindowsDebuggerToolsManager { private String win_dbg_exe; private AHost win_dbg_host; private boolean displayed_windbg_tips = false; @@ -76,9 +76,10 @@ public class WinDebugManager extends DebuggerManager { protected void displayWindebugTips(ConsoleManager cm) { cm.println(EPrintType.TIP, getClass(), " WinDebug Command Referrence: http://www.windbg.info/doc/1-common-cmds.html"); - cm.println(EPrintType.TIP, getClass(), " WinDebug command: k - show callstack"); - cm.println(EPrintType.TIP, getClass(), " WinDebug command: g - go (until next exception)"); - cm.println(EPrintType.TIP, getClass(), " WinDebug command: <F9> - set breakpoint"); + cm.println(EPrintType.TIP, getClass(), " WinDebug command: k - show callstack"); + cm.println(EPrintType.TIP, getClass(), " WinDebug command: g - go (until next exception)"); + cm.println(EPrintType.TIP, getClass(), " WinDebug command: .dump /ma <filename> - create coredump file"); + cm.println(EPrintType.TIP, getClass(), " WinDebug command: <F9> - set breakpoint"); } public static class WinDebug extends Debugger { @@ -203,6 +204,14 @@ public class WinDebugManager extends DebuggerManager { // TODO Auto-generated method stub return null; } + + @Override + public ExecHandle execThread(String commandline, + Map<String, String> env, String chdir, byte[] stdin_data) + throws Exception { + // TODO Auto-generated method stub + return null; + } } // end public static class WinDebug @@ -215,24 +224,9 @@ public class WinDebugManager extends DebuggerManager { * @return */ public static String[] getWinDebugPaths(Host host, PhpBuild build) { - // use x86 windebug for x86 builds and x64 windebug edition for x64 builds! - // (can debug with different windebug editions, but WER popup requires that the architectures match) - // @see HostEnvUtil - if (build.isX86()) { - // - return new String[] { - host.getSystemDrive()+"\\Program Files (x86)\\Debugging Tools for Windows\\WinDbg.exe", - host.getSystemDrive()+"\\Program Files (x86)\\Debugging Tools for Windows (x86)\\WinDbg.exe" - }; - } else { - return new String[] { - host.getSystemDrive()+"\\Program Files\\Debugging Tools for Windows (x64)\\WinDbg.exe", - host.getSystemDrive()+"\\Program Files\\Debugging Tools for Windows\\WinDbg.exe", - host.getSystemDrive()+"\\Program Files\\Debugging Tools for Windows (x86)\\WinDbg.exe" - }; - } + return getToolPaths(host, build, "windbg.exe"); } - + /** returns the path that WinDebug is installed at, or returns null if windebug is not found. * * @see #getWinDebugPaths diff --git a/src/com/mostc/pftt/util/WindowsDebuggerToolsManager.java b/src/com/mostc/pftt/util/WindowsDebuggerToolsManager.java new file mode 100644 index 0000000..844a3f7 --- /dev/null +++ b/src/com/mostc/pftt/util/WindowsDebuggerToolsManager.java @@ -0,0 +1,27 @@ +package com.mostc.pftt.util; + +import com.mostc.pftt.host.Host; +import com.mostc.pftt.model.core.PhpBuild; + +public abstract class WindowsDebuggerToolsManager extends DebuggerManager { + + public static String[] getToolPaths(Host host, PhpBuild build, String exe_file) { + // use x86 windebug for x86 builds and x64 windebug edition for x64 builds! + // (can debug with different windebug editions, but WER popup requires that the architectures match) + // @see HostEnvUtil + if (build.isX86()) { + // + return new String[] { + host.getSystemDrive()+"\\Program Files (x86)\\Debugging Tools for Windows\\"+exe_file, + host.getSystemDrive()+"\\Program Files (x86)\\Debugging Tools for Windows (x86)\\"+exe_file + }; + } else { + return new String[] { + host.getSystemDrive()+"\\Program Files\\Debugging Tools for Windows (x64)\\"+exe_file, + host.getSystemDrive()+"\\Program Files\\Debugging Tools for Windows\\"+exe_file, + host.getSystemDrive()+"\\Program Files\\Debugging Tools for Windows (x86)\\"+exe_file + }; + } + } + +}