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

com.github.jlgrock.javascriptframework.closuretesting.ClosureTestingMojo Maven / Gradle / Ivy

The newest version!
package com.github.jlgrock.javascriptframework.closuretesting;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import org.apache.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;

import com.github.jlgrock.javascriptframework.closuretesting.resultparsing.ParseRunner;
import com.github.jlgrock.javascriptframework.closuretesting.resultparsing.TestResultType;
import com.github.jlgrock.javascriptframework.closuretesting.resultparsing.TestUnitDriver;
import com.github.jlgrock.javascriptframework.closuretesting.resultparsing.generators.SuiteGenerator;
import com.github.jlgrock.javascriptframework.closuretesting.resultparsing.testingcomponents.TestCase;
import com.github.jlgrock.javascriptframework.mavenutils.io.DirectoryIO;
import com.github.jlgrock.javascriptframework.mavenutils.logging.MojoLogAppender;
import com.github.jlgrock.javascriptframework.mavenutils.mavenobjects.JsarRelativeLocations;
import com.github.jlgrock.javascriptframework.mavenutils.pathing.FileListBuilder;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.LifecyclePhase;

/**
 * Will execute the jsclosure suite in a selenium testbed and execute it,
 * parsing values for problems. If problems arise, this can stop the build.
 */
@Mojo( name = "js-closure-test",
        defaultPhase = LifecyclePhase.TEST)
public class ClosureTestingMojo extends AbstractClosureTestingMojo {

	/**
	 * The Logger.
	 */
	private static final Logger LOGGER = Logger
			.getLogger(ClosureTestingMojo.class);

	@Override
	public final void execute() throws MojoExecutionException,
			MojoFailureException {
		MojoLogAppender.beginLogging(this);
		try {
			if (!isSkip()) {
                List files = generateFiles();
                if (!isSkipTests()) {
                    List testCases = parseFiles(files,
                            getMaximumFailures(), getTestTimeoutSeconds(),
                            getMaxTestThreads(), getBrowserVersion());

                    // Encountered Error(s)
                    if (testCases.size() > 0) {
                        printFailures(testCases);
                        throw new MojoFailureException(
                                "There were test case failures.");
                    }
                } else {
                    LOGGER.info("Test executions are skipped.");
                }
            } else {
                LOGGER.info("Tests are skipped.");
            }
		} catch (MojoFailureException mje) {
			throw mje;
		} catch (Exception e) {
			LOGGER.error(e.getMessage(), e);
			throw new MojoExecutionException(e.getMessage());
		} finally {
			MojoLogAppender.endLogging();
		}
	}

	/**
	 * Print failures. Will be done whenever an error is encountered.
	 * 
	 * @param testCases
	 *            the test cases to examine and print (if there is a failure)
	 */
	private void printFailures(final List testCases) {
		StringBuffer sb = new StringBuffer();
		for (TestCase testCase : testCases) {
			if (!testCase.getResult().equals(TestResultType.PASSED)) {
				sb.append(testCase.toString());
			}
		}
		LOGGER.error(sb.toString());
	}

	/**
	 * Will generate the files used in the google testing.
	 * 
	 * @return the set of files that were created
	 * @throws IOException
	 *             if there is a problem reading or writing to the files
	 */
	private List generateFiles() throws IOException {
		File testOutputDir = JsarRelativeLocations
				.getTestSuiteLocation(getFrameworkTargetDirectory());
		File testDepsDir = JsarRelativeLocations
				.getTestLocation(getFrameworkTargetDirectory());
		File depsFileLocation = JsarRelativeLocations
				.getTestDepsLocation(getFrameworkTargetDirectory());
		List returnFiles = new ArrayList();

		DirectoryIO.recursivelyDeleteDirectory(testOutputDir);

        File baseLocation = new File(getClosureLibraryLocation()
				.getAbsoluteFile()
				+ File.separator
				+ "closure"
				+ File.separator + "goog" + File.separator + "base.js");

		LOGGER.info("Generating Test Suite...");
		List fileSet = calculateFileSet();
		List testDeps = FileListBuilder.buildFilteredList(testDepsDir,
				"js");
		List depsFileSet = FileListBuilder.buildFilteredList(
				depsFileLocation, "js");
		File depsFile = null;
		if (depsFileSet.size() == 1) {
			depsFile = depsFileSet.toArray(new File[depsFileSet.size()])[0];
		} else {
			throw new IOException(
					"Could not find debug/deps file (or found more than one) at location '"
							+ depsFileLocation + "'.");
		}

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug("Files that will be included in testing:" + fileSet);
			LOGGER.debug("Base Location:" + baseLocation);
			LOGGER.debug("calc deps Location:" + depsFile);
			LOGGER.debug("Testing Dependency Location:" + testDepsDir);
			LOGGER.debug("Testing Source Directory:" + getTestSourceDirectory());
		}

		SuiteGenerator suite = new SuiteGenerator(fileSet, baseLocation,
				depsFile, testDeps, getPreamble(), getPrologue(), getEpilogue());

        Set testFiles = suite.generateTestFiles(getTestSourceDirectory(), testOutputDir);
        returnFiles.addAll(testFiles);

