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

org.daisy.maven.xproc.xprocspec.XProcSpecRunner Maven / Gradle / Ivy

package org.daisy.maven.xproc.xprocspec;

import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import static com.google.common.io.Files.asByteSink;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.net.URI;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.ServiceLoader;

import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;

import net.sf.saxon.xpath.XPathFactoryImpl;

import org.daisy.maven.xproc.api.XProcExecutionException;
import org.daisy.maven.xproc.api.XProcEngine;

import org.xml.sax.InputSource;

public class XProcSpecRunner {
	
	private XProcEngine engine;
	
	public void setXProcEngine(XProcEngine engine) {
		this.engine = engine;
	}
	
	protected void activate() {
		if (engine == null) {
			
			// We are not in an OSGi environment, try with ServiceLoader
			ServiceLoader xprocEngines = ServiceLoader.load(XProcEngine.class);
			try {
				engine = xprocEngines.iterator().next();
			} catch (NoSuchElementException e) {
				throw new RuntimeException("Could not find any XProc engines on the classpath.");
			}
		}
	}
	
	public boolean run(Map tests,
	                   File reportsDir,
	                   File surefireReportsDir,
	                   File tempDir,
	                   File catalog,
	                   Reporter reporter) {
		
		if (engine == null)
			activate();
		engine.setCatalog(catalog);
		
		URI xprocspec = asURI(XProcSpecRunner.class.getResource("/content/xml/xproc/xprocspec.xpl"));
		URI xprocspecSummary = asURI(XProcSpecRunner.class.getResource("/xprocspec-extra/xprocspec-summary.xpl"));
		URL xspecCss = XProcSpecRunner.class.getResource("/xprocspec-extra/xspec.css");
		
		reportsDir.mkdirs();
		surefireReportsDir.mkdirs();
		
		int totalRun = 0;
		int totalFailures = 0;
		int totalErrors = 0;
		int totalSkipped = 0;
		
		long startTime = System.nanoTime();
		
		reporter.init();
		
		for (String testName : tests.keySet()) {
			File test = tests.get(testName);
			File report = new File(reportsDir, testName + ".html");
			File surefireReport = new File(surefireReportsDir, "TEST-" + testName + ".xml");
			Map> input = ImmutableMap.of("source", Arrays.asList(new String[]{asURI(test).toASCIIString()}));
			Map output = ImmutableMap.of("html", asURI(report).toASCIIString(),
			                                            "junit", asURI(surefireReport).toASCIIString());
			Map options = ImmutableMap.of("temp-dir", asURI(tempDir) + "/tmp/");
			reporter.running(testName);
			try {
				engine.run(xprocspec.toASCIIString(), input, output, options, null);
				if (!surefireReport.exists()) {
					totalRun += 1;
					totalErrors += 1;
					reporter.result(1, 0, 1, 0, 0L, null, null); }
				else {
					Integer run = (Integer)evaluateXPath(surefireReport, "sum(/testsuites/@tests)", null, Integer.class);
					Integer failures = (Integer)evaluateXPath(surefireReport, "number(/testsuites/@failures)", null, Integer.class);
					Integer errors = (Integer)evaluateXPath(surefireReport, "number(/testsuites/@errors)", null, Integer.class);
					Integer skipped = (Integer)evaluateXPath(surefireReport, "sum(/testsuites/*/number(@skipped))", null, Integer.class);
					Long time = (Long)evaluateXPath(surefireReport, "number(/testsuites/@time)", null, Long.class);
					totalRun += run;
					totalFailures += failures;
					totalErrors += errors;
					totalSkipped += skipped;
					reporter.result(run, failures, errors, skipped, time, null, null); }}
			catch (XProcExecutionException e) {
				totalRun += 1;
				totalErrors += 1;
				reporter.result(1, 0, 1, 0, 0L, e.getMessage(), Throwables.getStackTraceAsString(e)); }
		}
		
		long totalTime = TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
		reporter.finish(totalRun, totalFailures, totalErrors, totalSkipped, totalTime);
		
		/* Generate summary */
		try {
			StringBuilder testNames = new StringBuilder();
			StringBuilder surefireReports = new StringBuilder();
			StringBuilder reports = new StringBuilder();
			File summary = new File(reportsDir, "index.html");
			File css = new File(reportsDir, "xspec.css");
			for (String testName : tests.keySet()) {
				testNames.append(testName);
				testNames.append(" ");
				File surefireReport = new File(surefireReportsDir, "TEST-" + testName + ".xml");
				surefireReports.append(asURI(surefireReport).toASCIIString());
				surefireReports.append(" ");
				File report = new File(reportsDir, testName + ".html");
				reports.append(asURI(report).toASCIIString());
				reports.append(" "); }
			testNames.deleteCharAt(testNames.length() - 1);
			surefireReports.deleteCharAt(surefireReports.length() - 1);
			reports.deleteCharAt(reports.length() - 1);
			Map output = ImmutableMap.of("result", asURI(summary).toASCIIString());
			Map params = ImmutableMap.of("test-names", testNames.toString(),
			                                            "surefire-reports", surefireReports.toString(),
			                                            "reports", reports.toString());
			engine.run(xprocspecSummary.toASCIIString(), null, output, null, ImmutableMap.of("parameters", params));
			asByteSink(css).writeFrom(xspecCss.openStream()); }
		catch (XProcExecutionException e) {
			throw new RuntimeException(e); }
		catch (IOException e) {
			throw new RuntimeException(e); }
		
		return totalErrors == 0 && totalFailures == 0;
	}
	
