package org.jboss.windup.tests.application;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.windup.exec.WindupProcessor;
import org.jboss.windup.exec.WindupProgressMonitor;
import org.jboss.windup.exec.configuration.WindupConfiguration;
import org.jboss.windup.exec.configuration.options.UserRulesDirectoryOption;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.GraphContextFactory;
import org.jboss.windup.graph.model.ProjectModel;
import org.jboss.windup.graph.service.GraphService;
import org.jboss.windup.reporting.model.ApplicationReportModel;
import org.jboss.windup.reporting.model.MigrationIssuesReportModel;
import org.jboss.windup.reporting.model.ReportModel;
import org.jboss.windup.reporting.service.ReportService;
import org.jboss.windup.rules.apps.java.config.ExcludePackagesOption;
import org.jboss.windup.rules.apps.java.config.ScanPackagesOption;
import org.jboss.windup.rules.apps.java.config.SourceModeOption;
import org.jboss.windup.rules.apps.java.dependencyreport.CreateDependencyReportRuleProvider;
import org.jboss.windup.rules.apps.java.model.JavaApplicationOverviewReportModel;
import org.jboss.windup.rules.apps.java.model.JavaClassFileModel;
import org.jboss.windup.rules.apps.java.reporting.rules.CreateJavaApplicationOverviewReportRuleProvider;
import org.jboss.windup.rules.apps.java.reporting.rules.EnableCompatibleFilesReportOption;
import org.jboss.windup.rules.apps.tattletale.EnableTattletaleReportOption;
import org.junit.Assert;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* Base class for Windup end-to-end tests.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public abstract class WindupArchitectureTest
{
public static final String REPORTS_TEMPLATES_MIGRATION_ISSUES_FTL = "/reports/templates/migration-issues.ftl";
@Inject
private WindupProcessor processor;
@Inject
private GraphContextFactory factory;
/**
* Get an instance of the {@link GraphContextFactory}.
*/
protected GraphContextFactory getFactory()
{
return factory;
}
Path getDefaultPath()
{
return FileUtils.getTempDirectory().toPath().resolve("Windup")
.resolve("windupgraph_" + RandomStringUtils.randomAlphanumeric(6));
}
protected GraphContext createGraphContext()
{
return createGraphContext(getDefaultPath());
}
GraphContext createGraphContext(Path path)
{
return factory.create(path);
}
protected void runTest(String inputPath, boolean sourceMode) throws Exception
{
runTest(Collections.singletonList(inputPath), sourceMode);
}
protected void runTest(Iterable<String> inputPaths, boolean sourceMode) throws Exception
{
List<String> includeList = Collections.emptyList();
List<String> excludeList = Collections.emptyList();
runTest(createGraphContext(), inputPaths, null, sourceMode, includeList, excludeList);
}
protected void runTest(GraphContext graphContext, String inputPath, boolean sourceMode)
throws Exception
{
runTest(graphContext, Collections.singletonList(inputPath), sourceMode);
}
void runTest(GraphContext graphContext, Iterable<String> inputPaths, boolean sourceMode)
throws Exception
{
List<String> includeList = Collections.emptyList();
List<String> excludeList = Collections.emptyList();
runTest(graphContext, inputPaths, null, sourceMode, includeList, excludeList);
}
void runTest(GraphContext graphContext, String inputPath, boolean sourceMode,
List<String> includePackages) throws Exception
{
runTest(graphContext, Collections.singletonList(inputPath), sourceMode, includePackages);
}
void runTest(GraphContext graphContext, Iterable<String> inputPaths, boolean sourceMode,
List<String> includePackages) throws Exception
{
List<String> excludeList = Collections.emptyList();
runTest(graphContext, inputPaths, null, sourceMode, includePackages, excludeList);
}
void runTest(final GraphContext graphContext,
final String inputPath,
final File userRulesDir,
final boolean sourceMode,
final List<String> includePackages,
final List<String> excludePackages) throws Exception
{
runTest(graphContext, Collections.singletonList(inputPath), userRulesDir, sourceMode, includePackages, excludePackages);
}
void runTest(final GraphContext graphContext,
final Iterable<String> inputPaths,
final File userRulesDir,
final boolean sourceMode,
final List<String> includePackages,
final List<String> excludePackages) throws Exception
{
Map<String, Object> otherOptions = Collections.emptyMap();
runTest(graphContext, inputPaths, userRulesDir, sourceMode, includePackages, excludePackages, otherOptions);
}
void runTest(final GraphContext graphContext,
final Iterable<String> inputPaths,
final File userRulesDir,
final boolean sourceMode,
final List<String> includePackages,
final List<String> excludePackages,
final Map<String, Object> otherOptions) throws Exception
{
Locale previousLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
WindupConfiguration windupConfiguration = new WindupConfiguration().setGraphContext(graphContext);
windupConfiguration.setAlwaysHaltOnException(true);
for (String inputPath : inputPaths)
{
windupConfiguration.addInputPath(Paths.get(inputPath));
}
windupConfiguration.setOutputDirectory(graphContext.getGraphDirectory());
if (userRulesDir != null)
{
windupConfiguration.setOptionValue(UserRulesDirectoryOption.NAME, userRulesDir);
}
windupConfiguration.setOptionValue(SourceModeOption.NAME, sourceMode);
windupConfiguration.setOptionValue(ScanPackagesOption.NAME, includePackages);
windupConfiguration.setOptionValue(ExcludePackagesOption.NAME, excludePackages);
windupConfiguration.setOptionValue(EnableTattletaleReportOption.NAME, false);
windupConfiguration.setOptionValue(EnableCompatibleFilesReportOption.NAME, true);
for (Map.Entry<String, Object> otherOption : otherOptions.entrySet())
{
windupConfiguration.setOptionValue(otherOption.getKey(), otherOption.getValue());
}
RecordingWindupProgressMonitor recordingMonitor = new RecordingWindupProgressMonitor();
WindupProgressMonitor testMonitor = overrideWindupProgressMonitor(recordingMonitor);
windupConfiguration.setProgressMonitor(testMonitor);
processor.execute(windupConfiguration);
assertRecordedData(recordingMonitor);
Locale.setDefault(previousLocale);
}
/**
* Override to customize what's expected.
*/
protected void assertRecordedData(RecordingWindupProgressMonitor recordingMonitor)
{
Assert.assertFalse(recordingMonitor.isCancelled());
Assert.assertTrue(recordingMonitor.isDone());
Assert.assertFalse(recordingMonitor.getSubTaskNames().isEmpty());
Assert.assertTrue(recordingMonitor.getTotalWork() > 0);
Assert.assertTrue(recordingMonitor.getCompletedWork() > 0);
Assert.assertEquals(recordingMonitor.getTotalWork() + 1, recordingMonitor.getCompletedWork());
}
/**
* Override this if you need to intercept the calls to WindupProgressMonitor.
* The overriding method must keep the functionality of passed monitor.
* See {@link CombinedWindupProgressMonitor}.
*/
public WindupProgressMonitor overrideWindupProgressMonitor(final WindupProgressMonitor testMonitor)
{
return testMonitor;
}
JavaApplicationOverviewReportModel getMainApplicationReport(GraphContext context)
{
return (JavaApplicationOverviewReportModel) getReport(context, CreateJavaApplicationOverviewReportRuleProvider.TEMPLATE_APPLICATION_REPORT,
CreateJavaApplicationOverviewReportRuleProvider.DETAILS_REPORT);
}
Iterable<ReportModel> getApplicationDetailsReports(GraphContext context)
{
return getReports(context, CreateJavaApplicationOverviewReportRuleProvider.TEMPLATE_APPLICATION_REPORT);
}
MigrationIssuesReportModel getMigrationIssuesReport(GraphContext context, final ProjectModel projectModel)
{
Iterable<ReportModel> reportModels = Iterables.filter(getReports(context, REPORTS_TEMPLATES_MIGRATION_ISSUES_FTL),
new Predicate<ReportModel>()
{
@Override
public boolean apply(@Nullable ReportModel input)
{
if (!(input instanceof MigrationIssuesReportModel))
return false;
MigrationIssuesReportModel migrationIssuesReportModel = (MigrationIssuesReportModel) input;
return projectModel == null || projectModel.equals(migrationIssuesReportModel.getProjectModel());
}
});
if (!reportModels.iterator().hasNext())
return null;
if (Iterables.size(reportModels) > 1)
Assert.fail("Only one migration issues report expected for the application!");
return (MigrationIssuesReportModel)reportModels.iterator().next();
}
MigrationIssuesReportModel getMigrationIssuesReport(GraphContext context)
{
return (MigrationIssuesReportModel) getReport(context, REPORTS_TEMPLATES_MIGRATION_ISSUES_FTL, "Migration Issues");
}
ApplicationReportModel getJarDependencyReport(GraphContext context)
{
return (ApplicationReportModel) getReport(context, CreateDependencyReportRuleProvider.TEMPLATE, CreateDependencyReportRuleProvider.REPORT_NAME);
}
Iterable<ReportModel> getReports(GraphContext context, String template)
{
ReportService reportService = new ReportService(context);
return reportService.findAllByProperty(ReportModel.TEMPLATE_PATH, template);
}
ReportModel getReport(GraphContext context, String template, String name)
{
ReportService reportService = new ReportService(context);
Iterable<ReportModel> reportModels = reportService.findAllByProperty(
ReportModel.TEMPLATE_PATH,
template);
ReportModel reportModel = null;
for (ReportModel candidateModel : reportModels)
{
if (StringUtils.equals(candidateModel.getReportName(), name))
{
reportModel = candidateModel;
break;
}
}
Assert.assertNotNull(reportModel);
return reportModel;
}
protected void allDecompiledFilesAreLinked(GraphContext context)
{
GraphService<JavaClassFileModel> classModels = new GraphService<>(context, JavaClassFileModel.class);
for (JavaClassFileModel javaClassFileModel : classModels.findAllWithoutProperty(JavaClassFileModel.SKIP_DECOMPILATION, true))
{
Assert.assertNotNull(javaClassFileModel.getJavaClass().getDecompiledSource());
}
}
/**
* Stores the info about incoming calls which the tests can review.
*/
protected static class RecordingWindupProgressMonitor implements WindupProgressMonitor
{
private int totalWork = -1;
private boolean done;
private boolean cancelled;
private List<String> taskNames = new ArrayList<>();
private List<String> subTaskNames = new ArrayList<>();
private int workDone;
@Override
public void beginTask(String name, int totalWork)
{
if (this.totalWork == -1)
this.totalWork = totalWork;
else
throw new IllegalStateException("Total work already set.");
}
@Override
public void done()
{
this.done = true;
}
@Override
public boolean isCancelled()
{
return cancelled;
}
@Override
public void setCancelled(boolean cancelled)
{
if (cancelled)
this.cancelled = true;
}
@Override
public void setTaskName(String name)
{
this.taskNames.add(name);
}
@Override
public void subTask(String name)
{
this.subTaskNames.add(name);
}
@Override
public void worked(int work)
{
this.workDone += work;
}
public int getTotalWork()
{
return totalWork;
}
public boolean isDone()
{
return done;
}
public List<String> getSubTaskNames()
{
return subTaskNames;
}
public int getCompletedWork()
{
return workDone;
}
}
}