		if (isRunTestsOnCompiled()) {
			File testCompiledOutputDir = JsarRelativeLocations
					.getCompiledTestSuiteLocation(getFrameworkTargetDirectory());
			File compiledFile = new File(
					JsarRelativeLocations
							.getCompileLocation(getFrameworkTargetDirectory()),
					getCompiledFilename());

			SuiteGenerator suiteCompiled = new SuiteGenerator(fileSet,
					baseLocation, compiledFile, testDeps, getPreamble(),
					getPrologue(), getEpilogue());
			returnFiles.addAll(suiteCompiled.generateTestFiles(
					getTestSourceDirectory(), testCompiledOutputDir));
		}

		for (File file : returnFiles) {
			LOGGER.debug("filename: " + file.getAbsolutePath());
		}
		LOGGER.debug("baseLocation: " + baseLocation.getAbsolutePath());
		LOGGER.debug("testOutputDir: " + testOutputDir.getAbsolutePath());

		return returnFiles;
	}

	/**
	 * Parse the files created.
	 * 
	 * @param files
	 *            the files to parse
	 * @param maxFailures
	 *            the maximum number of failures to allow during the parsing.
	 * @param testTimeoutSeconds
	 *            the maximum number of seconds to execute before deciding that
	 *            a test case has failed.
	 * @param maxThreads
	 *            the maximum number of threads to spawn for test execution
	 * @param browserVersion
	 *            requested browser version (use null for the default version)
	 * @return the set of test cases received from parsing
	 */
	private static List parseFiles(
            final List files,
			final int maxFailures, final long testTimeoutSeconds,
			final int maxThreads, final String browserVersion) {
		final List failures = new ArrayList();
		int fileCount = (files != null ? files.size() : 0);
		int threadCount = Math.min(fileCount, maxThreads);
		LOGGER.info(String.format("Parsing %d Test Files (%d threads)...",
				fileCount, threadCount));

		if (fileCount > 0) {
			// create a synchronized list so test threads can determine whether
			// or not
			// the maximum failure count has reached and threads do not attempt
			// simultaneous
			// writes to the underlying failures list; we do this separately so
			// we don't return
			// the synchronized list to the calling method
			final List syncFailures = Collections
					.synchronizedList(failures);
			// initialize the thread pool for test execution, using a fixed-size
			// thread pool if multiple threads are specified and
			// a single-threaded pool if running in serial mode
			final ExecutorService threadPool = (maxThreads > 1 ? Executors
					.newFixedThreadPool(threadCount) : Executors
					.newSingleThreadExecutor());
			// initialize the ParseRunner queue; one ParseRunner per thread
			final BlockingQueue runnerQueue = new ArrayBlockingQueue(
					threadCount);
			BrowserVersion bv = TestUnitDriver.getBrowserVersionSafe(browserVersion);
			LOGGER.debug("HtmlUnit browser version: " + bv.getNickname());
			for (int idx = 0; idx < threadCount; idx++) {
				runnerQueue.add(new ParseRunner(
						new TestUnitDriver(true, bv),
						testTimeoutSeconds));
			}

			// the latch that will be used as the control gate to indicate tests
			// have completed
			final CountDownLatch latch = new CountDownLatch(fileCount);

			for (final File file : files) {
				final Callable testTask = new Callable() {
					@Override
					public Void call() throws Exception {
						// if we have reached the maximum number of failures,
						// return without testing
						if (maxFailures > 0) {
							synchronized (syncFailures) {
								if (syncFailures.size() > maxFailures) {
									latch.countDown();
									return null;
								}
							}
						}

						// get the next available ParseRunner
						final ParseRunner runner = runnerQueue.take();
						try {
							final TestCase testCase = runner.parseFile(file);
							if (!TestResultType.PASSED.equals(testCase
									.getResult())) {
								synchronized (syncFailures) {
									syncFailures.add(testCase);
								}
							}
						} finally {
							if (runner != null) {
								runnerQueue.put(runner);
							}
							latch.countDown();
						}
						return null;
					}
				};
				threadPool.submit(testTask);
			}

			// stop the thread pool, preventing additional tasks from being
			// submitted
			threadPool.shutdown();
			// wait for all test cases to complete execution
			try {
				latch.await();
			} catch (InterruptedException ie) {
				// attempt to stop execution gracefully
				threadPool.shutdownNow();
			} finally {
				// clean up test resources
				if (runnerQueue.size() != threadCount) {
					throw new IllegalStateException(
							"ParseRunners were not properly returned to the queue.");
				}
				while (!runnerQueue.isEmpty()) {
					runnerQueue.remove().quit();
				}
			}
		}
		return failures;
	}

	/**
	 * Will calculate the set of files.
	 * 
	 * @return the file set
	 */
	private List calculateFileSet() {
		LOGGER.info("Calculating File Set...");
		List files = new ArrayList();
		files.addAll(FileListBuilder.buildFilteredList(
				getTestSourceDirectory(), "js"));
		if (getIncludes() != null) {
			files.addAll(getIncludes());
		}
		if (getExcludes() != null) {
			files.removeAll(getExcludes());
		}
		return files;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy