All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cj.qunit.mojo.QunitMavenRunner Maven / Gradle / Ivy

package com.cj.qunit.mojo;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.util.log.Logger;

import com.cj.qunit.mojo.http.WebServerUtils;
import com.cj.qunitTestDriver.QUnitTestPage;
import com.gargoylesoftware.htmlunit.BrowserVersion;

public class QunitMavenRunner {
    public enum Runner{
        HTMLUNIT{
            String runTest(
                    final WebServerUtils.JettyPlusPortPlusScanner jetty,
                    final LocatedTest test,
                    final String name,  int testTimeout,
                    final Listener listener,
                    boolean verbose,
                    boolean preserveTempFiles,
                    int retryCount) {

                QUnitTestPage page = new QUnitTestPage(jetty.port, test.relativePathToHtmlFile, testTimeout, BrowserVersion.FIREFOX_17, true);
                page.assertTestsPass();
                return null;
            }
        }, 
        PHANTOMJS{
            
            String runTest(
                    final WebServerUtils.JettyPlusPortPlusScanner jetty,
                    final LocatedTest test,
                    final String name,  int testTimeout,
                    final Listener listener,
                    boolean verbose,
                    boolean preserveTempFiles,
                    int retryCount) {
                
                try {
                    String message = null;
                    for (int run = 0; run <= retryCount; run++) {
                        File f = File.createTempFile("phantomjs-run-qunit", ".js");

                        if (!preserveTempFiles) { f.deleteOnExit(); }

                        FileUtils.write(f, IOUtils.toString(getClass().getResourceAsStream("/qunit-mojo/phantomjs-run-qunit.js")));
                    
                        String baseUrl = "http://localhost:" + jetty.port;
                        String url = baseUrl + "/" + test.relativePathToHtmlFile;
                        String[] command = {
                                "phantomjs",
                                f.getAbsolutePath(),
                                url,
                                Integer.toString(testTimeout)};
                    
                        Process phantomjs = new ProcessBuilder().redirectErrorStream(true).command(command).start();

                        String logMessage = "Executing " + mkString(command, " ");

                        if (verbose) { listener.info(logMessage); } else { listener.debug(logMessage); }
                    
                        copyStreamAsyncOneByteAtATime(phantomjs.getInputStream(), System.out);
                        copyStreamAsyncOneByteAtATime(phantomjs.getErrorStream(), System.err);
                    
                        final int exitCode = phantomjs.waitFor();

                        logMessage = "Exit code " + exitCode + " for " + name;
                    
                        if (verbose) { listener.info(logMessage); } else { listener.debug(logMessage); }

                        if (exitCode == 0) {
                            return null;
                        }
                        if (exitCode < 128) {
                            return "Problems found in " + name;
                        }
                        // apparently when exitCode is >128 phantomjs crashed with error exitCode-128  (e.g. 139-128=11 or SIGSEGV)
                        message = "Problems found in " + name + " (Crash? " + exitCode + ")";
                    }
                    return message;
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

        
        private static  String mkString(T[] items, String separator){
            StringBuilder text = new StringBuilder();
            for(T next : items){
                if(text.length()>0){
                    text.append(separator);
                }
                text.append(next);
            }
            return text.toString();
        }
        
        private static void copyStreamAsyncOneByteAtATime(final InputStream in, final OutputStream out){
            new Thread(){
                public void run() {
                    try {
                        for(int b = in.read();b!=-1;b = in.read()){
                            out.write(b);
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    
                };
            }.start();
            
        }
        
        abstract String runTest(
                final WebServerUtils.JettyPlusPortPlusScanner jetty,
                final LocatedTest test,
                final String name,  int testTimeout,
                final Listener listener,
                boolean verbose,
                boolean preserveTempFiles,
                int retryCount);
    }
    
    public static interface Listener {
        void warn(String info);
        void info(String info);
        void runningTest(String relativePath);
        void debug(String info);
    }

    private static  List concat(List ... lists){
        final List result = new ArrayList();

        for(List list : lists){
            result.addAll(list);
        }

        return result;
    }
    
    final int numThreads;
    final Runner runner;
    final boolean verbose;
    final boolean preserveTempFiles;
    final int retryCount;

    public QunitMavenRunner() {
        this(1, Runner.HTMLUNIT, false, false, 0);
    }
    
    public QunitMavenRunner(int numThreads, Runner runner, boolean verbose, boolean preserveTempFiles, int retryCount) {
        super();
        this.numThreads = numThreads;
        this.runner = runner;
        this.verbose = verbose;
        this.preserveTempFiles = preserveTempFiles;
        this.retryCount = retryCount;
    }
        
    public static String requireConfigBaseUrl(String webPathToRequireDotJsConfig, int port){
        
        try {
        	
            if(webPathToRequireDotJsConfig == null || webPathToRequireDotJsConfig.isEmpty()) return "";
            
            final String code = IOUtils.toString(new URL("http://localhost:" + port + webPathToRequireDotJsConfig).openStream());
            Rhino rhino = new Rhino();
            rhino.eval(code);
            final String result = rhino.eval("require.baseUrl");
            final String baseUrl =  result == null ? "" : result;
            System.out.println("baseUrl is " + baseUrl);
            return baseUrl;
            
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    public List run(final String webRoot, final List codePaths, final String filter, final List extraPathsToServe, final String webPathToRequireDotJsConfig, final Listener log, final int testTimeout, Logger jettyLog) {
        final String requireDotJsConfig;

        final String normalizedWebRoot = normalizedWebRoot(webRoot);

        if(webPathToRequireDotJsConfig!=null && webPathToRequireDotJsConfig.trim().equals("")){
            requireDotJsConfig = null;
        }else{
            requireDotJsConfig = webPathToRequireDotJsConfig;
        }

        validateJsConfigpath(normalizedWebRoot, codePaths, extraPathsToServe, requireDotJsConfig);

        final WebServerUtils.JettyPlusPortPlusScanner jetty = WebServerUtils.launchHttpServer(normalizedWebRoot, codePaths, extraPathsToServe, requireDotJsConfig, jettyLog, true, filter);

        try {

            final List problems = new ArrayList(); 

            final List allPaths = new ArrayList(codePaths);
            allPaths.addAll(extraPathsToServe);
            
            final List allTests = jetty.scanner.findTests();

            if (verbose) {
                log.info(String.format("found %d test files", allTests.size()));
            }
            
            final List testsRemaining = new ArrayList(allTests);

            log.info("Executing qunit tests on " + numThreads + " thread(s) using " + runner.toString().toLowerCase());
            
            runInParallel(numThreads, log, new Runnable(){
                public void run() {
                    while(true){
                        final LocatedTest test;

                        synchronized(testsRemaining){
                            if (verbose) { log.info(testsRemaining.size() + " tests remaining"); }

                            test = (testsRemaining.size() > 0) ? testsRemaining.remove(0) : null;
                        }

                        if(test==null){
                            break;
                        }

                        final String relativePathToDetectedFile = test.relativePathToDetectedFile;
                        log.runningTest(relativePathToDetectedFile);

                        String problem = null;
                        try {
                            problem = runner.runTest(jetty, test, relativePathToDetectedFile, testTimeout, log, verbose, preserveTempFiles, retryCount);
                        } catch (Throwable m){
                            problem = "Problems found in '" + relativePathToDetectedFile +"':\n"+m.getMessage();
                        }   

                        if(problem!=null){
                            synchronized(problems){
                                log.warn(problem);
                                problems.add(problem);
                            }
                        }
                    }
                }
                
            });
            
            return problems;
        }finally{
            try {
                jetty.server.stop();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    
    private void runInParallel(int numThreads, Listener log, final Runnable runnable) {

        List threads = new ArrayList();

        for(int x=0;x codePaths,
            final List extraPathsToServe,
            final String webPathToRequireDotJsConfig) {

        if(webPathToRequireDotJsConfig==null) return;

        boolean found = false;

        final String relativeFilesystemPathToRequireDotJsConfig = webPathToRequireDotJsConfig.replaceFirst(Pattern.quote(webRoot), "");

        List placesLooked = new ArrayList();
        for(File codeDir : concat(codePaths, extraPathsToServe)){
            final File config = new File(codeDir, relativeFilesystemPathToRequireDotJsConfig);
            placesLooked.add(config);
            if(config.exists()){
                found = true; 
            }
        }

        if(!found){

            StringBuilder text = new StringBuilder("You configured a require.js configuration path of \"" + webPathToRequireDotJsConfig + "\".  However, it doesn't seem to exist.  Here's where I looked for it:");
            for(File path : placesLooked){
                text.append("\n    ").append(path.getAbsolutePath()).append("\n");
            }
            throw new RuntimeException(text.toString());
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy