//////////////////////////////////////////////////////////////////////////////// // checkstyle: Checks Java source code for adherence to a set of rules. // Copyright (C) 2001-2017 the original author or authors. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// package com.puppycrawl.tools.checkstyle; import static com.puppycrawl.tools.checkstyle.Checker.EXCEPTION_MSG; import static org.hamcrest.CoreMatchers.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; import java.io.IOError; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.nio.file.Files; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.powermock.api.mockito.PowerMockito; import com.puppycrawl.tools.checkstyle.api.AbstractCheck; import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; import com.puppycrawl.tools.checkstyle.api.AuditEvent; import com.puppycrawl.tools.checkstyle.api.CheckstyleException; import com.puppycrawl.tools.checkstyle.api.Configuration; import com.puppycrawl.tools.checkstyle.api.DetailAST; import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; import com.puppycrawl.tools.checkstyle.api.Filter; import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; import com.puppycrawl.tools.checkstyle.api.TokenTypes; import com.puppycrawl.tools.checkstyle.checks.TranslationCheck; import com.puppycrawl.tools.checkstyle.checks.coding.HiddenFieldCheck; import com.puppycrawl.tools.checkstyle.filters.SuppressionFilter; import com.puppycrawl.tools.checkstyle.utils.CommonUtils; public class CheckerTest extends BaseCheckTestSupport { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); private static Method getAcceptFileStarted() throws NoSuchMethodException { final Class<Checker> checkerClass = Checker.class; final Method acceptFileStarted = checkerClass.getDeclaredMethod("acceptFileStarted", String.class); acceptFileStarted.setAccessible(true); return acceptFileStarted; } private static Method getFireAuditFinished() throws NoSuchMethodException { final Class<Checker> checkerClass = Checker.class; final Method fireAuditFinished = checkerClass.getDeclaredMethod("fireAuditFinished"); fireAuditFinished.setAccessible(true); return fireAuditFinished; } private static Method getFireAuditStartedMethod() throws NoSuchMethodException { final Class<Checker> checkerClass = Checker.class; final Method fireAuditStarted = checkerClass.getDeclaredMethod("fireAuditStarted"); fireAuditStarted.setAccessible(true); return fireAuditStarted; } @Test public void testDestroy() throws Exception { final Checker checker = new Checker(); final DebugAuditAdapter auditAdapter = new DebugAuditAdapter(); checker.addListener(auditAdapter); final DebugFilter filter = new DebugFilter(); checker.addFilter(filter); // should remove al listeners and filters checker.destroy(); // Let's try fire some events getFireAuditStartedMethod().invoke(checker); getFireAuditFinished().invoke(checker); checker.fireFileStarted("Some File Name"); checker.fireFileFinished("Some File Name"); final SortedSet<LocalizedMessage> messages = new TreeSet<>(); messages.add(new LocalizedMessage(0, 0, "a Bundle", "message.key", new Object[] {"arg"}, null, getClass(), null)); checker.fireErrors("Some File Name", messages); assertFalse("Checker.destroy() doesn't remove listeners.", auditAdapter.wasCalled()); assertFalse("Checker.destroy() doesn't remove filters.", filter.wasCalled()); } @Test public void testAddListener() throws Exception { final Checker checker = new Checker(); final DebugAuditAdapter auditAdapter = new DebugAuditAdapter(); checker.addListener(auditAdapter); // Let's try fire some events getFireAuditStartedMethod().invoke(checker); assertTrue("Checker.fireAuditStarted() doesn't call listener", auditAdapter.wasCalled()); auditAdapter.resetListener(); getFireAuditFinished().invoke(checker); assertTrue("Checker.fireAuditFinished() doesn't call listener", auditAdapter.wasCalled()); auditAdapter.resetListener(); checker.fireFileStarted("Some File Name"); assertTrue("Checker.fireFileStarted() doesn't call listener", auditAdapter.wasCalled()); auditAdapter.resetListener(); checker.fireFileFinished("Some File Name"); assertTrue("Checker.fireFileFinished() doesn't call listener", auditAdapter.wasCalled()); auditAdapter.resetListener(); final SortedSet<LocalizedMessage> messages = new TreeSet<>(); messages.add(new LocalizedMessage(0, 0, "a Bundle", "message.key", new Object[] {"arg"}, null, getClass(), null)); checker.fireErrors("Some File Name", messages); assertTrue("Checker.fireErrors() doesn't call listener", auditAdapter.wasCalled()); } @Test public void testRemoveListener() throws Exception { final Checker checker = new Checker(); final DebugAuditAdapter auditAdapter = new DebugAuditAdapter(); final DebugAuditAdapter aa2 = new DebugAuditAdapter(); checker.addListener(auditAdapter); checker.addListener(aa2); checker.removeListener(auditAdapter); // Let's try fire some events getFireAuditStartedMethod().invoke(checker); assertTrue("Checker.fireAuditStarted() doesn't call listener", aa2.wasCalled()); assertFalse("Checker.fireAuditStarted() does call removed listener", auditAdapter.wasCalled()); aa2.resetListener(); getFireAuditFinished().invoke(checker); assertTrue("Checker.fireAuditFinished() doesn't call listener", aa2.wasCalled()); assertFalse("Checker.fireAuditFinished() does call removed listener", auditAdapter.wasCalled()); aa2.resetListener(); checker.fireFileStarted("Some File Name"); assertTrue("Checker.fireFileStarted() doesn't call listener", aa2.wasCalled()); assertFalse("Checker.fireFileStarted() does call removed listener", auditAdapter.wasCalled()); aa2.resetListener(); checker.fireFileFinished("Some File Name"); assertTrue("Checker.fireFileFinished() doesn't call listener", aa2.wasCalled()); assertFalse("Checker.fireFileFinished() does call removed listener", auditAdapter.wasCalled()); aa2.resetListener(); final SortedSet<LocalizedMessage> messages = new TreeSet<>(); messages.add(new LocalizedMessage(0, 0, "a Bundle", "message.key", new Object[] {"arg"}, null, getClass(), null)); checker.fireErrors("Some File Name", messages); assertTrue("Checker.fireErrors() doesn't call listener", aa2.wasCalled()); assertFalse("Checker.fireErrors() does call removed listener", auditAdapter.wasCalled()); } @Test public void testAddBeforeExecutionFileFilter() throws Exception { final Checker checker = new Checker(); final TestBeforeExecutionFileFilter filter = new TestBeforeExecutionFileFilter(); checker.addBeforeExecutionFileFilter(filter); filter.resetFilter(); getAcceptFileStarted().invoke(checker, "Test.java"); assertTrue("Checker.acceptFileStarted() doesn't call filter", filter.wasCalled()); } @Test public void testRemoveBeforeExecutionFileFilter() throws Exception { final Checker checker = new Checker(); final TestBeforeExecutionFileFilter filter = new TestBeforeExecutionFileFilter(); final TestBeforeExecutionFileFilter f2 = new TestBeforeExecutionFileFilter(); checker.addBeforeExecutionFileFilter(filter); checker.addBeforeExecutionFileFilter(f2); checker.removeBeforeExecutionFileFilter(filter); f2.resetFilter(); getAcceptFileStarted().invoke(checker, "Test.java"); assertTrue("Checker.acceptFileStarted() doesn't call filter", f2.wasCalled()); assertFalse("Checker.acceptFileStarted() does call removed filter", filter.wasCalled()); } @Test public void testAddFilter() { final Checker checker = new Checker(); final DebugFilter filter = new DebugFilter(); checker.addFilter(filter); filter.resetFilter(); final SortedSet<LocalizedMessage> messages = new TreeSet<>(); messages.add(new LocalizedMessage(0, 0, "a Bundle", "message.key", new Object[] {"arg"}, null, getClass(), null)); checker.fireErrors("Some File Name", messages); assertTrue("Checker.fireErrors() doesn't call filter", filter.wasCalled()); } @Test public void testRemoveFilter() { final Checker checker = new Checker(); final DebugFilter filter = new DebugFilter(); final DebugFilter f2 = new DebugFilter(); checker.addFilter(filter); checker.addFilter(f2); checker.removeFilter(filter); f2.resetFilter(); final SortedSet<LocalizedMessage> messages = new TreeSet<>(); messages.add(new LocalizedMessage(0, 0, "a Bundle", "message.key", new Object[] {"arg"}, null, getClass(), null)); checker.fireErrors("Some File Name", messages); assertTrue("Checker.fireErrors() doesn't call filter", f2.wasCalled()); assertFalse("Checker.fireErrors() does call removed filter", filter.wasCalled()); } @Test public void testFileExtensions() throws Exception { final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration"); checkerConfig.addAttribute("charset", "UTF-8"); checkerConfig.addAttribute("cacheFile", temporaryFolder.newFile().getPath()); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); final DebugAuditAdapter auditAdapter = new DebugAuditAdapter(); checker.addListener(auditAdapter); final List<File> files = new ArrayList<>(); final File file = new File("file.pdf"); files.add(file); final File otherFile = new File("file.java"); files.add(otherFile); final String[] fileExtensions = {"java", "xml", "properties"}; checker.setFileExtensions(fileExtensions); checker.setCacheFile(temporaryFolder.newFile().getPath()); final int counter = checker.process(files); // comparing to 1 as there is only one legal file in input final int numLegalFiles = 1; assertEquals(numLegalFiles, counter); assertEquals(numLegalFiles, auditAdapter.getNumFilesStarted()); assertEquals(numLegalFiles, auditAdapter.getNumFilesFinished()); } @Test public void testIgnoredFileExtensions() throws Exception { final DefaultConfiguration checkerConfig = new DefaultConfiguration("configuration"); checkerConfig.addAttribute("charset", "UTF-8"); checkerConfig.addAttribute("cacheFile", temporaryFolder.newFile().getPath()); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); final DebugAuditAdapter auditAdapter = new DebugAuditAdapter(); checker.addListener(auditAdapter); final List<File> allIgnoredFiles = new ArrayList<>(); final File ignoredFile = new File("file.pdf"); allIgnoredFiles.add(ignoredFile); final String[] fileExtensions = {"java", "xml", "properties"}; checker.setFileExtensions(fileExtensions); checker.setCacheFile(temporaryFolder.newFile().getPath()); final int counter = checker.process(allIgnoredFiles); // comparing to 0 as there is no legal file in input final int numLegalFiles = 0; assertEquals(numLegalFiles, counter); assertEquals(numLegalFiles, auditAdapter.getNumFilesStarted()); assertEquals(numLegalFiles, auditAdapter.getNumFilesFinished()); } @SuppressWarnings("deprecation") @Test public void testSetters() { // all that is set by reflection, so just make code coverage be happy final Checker checker = new Checker(); checker.setClassLoader(getClass().getClassLoader()); checker.setClassloader(getClass().getClassLoader()); checker.setBasedir("some"); checker.setSeverity("ignore"); final PackageObjectFactory factory = new PackageObjectFactory( new HashSet<>(), Thread.currentThread().getContextClassLoader()); checker.setModuleFactory(factory); checker.setFileExtensions((String[]) null); checker.setFileExtensions(".java", "xml"); try { checker.setCharset("UNKNOWN-CHARSET"); fail("Exception is expected"); } catch (UnsupportedEncodingException ex) { assertEquals("unsupported charset: 'UNKNOWN-CHARSET'", ex.getMessage()); } } @Test public void testNoClassLoaderNoModuleFactory() { final Checker checker = new Checker(); try { checker.finishLocalSetup(); fail("Exception is expected"); } catch (CheckstyleException ex) { assertEquals("if no custom moduleFactory is set, " + "moduleClassLoader must be specified", ex.getMessage()); } } @Test public void testNoModuleFactory() throws Exception { final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.finishLocalSetup(); } @Test public void testFinishLocalSetupFullyInitialized() throws Exception { final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); final PackageObjectFactory factory = new PackageObjectFactory( new HashSet<>(), Thread.currentThread().getContextClassLoader()); checker.setModuleFactory(factory); checker.finishLocalSetup(); } @Test public void testSetupChildExceptions() { final Checker checker = new Checker(); final PackageObjectFactory factory = new PackageObjectFactory( new HashSet<>(), Thread.currentThread().getContextClassLoader()); checker.setModuleFactory(factory); final Configuration config = new DefaultConfiguration("java.lang.String"); try { checker.setupChild(config); fail("Exception is expected"); } catch (CheckstyleException ex) { assertEquals("java.lang.String is not allowed as a child in Checker", ex.getMessage()); } } @Test public void testSetupChildListener() throws Exception { final Checker checker = new Checker(); final PackageObjectFactory factory = new PackageObjectFactory( new HashSet<>(), Thread.currentThread().getContextClassLoader()); checker.setModuleFactory(factory); final Configuration config = new DefaultConfiguration( DebugAuditAdapter.class.getCanonicalName()); checker.setupChild(config); } @Test public void testDestroyCheckerWithWrongCacheFileNameLength() throws Exception { final Checker checker = new Checker(); final PackageObjectFactory factory = new PackageObjectFactory( new HashSet<>(), Thread.currentThread().getContextClassLoader()); checker.setModuleFactory(factory); checker.configure(new DefaultConfiguration("default config")); // We set wrong file name length in order to reproduce IOException on OS Linux, OS Windows. // The maximum file name length which is allowed in most UNIX, Windows file systems is 255. // See https://en.wikipedia.org/wiki/Filename; checker.setCacheFile(String.format(Locale.ENGLISH, "%0300d", 0)); try { checker.destroy(); fail("Exception did not happen"); } catch (IllegalStateException ex) { assertTrue(ex.getCause() instanceof IOException); } } /** * It is OK to have long test method name here as it describes the test purpose. * @noinspection InstanceMethodNamingConvention */ @Test public void testCacheAndCheckWhichDoesNotImplementExternalResourceHolderInterface() throws Exception { assertFalse(ExternalResourceHolder.class.isAssignableFrom(HiddenFieldCheck.class)); final DefaultConfiguration checkConfig = createCheckConfig(HiddenFieldCheck.class); final DefaultConfiguration treeWalkerConfig = createCheckConfig(TreeWalker.class); treeWalkerConfig.addChild(checkConfig); final DefaultConfiguration checkerConfig = new DefaultConfiguration("checkstyleConfig"); checkerConfig.addAttribute("charset", "UTF-8"); checkerConfig.addChild(treeWalkerConfig); final File cacheFile = temporaryFolder.newFile(); checkerConfig.addAttribute("cacheFile", cacheFile.getPath()); final Checker checker = new Checker(); final Locale locale = Locale.ROOT; checker.setLocaleCountry(locale.getCountry()); checker.setLocaleLanguage(locale.getLanguage()); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); checker.addListener(new BriefUtLogger(stream)); final File tmpFile = temporaryFolder.newFile("file.java"); final String[] expected = CommonUtils.EMPTY_STRING_ARRAY; verify(checker, tmpFile.getPath(), tmpFile.getPath(), expected); final Properties cacheAfterFirstRun = new Properties(); cacheAfterFirstRun.load(Files.newBufferedReader(cacheFile.toPath())); // one more time to reuse cache verify(checker, tmpFile.getPath(), tmpFile.getPath(), expected); final Properties cacheAfterSecondRun = new Properties(); cacheAfterSecondRun.load(Files.newBufferedReader(cacheFile.toPath())); assertEquals(cacheAfterFirstRun, cacheAfterSecondRun); } @Test public void testWithCacheWithNoViolation() throws Exception { final Checker checker = new Checker(); final PackageObjectFactory factory = new PackageObjectFactory( new HashSet<>(), Thread.currentThread().getContextClassLoader()); checker.setModuleFactory(factory); checker.configure(createCheckConfig(TranslationCheck.class)); final File cacheFile = temporaryFolder.newFile(); checker.setCacheFile(cacheFile.getPath()); checker.setupChild(createCheckConfig(TranslationCheck.class)); final File tmpFile = temporaryFolder.newFile("file.java"); final List<File> files = new ArrayList<>(1); files.add(tmpFile); checker.process(files); // invoke destroy to persist cache checker.destroy(); final Properties cache = new Properties(); cache.load(Files.newBufferedReader(cacheFile.toPath())); // There should 2 objects in cache: processed file (file.java) and checker configuration. final int expectedNumberOfObjectsInCache = 2; assertEquals(expectedNumberOfObjectsInCache, cache.size()); final String expectedConfigHash = "68EE3C3B4593FD8D86159C670C504542E20C6FA0"; assertEquals(expectedConfigHash, cache.getProperty(PropertyCacheFile.CONFIG_HASH_KEY)); assertNotNull(cache.getProperty(tmpFile.getPath())); } @Test public void testClearExistingCache() throws Exception { final DefaultConfiguration checkConfig = createCheckConfig(HiddenFieldCheck.class); final DefaultConfiguration treeWalkerConfig = createCheckConfig(TreeWalker.class); treeWalkerConfig.addChild(checkConfig); final DefaultConfiguration checkerConfig = new DefaultConfiguration("myConfig"); checkerConfig.addAttribute("charset", "UTF-8"); checkerConfig.addChild(treeWalkerConfig); final File cacheFile = temporaryFolder.newFile(); checkerConfig.addAttribute("cacheFile", cacheFile.getPath()); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); checker.addListener(new BriefUtLogger(stream)); checker.clearCache(); // invoke destroy to persist cache checker.destroy(); final Properties cacheAfterClear = new Properties(); cacheAfterClear.load(Files.newBufferedReader(cacheFile.toPath())); assertEquals(1, cacheAfterClear.size()); assertNotNull(cacheAfterClear.getProperty(PropertyCacheFile.CONFIG_HASH_KEY)); final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath(); final String[] expected = CommonUtils.EMPTY_STRING_ARRAY; // file that should be audited is not in cache verify(checker, pathToEmptyFile, pathToEmptyFile, expected); final Properties cacheAfterSecondRun = new Properties(); cacheAfterSecondRun.load(Files.newBufferedReader(cacheFile.toPath())); assertNotNull(cacheAfterSecondRun.getProperty(pathToEmptyFile)); assertEquals( cacheAfterClear.getProperty(PropertyCacheFile.CONFIG_HASH_KEY), cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY) ); final int expectedNumberOfObjectsInCacheAfterSecondRun = 2; assertEquals(expectedNumberOfObjectsInCacheAfterSecondRun, cacheAfterSecondRun.size()); } @Test public void testClearCacheWhenCacheFileIsNotSet() { // The idea of the test is to check that when cache file is not set, // the invokation of clearCache method does not throw an exception. final Checker checker = new Checker(); checker.clearCache(); } @Test public void testCatchErrorInProcessFilesMethod() throws Exception { // The idea of the test is to satisfy coverage rate. // An Error indicates serious problems that a reasonable application should not try to // catch, but due to issue https://github.com/checkstyle/checkstyle/issues/2285 // we catch errors in 'processFiles' method. Most such errors are abnormal conditions, // that is why we use PowerMockito to reproduce them. final File mock = PowerMockito.mock(File.class); // Assume that I/O error is happened when we try to invoke 'lastModified()' method. final String errorMessage = "Java Virtual Machine is broken" + " or has run out of resources necessary for it to continue operating."; final Error expectedError = new IOError(new InternalError(errorMessage)); when(mock.lastModified()).thenThrow(expectedError); final Checker checker = new Checker(); final List<File> filesToProcess = new ArrayList<>(); filesToProcess.add(mock); try { checker.process(filesToProcess); fail("IOError is expected!"); } // -@cs[IllegalCatchExtended] Testing for catch Error is part of 100% coverage. catch (Error error) { assertThat(error.getCause(), instanceOf(IOError.class)); assertThat(error.getCause().getCause(), instanceOf(InternalError.class)); assertEquals(errorMessage, error.getCause().getCause().getMessage()); } } /** * It is OK to have long test method name here as it describes the test purpose. * @noinspection InstanceMethodNamingConvention */ @Test public void testCacheAndFilterWhichDoesNotImplementExternalResourceHolderInterface() throws Exception { assertFalse(ExternalResourceHolder.class.isAssignableFrom(DummyFilter.class)); final DefaultConfiguration filterConfig = createCheckConfig(DummyFilter.class); final DefaultConfiguration checkerConfig = new DefaultConfiguration("checkstyle_checks"); checkerConfig.addChild(filterConfig); final File cacheFile = temporaryFolder.newFile(); checkerConfig.addAttribute("cacheFile", cacheFile.getPath()); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); checker.addListener(new BriefUtLogger(stream)); final String[] expected = CommonUtils.EMPTY_STRING_ARRAY; final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath(); verify(checker, pathToEmptyFile, expected); final Properties cacheAfterFirstRun = new Properties(); cacheAfterFirstRun.load(Files.newBufferedReader(cacheFile.toPath())); // One more time to use cache. verify(checker, pathToEmptyFile, expected); final Properties cacheAfterSecondRun = new Properties(); cacheAfterSecondRun.load(Files.newBufferedReader(cacheFile.toPath())); assertEquals( cacheAfterFirstRun.getProperty(pathToEmptyFile), cacheAfterSecondRun.getProperty(pathToEmptyFile) ); assertEquals( cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY), cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY) ); final int expectedNumberOfObjectsInCache = 2; assertEquals(expectedNumberOfObjectsInCache, cacheAfterFirstRun.size()); assertEquals(expectedNumberOfObjectsInCache, cacheAfterSecondRun.size()); } /** * It is OK to have long test method name here as it describes the test purpose. * @noinspection InstanceMethodNamingConvention */ @Test public void testCacheAndCheckWhichAddsNewResourceLocationButKeepsSameCheckerInstance() throws Exception { // Use case (https://github.com/checkstyle/checkstyle/pull/3092#issuecomment-218162436): // Imagine that cache exists in a file. New version of Checkstyle appear. // New release contains update to a some check to have additional external resource. // User update his configuration and run validation as usually. // Cache should not be reused. final DynamicalResourceHolderCheck check = new DynamicalResourceHolderCheck(); final String firstExternalResourceLocation = getPath("checks" + File.separator + "imports" + File.separator + "import-control_one.xml"); check.setFirstExternalResourceLocation(firstExternalResourceLocation); final DefaultConfiguration checkerConfig = new DefaultConfiguration("checkstyle_checks"); final File cacheFile = temporaryFolder.newFile(); checkerConfig.addAttribute("cacheFile", cacheFile.getPath()); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.addFileSetCheck(check); checker.configure(checkerConfig); checker.addListener(new BriefUtLogger(stream)); final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath(); final String[] expected = CommonUtils.EMPTY_STRING_ARRAY; verify(checker, pathToEmptyFile, expected); final Properties cacheAfterFirstRun = new Properties(); cacheAfterFirstRun.load(Files.newBufferedReader(cacheFile.toPath())); final int expectedNumberOfObjectsInCacheAfterFirstRun = 3; assertEquals(expectedNumberOfObjectsInCacheAfterFirstRun, cacheAfterFirstRun.size()); // Change a list of external resources which are used by the check final String secondExternalResourceLocation = "checks" + File.separator + "imports" + File.separator + "import-control_one-re.xml"; check.setSecondExternalResourceLocation(secondExternalResourceLocation); verify(checker, pathToEmptyFile, expected); final Properties cacheAfterSecondRun = new Properties(); cacheAfterSecondRun.load(Files.newBufferedReader(cacheFile.toPath())); assertEquals( cacheAfterFirstRun.getProperty(pathToEmptyFile), cacheAfterSecondRun.getProperty(pathToEmptyFile) ); assertEquals( cacheAfterFirstRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY), cacheAfterSecondRun.getProperty(PropertyCacheFile.CONFIG_HASH_KEY) ); assertEquals( cacheAfterFirstRun.getProperty(firstExternalResourceLocation), cacheAfterSecondRun.getProperty(firstExternalResourceLocation) ); final int expectedNumberOfObjectsInCacheAfterSecondRun = 4; assertEquals(expectedNumberOfObjectsInCacheAfterSecondRun, cacheAfterSecondRun.size()); assertNotNull(cacheAfterSecondRun.getProperty(secondExternalResourceLocation)); } @Test public void testClearLazyLoadCacheInDetailAST() throws Exception { final DefaultConfiguration checkConfig1 = createCheckConfig(CheckWhichDoesNotRequireCommentNodes.class); final DefaultConfiguration checkConfig2 = createCheckConfig(CheckWhichRequiresCommentNodes.class); final DefaultConfiguration treeWalkerConfig = createCheckConfig(TreeWalker.class); treeWalkerConfig.addChild(checkConfig1); treeWalkerConfig.addChild(checkConfig2); final DefaultConfiguration checkerConfig = new DefaultConfiguration("checkstyleConfig"); checkerConfig.addChild(treeWalkerConfig); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); checker.addListener(new BriefUtLogger(stream)); final String filePath = getPath("api/InputClearDetailAstLazyLoadCache.java"); final String[] expected = CommonUtils.EMPTY_STRING_ARRAY; verify(checker, filePath, filePath, expected); } @Test public void testCacheOnViolationSuppression() throws Exception { final File cacheFile = temporaryFolder.newFile(); final DefaultConfiguration violationCheck = createCheckConfig(DummyFileSetViolationCheck.class); final DefaultConfiguration defaultConfig = new DefaultConfiguration("defaultConfiguration"); defaultConfig.addAttribute("cacheFile", cacheFile.getPath()); defaultConfig.addChild(violationCheck); final DefaultConfiguration filterConfig = createCheckConfig(SuppressionFilter.class); filterConfig.addAttribute("file", getPath("suppress_all.xml")); defaultConfig.addChild(filterConfig); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.addListener(new BriefUtLogger(stream)); checker.configure(defaultConfig); final String fileViolationPath = temporaryFolder.newFile("ViolationFile.java").getPath(); final String[] expected = CommonUtils.EMPTY_STRING_ARRAY; verify(checker, fileViolationPath, expected); try (FileInputStream input = new FileInputStream(cacheFile)) { final Properties details = new Properties(); details.load(input); assertNotNull("suppressed violation file saved in cache", details.getProperty(fileViolationPath)); } } @Test public void testHaltOnExceptionOff() throws Exception { final DefaultConfiguration checkConfig = createCheckConfig(CheckWhichThrowsError.class); final DefaultConfiguration treeWalkerConfig = createCheckConfig(TreeWalker.class); treeWalkerConfig.addChild(checkConfig); final DefaultConfiguration checkerConfig = new DefaultConfiguration("checkstyleConfig"); checkerConfig.addChild(treeWalkerConfig); checkerConfig.addAttribute("haltOnException", "false"); final Checker checker = new Checker(); checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader()); checker.configure(checkerConfig); checker.addListener(new BriefUtLogger(stream)); final String filePath = getPath("InputMain.java"); final String[] expected = { "0: " + getCheckMessage(EXCEPTION_MSG, "java.lang.IndexOutOfBoundsException: test"), }; verify(checker, filePath, filePath, expected); } private static class DummyFilter implements Filter { @Override public boolean accept(AuditEvent event) { return false; } } private static class DummyFileSetViolationCheck extends AbstractFileSetCheck implements ExternalResourceHolder { @Override protected void processFiltered(File file, List<String> lines) throws CheckstyleException { log(0, "test"); } @Override public Set<String> getExternalResourceLocations() { final Set<String> externalResourceLocation = new HashSet<>(1); externalResourceLocation.add("non_existing_external_resource.xml"); return externalResourceLocation; } } private static class DynamicalResourceHolderCheck extends AbstractFileSetCheck implements ExternalResourceHolder { private String firstExternalResourceLocation; private String secondExternalResourceLocation; public void setFirstExternalResourceLocation(String firstExternalResourceLocation) { this.firstExternalResourceLocation = firstExternalResourceLocation; } public void setSecondExternalResourceLocation(String secondExternalResourceLocation) { this.secondExternalResourceLocation = secondExternalResourceLocation; } @Override protected void processFiltered(File file, List<String> lines) throws CheckstyleException { // there is no need in implementation of the method } @Override public Set<String> getExternalResourceLocations() { final Set<String> locations = new HashSet<>(); locations.add(firstExternalResourceLocation); // Attempt to change the behaviour of the check dynamically if (secondExternalResourceLocation != null) { locations.add(secondExternalResourceLocation); } return locations; } } private static class CheckWhichDoesNotRequireCommentNodes extends AbstractCheck { /** Number of children of method definition token. */ private static final int METHOD_DEF_CHILD_COUNT = 7; @Override public int[] getDefaultTokens() { return new int[] {TokenTypes.METHOD_DEF}; } @Override public int[] getAcceptableTokens() { return new int[] {TokenTypes.METHOD_DEF}; } @Override public int[] getRequiredTokens() { return new int[] {TokenTypes.METHOD_DEF}; } @Override public void visitToken(DetailAST ast) { if (ast.branchContains(TokenTypes.BLOCK_COMMENT_BEGIN)) { log(ast, "AST has incorrect structure structure." + " The check does not require comment nodes but there were comment nodes" + " in the AST."); } final int childCount = ast.getChildCount(); if (childCount != METHOD_DEF_CHILD_COUNT) { final String msg = String.format(Locale.getDefault(), "AST node in no comment tree has wrong number of children. " + "Expected is %d but was %d", METHOD_DEF_CHILD_COUNT, childCount); log(ast, msg); } // count children where comment lives int actualChildCount = 0; for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child = child.getNextSibling()) { actualChildCount++; } final int cacheChildCount = ast.getFirstChild().getChildCount(); if (cacheChildCount != actualChildCount) { final String msg = String.format(Locale.getDefault(), "AST node with no comment has wrong number of children. " + "Expected is %d but was %d", cacheChildCount, actualChildCount); log(ast, msg); } } } private static class CheckWhichRequiresCommentNodes extends AbstractCheck { /** Number of children of method definition token. */ private static final int METHOD_DEF_CHILD_COUNT = 7; @Override public boolean isCommentNodesRequired() { return true; } @Override public int[] getDefaultTokens() { return new int[] {TokenTypes.METHOD_DEF}; } @Override public int[] getAcceptableTokens() { return new int[] {TokenTypes.METHOD_DEF}; } @Override public int[] getRequiredTokens() { return new int[] {TokenTypes.METHOD_DEF}; } @Override public void visitToken(DetailAST ast) { if (!ast.branchContains(TokenTypes.BLOCK_COMMENT_BEGIN)) { log(ast, "Incorrect AST structure."); } final int childCount = ast.getChildCount(); if (childCount != METHOD_DEF_CHILD_COUNT) { final String msg = String.format(Locale.getDefault(), "AST node in comment tree has wrong number of children. " + "Expected is %d but was %d", METHOD_DEF_CHILD_COUNT, childCount); log(ast, msg); } // count children where comment lives int actualChildCount = 0; for (DetailAST child = ast.getFirstChild().getFirstChild(); child != null; child = child.getNextSibling()) { actualChildCount++; } final int cacheChildCount = ast.getFirstChild().getChildCount(); if (cacheChildCount != actualChildCount) { final String msg = String.format(Locale.getDefault(), "AST node with comment has wrong number of children. " + "Expected is %d but was %d", cacheChildCount, actualChildCount); log(ast, msg); } } } private static class CheckWhichThrowsError extends AbstractCheck { @Override public int[] getDefaultTokens() { return new int[] {TokenTypes.CLASS_DEF}; } @Override public int[] getAcceptableTokens() { return new int[] {TokenTypes.CLASS_DEF}; } @Override public int[] getRequiredTokens() { return new int[] {TokenTypes.CLASS_DEF}; } @Override public void visitToken(DetailAST ast) { throw new IndexOutOfBoundsException("test"); } } }