/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cache;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
public class FileAnalysisCacheTest {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
private File unexistingCacheFile;
private File newCacheFile;
private File emptyCacheFile;
private File sourceFile;
@Before
public void setUp() throws IOException {
unexistingCacheFile = new File(tempFolder.getRoot(), "non-existing-file.cache");
newCacheFile = new File(tempFolder.getRoot(), "pmd-analysis.cache");
emptyCacheFile = tempFolder.newFile();
sourceFile = tempFolder.newFile("Source.java");
}
@Test
public void testLoadFromNonExistingFile() throws IOException {
final FileAnalysisCache cache = new FileAnalysisCache(unexistingCacheFile);
assertNotNull("Cache creation from non existing file failed.", cache);
}
@Test
public void testLoadFromEmptyFile() throws IOException {
final FileAnalysisCache cache = new FileAnalysisCache(emptyCacheFile);
assertNotNull("Cache creation from empty file failed.", cache);
}
@Test
public void testLoadFromDirectoryShouldntThrow() throws IOException {
new FileAnalysisCache(tempFolder.getRoot());
}
@Test
public void testLoadFromUnreadableFileShouldntThrow() throws IOException {
emptyCacheFile.setReadable(false);
new FileAnalysisCache(emptyCacheFile);
}
@Test
public void testStoreCreatesFile() {
final FileAnalysisCache cache = new FileAnalysisCache(unexistingCacheFile);
cache.persist();
assertTrue("Cache file doesn't exist after store", unexistingCacheFile.exists());
}
@Test
public void testStoreOnUnwritableFileShouldntThrow() {
emptyCacheFile.setWritable(false);
final FileAnalysisCache cache = new FileAnalysisCache(emptyCacheFile);
cache.persist();
}
@Test
public void testStorePersistsFilesWithViolations() {
final FileAnalysisCache cache = new FileAnalysisCache(newCacheFile);
cache.isUpToDate(sourceFile);
final RuleViolation rv = mock(RuleViolation.class, Mockito.RETURNS_SMART_NULLS);
when(rv.getFilename()).thenReturn(sourceFile.getPath());
final net.sourceforge.pmd.Rule rule = mock(net.sourceforge.pmd.Rule.class, Mockito.RETURNS_SMART_NULLS);
when(rv.getRule()).thenReturn(rule);
cache.ruleViolationAdded(rv);
cache.persist();
final FileAnalysisCache reloadedCache = new FileAnalysisCache(newCacheFile);
assertTrue("Cache believes unmodified file with violations is not up to date",
reloadedCache.isUpToDate(sourceFile));
final List<RuleViolation> cachedViolations = reloadedCache.getCachedViolations(sourceFile);
assertEquals("Cached rule violations count mismatch", 1, cachedViolations.size());
}
@Test
public void testCacheValidityWithNoChanges() {
final RuleSets rs = mock(RuleSets.class);
final URLClassLoader cl = mock(URLClassLoader.class);
setupCacheWithFiles(newCacheFile, rs, cl, sourceFile);
final FileAnalysisCache reloadedCache = new FileAnalysisCache(newCacheFile);
reloadedCache.checkValidity(rs, cl);
assertTrue("Cache believes unmodified file is not up to date without ruleset / classpath changes",
reloadedCache.isUpToDate(sourceFile));
}
@Test
public void testRulesetChangeInvalidatesCache() {
final RuleSets rs = mock(RuleSets.class);
final URLClassLoader cl = mock(URLClassLoader.class);
setupCacheWithFiles(newCacheFile, rs, cl, sourceFile);
final FileAnalysisCache reloadedCache = new FileAnalysisCache(newCacheFile);
when(rs.getChecksum()).thenReturn(1L);
reloadedCache.checkValidity(rs, cl);
assertFalse("Cache believes unmodified file is up to date after ruleset changed",
reloadedCache.isUpToDate(sourceFile));
}
@Test
public void testClasspathChangeWithoutDFAorTypeResolutionDoesNotInvalidatesCache() throws MalformedURLException, IOException {
final RuleSets rs = mock(RuleSets.class);
final URLClassLoader cl = mock(URLClassLoader.class);
setupCacheWithFiles(newCacheFile, rs, cl, sourceFile);
final FileAnalysisCache reloadedCache = new FileAnalysisCache(newCacheFile);
when(cl.getURLs()).thenReturn(new URL[] { tempFolder.newFile().toURI().toURL(), });
reloadedCache.checkValidity(rs, cl);
assertTrue("Cache believes unmodified file is not up to date after classpath changed when no rule cares",
reloadedCache.isUpToDate(sourceFile));
}
@Test
public void testClasspathChangeInvalidatesCache() throws MalformedURLException, IOException {
final RuleSets rs = mock(RuleSets.class);
final URLClassLoader cl = mock(URLClassLoader.class);
setupCacheWithFiles(newCacheFile, rs, cl, sourceFile);
final FileAnalysisCache reloadedCache = new FileAnalysisCache(newCacheFile);
when(cl.getURLs()).thenReturn(new URL[] { tempFolder.newFile().toURI().toURL(), });
final net.sourceforge.pmd.Rule r = mock(net.sourceforge.pmd.Rule.class);
when(r.usesDFA()).thenReturn(true);
when(rs.getAllRules()).thenReturn(Collections.singleton(r));
reloadedCache.checkValidity(rs, cl);
assertFalse("Cache believes unmodified file is up to date after classpath changed",
reloadedCache.isUpToDate(sourceFile));
}
@Test
public void testUnknownFileIsNotUpToDate() throws IOException {
final FileAnalysisCache cache = new FileAnalysisCache(newCacheFile);
assertFalse("Cache believes an unknown file is up to date",
cache.isUpToDate(sourceFile));
}
@Test
public void testFileIsUpToDate() throws IOException {
setupCacheWithFiles(newCacheFile, mock(RuleSets.class), mock(ClassLoader.class), sourceFile);
final FileAnalysisCache cache = new FileAnalysisCache(newCacheFile);
assertTrue("Cache believes a known, unchanged file is not up to date",
cache.isUpToDate(sourceFile));
}
@Test
public void testFileIsNotUpToDateWhenEdited() throws IOException {
setupCacheWithFiles(newCacheFile, mock(RuleSets.class), mock(ClassLoader.class), sourceFile);
// Edit the file
Files.write(Paths.get(sourceFile.getAbsolutePath()), "some text".getBytes());
final FileAnalysisCache cache = new FileAnalysisCache(newCacheFile);
assertFalse("Cache believes a known, changed file is up to date",
cache.isUpToDate(sourceFile));
}
private void setupCacheWithFiles(final File cacheFile, final RuleSets ruleSets,
final ClassLoader classLoader, final File... files) {
// Setup a cache file with an entry for an empty Source.java with no violations
final FileAnalysisCache cache = new FileAnalysisCache(cacheFile);
cache.checkValidity(ruleSets, classLoader);
for (final File f : files) {
cache.isUpToDate(f);
}
cache.persist();
}
}