/** * This file is licensed under the University of Illinois/NCSA Open Source License. See LICENSE.TXT for details. */ package edu.illinois.keshmesh.detector.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.testplugin.JavaProjectHelper; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.internal.matchers.IsCollectionContaining; import edu.illinois.keshmesh.config.AbsentConfigurationOptionsInputStreamFactory; import edu.illinois.keshmesh.config.ConfigurationOptions; import edu.illinois.keshmesh.config.ConfigurationOptionsReaderFactory; import edu.illinois.keshmesh.constants.Constants; import edu.illinois.keshmesh.detector.IntermediateResults; import edu.illinois.keshmesh.detector.Main; import edu.illinois.keshmesh.detector.bugs.BugInstance; import edu.illinois.keshmesh.detector.bugs.BugInstances; import edu.illinois.keshmesh.detector.bugs.BugPattern; import edu.illinois.keshmesh.detector.bugs.BugPatterns; import edu.illinois.keshmesh.detector.bugs.CodePosition; import edu.illinois.keshmesh.detector.bugs.FixInformation; import edu.illinois.keshmesh.detector.exception.Exceptions.WALAInitializationException; import edu.illinois.keshmesh.report.FileWriterFactory; import edu.illinois.keshmesh.report.Reporter; import edu.illinois.keshmesh.report.ReporterFactory; import edu.illinois.keshmesh.report.StringWriterFactory; import edu.illinois.keshmesh.util.Logger; import edu.illinois.keshmesh.util.Modes; /** * * For each detected bug in the test input file, a number is assigned. For each * bug number, in the output folder a sub folder with that number is created * which contains the input file with that bug fixed * */ @SuppressWarnings("restriction") public abstract class AbstractTestCase { private boolean isBuildDone = false; private IJavaProject javaProject; private IPackageFragmentRoot fragmentRoot; private IPackageFragment packageP; private String testNumber; /* * Maps absolute path of the input Java file into the absolute path of the * file in the target workspace. */ private Map<String, IPath> inputFileToTargetMap; private Set<NumberedBugInstance> expectedBugInstances; private BugInstances bugInstances; static final String CONTAINER = "src"; static final String PACKAGE_NAME = "p"; private void setUpProject(String testID) throws Exception { javaProject = TestSetupHelper.createAndInitializeProject(testID); //Should be called after the projects are created //TestSetupHelper.setAutoBuilding(false); fragmentRoot = JavaProjectHelper.addSourceContainer(javaProject, CONTAINER); packageP = fragmentRoot.createPackageFragment(PACKAGE_NAME, true, null); } @Test public void shouldFindAllBugInstances() { assertEquals(expectedBugInstances.size(), bugInstances.size()); } @Test public void bugInstancesShouldExist() { for (NumberedBugInstance numberedBugInstance : expectedBugInstances) { bugInstanceShouldExist(numberedBugInstance.getBugInstance()); } } // @After public void tearDown() throws Exception { JavaProjectHelper.performDummySearch(); JavaProjectHelper.delete(javaProject); } protected abstract BugPattern getBugPattern(); protected IntermediateResults getIntermediateResults() { return getBugPattern().getBugPatternDetector().getIntermediateResults(); } protected abstract void fixBugInstance(BugInstance bugInstance) throws OperationCanceledException, CoreException; protected abstract BugInstanceCreator getBugInstanceCreator(); class BugInstanceMatcher extends BaseMatcher<BugInstance> { private final BugInstance bugInstance; public BugInstanceMatcher(BugInstance expectedBugInstance) { bugInstance = expectedBugInstance; } @Override public boolean matches(Object arg) { if (bugInstance == null || arg == null) { return bugInstance == null && arg == null; } else { return bugInstance.portableEquals(arg); } } @Override public void describeTo(Description description) { description.appendValue(bugInstance); } } private void bugInstanceShouldExist(BugInstance expectedBugInstance) { IsCollectionContaining<BugInstance> collectionContainsExpectedBugInstance = new IsCollectionContaining<BugInstance>(new BugInstanceMatcher(expectedBugInstance)); assertThat(bugInstances, collectionContainsExpectedBugInstance); } private String getPathForInputFile(String inputFileName) { String prefix = TestSetupHelper.join("test-files", getBugPattern().getName(), testNumber); assertTrue(String.format("The path %s contains \"in\"", prefix), !prefix.contains("in")); return TestSetupHelper.join(prefix, "in", inputFileName); } protected void setupProjectAndAnalyze(String testNumber, String... inputFileNames) throws Exception { this.testNumber = testNumber; inputFileToTargetMap = new HashMap<String, IPath>(); expectedBugInstances = new HashSet<NumberedBugInstance>(); setUpProject(getTestID()); for (String inputFileName : inputFileNames) { addFile(inputFileName); } parseExpectedBugInstances(inputFileNames); buildSynchronously(); findBugs(); } private void buildSynchronously() throws CoreException, InterruptedException { final Object lock = new Object(); ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, new BuildProgressMonitor(lock)); synchronized (lock) { if (!isBuildDone) { lock.wait(); } } } private void parseExpectedBugInstances(String... inputFileNames) throws IOException { for (String inputFileName : inputFileNames) { BugInstanceParser bugInstanceParser = new BugInstanceParser(getBugInstanceCreator(), getTargetPathForInputFile(inputFileName)); expectedBugInstances.addAll(bugInstanceParser.parseExpectedBugInstances()); } } private String getTestID() { return getBugPattern().getName() + "-" + testNumber; } private void addFile(String inputFileName) throws Exception { String inputFileString = getPathForInputFile(inputFileName); Path inputFilePath = new Path(inputFileString); File inputFile = Activator.getDefault().getFileInPlugin(inputFilePath); String inputFileContents = TestSetupHelper.format(TestSetupHelper.getFileContent(inputFile.getAbsolutePath())); ICompilationUnit compilationUnit = TestSetupHelper.createCU(packageP, inputFilePath.lastSegment(), inputFileContents); inputFileToTargetMap.put(inputFileString, compilationUnit.getResource().getLocation()); } private IPath getTargetPathForInputFile(String inputFileName) { String pathForInputFile = getPathForInputFile(inputFileName); if (!inputFileToTargetMap.containsKey(pathForInputFile)) { throw new RuntimeException("Could not find the path to test class \"" + pathForInputFile + "\""); } return inputFileToTargetMap.get(pathForInputFile); } private void findBugs() throws WALAInitializationException { Modes.setInTestMode(true); BugPatterns.enableBugPatterns(getBugPattern()); Reporter reporter = new ReporterFactory().create(new FileWriterFactory(Constants.PROFILING_RESULTS_FILENAME, new StringWriterFactory()), Constants.PROFILING_RESULTS_HEADER); ConfigurationOptions configurationOptions = new ConfigurationOptionsReaderFactory(new AbsentConfigurationOptionsInputStreamFactory()).create().read(); bugInstances = Main.initAndPerformAnalysis(javaProject, reporter, configurationOptions); Logger.log(bugInstances.toString()); } private void tryFix(BugInstance bugInstance, String bugInstanceNumber) throws IOException, OperationCanceledException, CoreException { if (bugInstances.size() == 1) bugInstanceNumber = ""; BugInstance actualBugInstance = bugInstances.portableFind(bugInstance); Assert.assertNotNull("Could not find bug instance.", actualBugInstance); fixBugInstance(actualBugInstance); if (bugInstance.getBugPattern().hasFixer()) { checkFix(bugInstanceNumber); } } private void checkFix(String bugInstanceNumber) throws IOException { for (Map.Entry<String, IPath> entry : inputFileToTargetMap.entrySet()) { TestSetupHelper.compareFiles(TestSetupHelper.getPathForOutputFile(entry.getKey(), bugInstanceNumber), entry.getValue().toPortableString()); } } protected void tryFix(String bugInstanceNumber) throws OperationCanceledException, IOException, CoreException { boolean foundBugInstance = false; for (NumberedBugInstance numberedBugInstance : expectedBugInstances) { if (numberedBugInstance.getNumber().equals(bugInstanceNumber)) { tryFix(numberedBugInstance.getBugInstance(), numberedBugInstance.getNumber()); foundBugInstance = true; } } assertTrue(String.format("Could not find bug instance number %s.", bugInstanceNumber), foundBugInstance); } @Before public abstract void setup() throws Exception; private Collection<String> getExpectedBugInstanceNumbers() { Collection<String> expectedBugInstanceNumbers = new LinkedList<String>(); for (NumberedBugInstance numberedBugInstance : expectedBugInstances) { expectedBugInstanceNumbers.add(numberedBugInstance.getNumber()); } return expectedBugInstanceNumbers; } @Test public void tryFixExpectedBugInstances() throws Exception { for (String bugInstanceNumber : getExpectedBugInstanceNumbers()) { setup(); tryFix(bugInstanceNumber); } } private final class BuildProgressMonitor extends NullProgressMonitor { private final Object lock; private BuildProgressMonitor(Object lock) { this.lock = lock; } @Override public void done() { synchronized (lock) { if (!isBuildDone) { isBuildDone = true; lock.notify(); } } } } protected static abstract class GeneralBugInstanceCreator implements BugInstanceCreator { @Override public BugInstance createTestBugInstance(BugPattern bugPattern, int firstLine, int lastLine, IPath targetFilePath, String... replacements) { return new BugInstance(bugPattern, new CodePosition(firstLine, lastLine, targetFilePath, null), createFixInformation(replacements)); } @Override public abstract FixInformation createFixInformation(String... replacements); } }