package net.jangaroo.jooc.mvnplugin.test; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.apache.maven.plugin.logging.Log; import org.codehaus.plexus.util.cli.CommandLineException; import org.codehaus.plexus.util.cli.CommandLineUtils; import org.codehaus.plexus.util.cli.Commandline; import org.codehaus.plexus.util.cli.StreamConsumer; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.codehaus.plexus.util.StringUtils.isBlank; public class PhantomJsTestRunner { public static final Pattern LOG_LEVEL_PATTERN = Pattern.compile("^\\[([A-Z]+)\\]\\s*(.*)$"); private static volatile Boolean phantomjsExecutableFound; private final String testPageUrl; private String testResultFilename; private final String phantomjs; private final Log log; private final String testRunner; private final int timeout; private final int maxRetriesOnCrashes; /** * @param phantomjs the binary to execute * @param testPageUrl the URL of the test page to load * @param testResultFilename the file to write the test result into * @param testRunner the test bootstrap script to be loaded in phantomjs * @param timeout timeout in seconds * @param maxRetriesOnCrashes number of retries when receiving unexpected result from phantomjs (crash?) * @param log the maven log */ public PhantomJsTestRunner(String phantomjs, String testPageUrl, String testResultFilename, String testRunner, int timeout, int maxRetriesOnCrashes, Log log) { this.phantomjs = makeOsSpecific(phantomjs); this.testPageUrl = testPageUrl; this.testResultFilename = testResultFilename; this.testRunner = testRunner; this.timeout = timeout; this.maxRetriesOnCrashes = maxRetriesOnCrashes; this.log = log; } private static String makeOsSpecific(String phantomjs) { if(SystemUtils.IS_OS_WINDOWS) { if(!isBlank(phantomjs) && phantomjs.charAt(phantomjs.length() - 4) != '.') { return phantomjs + ".exe"; } } return phantomjs; } public boolean execute() throws CommandLineException { final Commandline cmd = createCommandLine(); final ArrayList<String> arguments = new ArrayList<String>(); arguments.add(testRunner); arguments.add(testPageUrl); arguments.add(testResultFilename); arguments.add(String.valueOf(timeout)); cmd.addArguments(arguments.toArray(new String[arguments.size()])); final StreamConsumer outConsumer = new StreamConsumer() { @Override public void consumeLine(String line) { Matcher matcher = LOG_LEVEL_PATTERN.matcher(line); String logLevel; String msg; if (matcher.matches()) { logLevel = matcher.group(1); msg = matcher.group(2); } else { logLevel = "DEBUG"; msg = line; } if ("ERROR".equals(logLevel)) { log.error(msg); } else if ("WARN".equals(logLevel)) { log.warn(msg); } else if ("INFO".equals(logLevel)) { log.info(msg); } else { log.debug(msg); } } }; final StreamConsumer errConsumer = new StreamConsumer() { @Override public void consumeLine(String line) { log.warn(line); } }; log.info("executing phantomjs cmd: " + cmd.toString()); for (int tryCount = 0; tryCount <= maxRetriesOnCrashes; ++tryCount) { int returnCode = CommandLineUtils.executeCommandLine(cmd, outConsumer, errConsumer, timeout); if (returnCode >= 0 && returnCode <= 4) { // valid phantomjs-joounit-page-runner return codes! return returnCode == 0; } log.warn(String.format("unexpected result %d from phantomjs run #%d", returnCode, tryCount + 1)); } log.error(String.format("Got %d unexpected results from phantomjs, giving up.", maxRetriesOnCrashes + 1)); return false; } public boolean canRun() { return testResultFilename != null && testRunner != null && phantomjs != null && isExecutableAvailable(); } private boolean isExecutableAvailable() { if(phantomjsExecutableFound == null) { synchronized (PhantomJsTestRunner.class) { phantomjsExecutableFound = (new File(phantomjs).canExecute() || canExecutePhantomJs()); } } return phantomjsExecutableFound; } private boolean canExecutePhantomJs() { final String path = System.getenv("PATH"); final String[] directoryNames = StringUtils.split(path, File.pathSeparatorChar); for (String directoryName : directoryNames) { final File[] files = new File(directoryName).listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return phantomjs.equals(name); } }); if(!(files == null || files.length == 0)) { return true; } } log.warn(phantomjs + " not found in " + path); return false; } private Commandline createCommandLine() { final Commandline commandline = new Commandline(); commandline.setExecutable(phantomjs); return commandline; } @Override public String toString() { return "PhantomJsTestRunner{" + "phantomjs=" + phantomjs + ", testRunner='" + testRunner + '\'' + ", testPageUrl='" + testPageUrl + '\'' + ", testResultFilename=" + testResultFilename + '}'; } }