	public boolean run(File testsDir,
	                   File reportsDir,
	                   File surefireReportsDir,
	                   File tempDir,
	                   Reporter reporter) {
		
		Map tests = new HashMap();
		for (File file : listXProcSpecFilesRecursively(testsDir))
			tests.put(
				file.getAbsolutePath().substring(testsDir.getAbsolutePath().length() + 1)
					.replaceAll("\\.xprocspec$", "")
					.replaceAll("[\\./\\\\]", "_"),
				file);
		File catalog = new File(testsDir, "catalog.xml");
		if (!catalog.exists()) catalog = null;
		return run(tests, reportsDir, surefireReportsDir, tempDir, catalog, reporter);
	}
	
	public static interface Reporter {
		
		public void init();
		public void running(String name) ;
		public void result(int run, int failures, int errors, int skipped, long time, String shortDesc, String longDesc);
		public void finish(int run, int failures, int errors, int skipped, long time);
		
		public static class DefaultReporter implements Reporter {
			
			private PrintStream stream;
			private String currentTest;
			private List failedTests = new ArrayList();
			private List testsInError = new ArrayList();
			
			public DefaultReporter() {
				this(System.out);
			}
			
			public DefaultReporter(PrintStream stream) {
				this.stream = stream;
			}
			
			private void println(String format, Object... args) {
				stream.format(format + "\n", args);
			}
			
			public void init() {
				println("-------------------------------------------------------");
				println(" X P R O C S P E C   T E S T S");
				println("-------------------------------------------------------");
			}
			
			public void running(String name) {
				println("Running %s", name);
				currentTest = name;
			}
			
			public void result(int run, int failures, int errors, int skipped, long time, String shortDesc, String longDesc) {
				println("Tests run: %d, Failures: %d, Errors: %d, Skipped: %d, Time elapsed: %s sec%s",
				        run, failures, errors, skipped, (new DecimalFormat("0.#")).format(time),
				        (failures > 0 || errors > 0) ? " <<< FAILURE!" : "");
				List summary = errors > 0 ? testsInError : failures > 0 ? failedTests : null;
				if (summary != null) {
					if (shortDesc != null)
						summary.add(currentTest + ": " + shortDesc);
					else
						summary.add(currentTest); }
				if (longDesc != null)
					println(longDesc);
			}
			
			public void finish(int run, int failures, int errors, int skipped, long time) {
				if (run == 0)
					println("There are no tests to run.");
				println("");
				println("Results :");
				println("");
				if (failedTests.size() > 0) {
					println("Failed tests:");
					for (String test : failedTests)
						println("  " + test);
					println(""); }
				if (testsInError.size() > 0) {
					println("Tests in error:");
					for (String test : testsInError)
						println("  " + test);
					println(""); }
				println("Tests run: %d, Failures: %d, Errors: %d, Skipped: %d", run, failures, errors, skipped);
			}
		}
	}
	
	/*
	 * FileUtils.listFiles from Apache Commons IO could be used here as well,
	 * but would introduce another dependency.
	 */
	private static Collection listXProcSpecFilesRecursively(File directory) {
		ImmutableList.Builder builder = new ImmutableList.Builder();
		for (File file : directory.listFiles()) {
			if (file.isDirectory())
				builder.addAll(listXProcSpecFilesRecursively(file));
			else if (file.getName().endsWith(".xprocspec"))
				builder.add(file); }
		return builder.build();
	}
	
	public static URI asURI(Object o) {
		try {
			if (o instanceof URI)
				return (URI)o;
			if (o instanceof File)
				return asURI(((File)o).toURI());
			if (o instanceof URL) {
				URL url = (URL)o;
				if (url.getProtocol().equals("jar"))
					return new URI("jar:" + new URI(null, url.getAuthority(), url.getPath(), url.getQuery(), url.getRef()).toASCIIString());
				String authority = (url.getPort() != -1) ?
					url.getHost() + ":" + url.getPort() :
					url.getHost();
				return new URI(url.getProtocol(), authority, url.getPath(), url.getQuery(), url.getRef()); }}
		catch (Exception e) {}
		throw new RuntimeException("Object can not be converted to URI: " + o);
	}
	
	private static XPath xpath = new XPathFactoryImpl().newXPath();
	
	public static Object evaluateXPath(File file, String expression, final Map namespaces, Class type) {
		try {
			if (namespaces != null)
				xpath.setNamespaceContext(
					new NamespaceContext() {
						public String getNamespaceURI(String prefix) {
							return namespaces.get(prefix); }
						public String getPrefix(String namespaceURI) {
							for (String prefix : namespaces.keySet())
								if (namespaces.get(prefix).equals(namespaceURI))
									return prefix;
							return null; }
						public Iterator getPrefixes(String namespaceURI) {
							List prefixes = new ArrayList();
							for (String prefix : namespaces.keySet())
								if (namespaces.get(prefix).equals(namespaceURI))
									prefixes.add(prefix);
							return prefixes.iterator(); }});
			else
				xpath.setNamespaceContext(null);
			XPathExpression expr = xpath.compile(expression);
			InputSource source = new InputSource(file.toURI().toURL().openStream());
			if (type.equals(Boolean.class))
				return expr.evaluate(source, XPathConstants.BOOLEAN);
			if (type.equals(String.class))
				return expr.evaluate(source, XPathConstants.STRING);
			if (type.equals(Integer.class))
				return ((Double)expr.evaluate(source, XPathConstants.NUMBER)).intValue();
			if (type.equals(Long.class))
				return ((Double)expr.evaluate(source, XPathConstants.NUMBER)).longValue();
			else
				throw new RuntimeException("Cannot evaluate to a " + type.getName()); }
		catch (Exception e) {
			throw new RuntimeException("Exception occured during XPath evaluation.", e); }
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy