package org.jnario.maven; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Lists.transform; import static java.util.Arrays.asList; import static org.eclipse.xtext.util.Strings.concat; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.List; import java.util.Map.Entry; import org.apache.maven.plugin.MojoExecutionException; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtend.core.compiler.batch.XtendBatchCompiler; import org.eclipse.xtend.lib.macro.file.Path; import org.eclipse.xtend.maven.MavenProjectAdapter; import org.eclipse.xtend.maven.XtendTestCompile; import org.eclipse.xtext.ISetup; import org.eclipse.xtext.xbase.file.ProjectConfig; import org.eclipse.xtext.xbase.file.RuntimeWorkspaceConfigProvider; import org.eclipse.xtext.xbase.file.WorkspaceConfig; import org.jnario.compiler.HtmlAssetsCompiler; import org.jnario.compiler.JnarioDocCompiler; import org.jnario.feature.FeatureStandaloneSetup; import org.jnario.report.Executable2ResultMapping; import org.jnario.report.HashBasedSpec2ResultMapping; import org.jnario.report.SpecResultParser; import org.jnario.spec.SpecStandaloneSetup; import org.jnario.suite.SuiteStandaloneSetup; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; /** * Goal which generates Jnario documentation. * * @author Sebastian Benz - Initial contribution and API * @requiresDependencyResolution test * @goal generate */ public class JnarioDocGenerate extends XtendTestCompile { private final class XmlFiles implements FilenameFilter { public boolean accept(File dir, String name) { return name.endsWith("xml"); } } /** * Location of the generated documentation. * * @parameter default-value="${basedir}/target/jnario-doc" * @required */ private String docOutputDirectory; /** * Location of the generated JUnit XML reports. * * @parameter default-value="${basedir}/target/surefire-reports" * @required */ private String reportsDirectory; /** * Location of the generated JUnit XML reports. * * @parameter */ private String sourceDirectory; @Inject private RuntimeWorkspaceConfigProvider workspaceConfigProvider; private Provider<ResourceSet> resourceSetProvider; @Override protected void internalExecute() throws MojoExecutionException { getLog().info("Generating Jnario reports to " + docOutputDirectory); // the order is important, the suite compiler must be executed last List<Injector> injectors = createInjectors(new SpecStandaloneSetup(), new FeatureStandaloneSetup(), new SuiteStandaloneSetup()); generateCssAndJsFiles(injectors); resourceSetProvider = new JnarioMavenProjectResourceSetProvider(project); HashBasedSpec2ResultMapping resultMapping = createSpec2ResultMapping(injectors); for (Injector injector : injectors) { generateDoc(injector, resultMapping); } } protected HashBasedSpec2ResultMapping createSpec2ResultMapping(List<Injector> injectors) throws MojoExecutionException { HashBasedSpec2ResultMapping resultMapping = injectors.get(2).getInstance(HashBasedSpec2ResultMapping.class); File reportFolder = new File(reportsDirectory); if(reportFolder.exists()){ addExecutionResults(resultMapping, reportFolder); }else{ throw new MojoExecutionException("Surefire Report folder does not exist"); } return resultMapping; } protected void generateCssAndJsFiles(List<Injector> injectors) { HtmlAssetsCompiler assetsCompiler = injectors.get(0).getInstance(HtmlAssetsCompiler.class); assetsCompiler.setOutputPath(docOutputDirectory); getLog().info("Generating HTML assets to " + docOutputDirectory); assetsCompiler.compile(); } protected void addExecutionResults(HashBasedSpec2ResultMapping resultMapping, File reportFolder) throws MojoExecutionException { SpecResultParser specResultParser = new SpecResultParser(); for (File file : reportFolder.listFiles(new XmlFiles())) { FileInputStream is = null; try { is = new FileInputStream(file); specResultParser.parse(is, resultMapping); } catch (Exception e) { throw new MojoExecutionException("Exception while parsing spec for: " + file, e); }finally{ try { is.close(); } catch (IOException e) { } } } } protected void compileTestSources(XtendBatchCompiler xtend2BatchCompiler) throws MojoExecutionException { List<String> testCompileSourceRoots = Lists.newArrayList(project.getTestCompileSourceRoots()); String testClassPath = concat(File.pathSeparator, getTestClassPath()); if(sourceDirectory != null){ testCompileSourceRoots = Collections.singletonList(sourceDirectory); } getLog().debug("source folders: " + testCompileSourceRoots); compile(xtend2BatchCompiler, testClassPath, testCompileSourceRoots, docOutputDirectory); } private void generateDoc(Injector injector, Executable2ResultMapping resultMapping) throws MojoExecutionException { JnarioDocCompiler docCompiler = injector.getInstance(JnarioDocCompiler.class); docCompiler.setExecutable2ResultMapping(resultMapping); compileTestSources(docCompiler); } protected void compile(XtendBatchCompiler xtend2BatchCompiler, String classPath, List<String> sourceDirectories, String outputPath) throws MojoExecutionException { configureWorkspace(sourceDirectories, outputPath); resourceSetProvider.get().eAdapters().clear(); xtend2BatchCompiler.setResourceSetProvider(resourceSetProvider); MavenProjectAdapter.install(resourceSetProvider.get(), project); Iterable<String> filtered = filter(sourceDirectories, FILE_EXISTS); if (Iterables.isEmpty(filtered)) { getLog().info( "skip compiling sources because the configured directory '" + Iterables.toString(sourceDirectories) + "' does not exists."); return; } getLog().debug("Set temp directory: " + getTempDirectory()); xtend2BatchCompiler.setTempDirectory(getTempDirectory()); getLog().debug("Set DeleteTempDirectory: " + false); xtend2BatchCompiler.setDeleteTempDirectory(false); getLog().debug("Set classpath: " + classPath); xtend2BatchCompiler.setClassPath(classPath); getLog().debug("Set source path: " + concat(File.pathSeparator, newArrayList(filtered))); xtend2BatchCompiler.setSourcePath(concat(File.pathSeparator, newArrayList(filtered))); getLog().debug("Set output path: " + outputPath); xtend2BatchCompiler.setOutputPath(outputPath); getLog().debug("Set encoding: " + encoding); xtend2BatchCompiler.setFileEncoding(encoding); getLog().debug("Set writeTraceFiles: " + writeTraceFiles); xtend2BatchCompiler.setWriteTraceFiles(writeTraceFiles); if (!xtend2BatchCompiler.compile()) { throw new MojoExecutionException("Error compiling xtend sources in '" + concat(File.pathSeparator, newArrayList(filtered)) + "'."); } } private void configureWorkspace(List<String> sourceDirectories, String outputPath) throws MojoExecutionException { WorkspaceConfig workspaceConfig = new WorkspaceConfig(project.getBasedir().getParentFile().getAbsolutePath()); ProjectConfig projectConfig = new ProjectConfig(project.getBasedir().getName()); URI absoluteRootPath = project.getBasedir().getAbsoluteFile().toURI(); URI relativizedTarget = absoluteRootPath.relativize(new File(outputPath).toURI()); if (relativizedTarget.isAbsolute()) { throw new MojoExecutionException("Output path '" + outputPath + "' must be a child of the project folder '" + absoluteRootPath + "'"); } for (String source : sourceDirectories) { URI relativizedSrc = absoluteRootPath.relativize(new File(source).toURI()); if (relativizedSrc.isAbsolute()) { throw new MojoExecutionException("Source folder " + source + " must be a child of the project folder " + absoluteRootPath); } projectConfig.addSourceFolderMapping(relativizedSrc.getPath(), relativizedTarget.getPath()); } workspaceConfig.addProjectConfig(projectConfig); workspaceConfigProvider.setWorkspaceConfig(workspaceConfig); if (getLog().isDebugEnabled()) { getLog().debug("WS config root: " + workspaceConfig.getAbsoluteFileSystemPath()); getLog().debug("Project name: " + projectConfig.getName()); getLog().debug("Project root path: " + projectConfig.getRootPath()); for (Entry<Path, Path> entry : projectConfig.getSourceFolderMappings().entrySet()) { getLog().debug("Source path: " + entry.getKey() + " -> " + entry.getValue()); } } } private List<Injector> createInjectors(ISetup... setups) { return transform(asList(setups), new Function<ISetup, Injector>() { public Injector apply(ISetup input) { return input.createInjectorAndDoEMFRegistration(); } }); } }