////////////////////////////////////////////////////////////////////////////////
// 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.filters;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.BDDMockito;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.google.common.io.Closeables;
import com.puppycrawl.tools.checkstyle.BaseCheckTestSupport;
import com.puppycrawl.tools.checkstyle.BriefUtLogger;
import com.puppycrawl.tools.checkstyle.Checker;
import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
import com.puppycrawl.tools.checkstyle.api.AuditEvent;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
import nl.jqno.equalsverifier.EqualsVerifier;
import nl.jqno.equalsverifier.Warning;
@RunWith(PowerMockRunner.class)
@PrepareForTest({SuppressionFilter.class, CommonUtils.class})
public class SuppressionFilterTest extends BaseCheckTestSupport {
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
@Override
protected String getPath(String filename) throws IOException {
return super.getPath("filters" + File.separator + filename);
}
@Test
public void testEqualsAndHashCode() {
EqualsVerifier
.forClass(SuppressionFilter.class)
.usingGetClass()
.withIgnoredFields("file", "optional", "configuration")
.suppress(Warning.NONFINAL_FIELDS)
.verify();
}
@Test
public void testAccept() throws CheckstyleException {
final String fileName = "src/test/resources/com/puppycrawl/tools/checkstyle/filters/"
+ "suppressions_none.xml";
final boolean optional = false;
final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
final AuditEvent ev = new AuditEvent(this, "ATest.java", null);
assertTrue(filter.accept(ev));
}
@Test
public void testAcceptOnNullFile() throws CheckstyleException {
final String fileName = null;
final boolean optional = false;
final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
final AuditEvent ev = new AuditEvent(this, "AnyJava.java", null);
assertTrue(filter.accept(ev));
}
@Test
public void testNonExistingSuppressionFileWithFalseOptional() {
final String fileName = "src/test/resources/com/puppycrawl/tools/checkstyle/filters/"
+ "non_existing_suppression_file.xml";
try {
final boolean optional = false;
createSuppressionFilter(fileName, optional);
fail("Exception is expected");
}
catch (CheckstyleException ex) {
assertEquals("Unable to find: " + fileName, ex.getMessage());
}
}
@Test
public void testExistingInvalidSuppressionFileWithTrueOptional() {
final String fileName = "src/test/resources/com/puppycrawl/tools/checkstyle/filters/"
+ "suppressions_invalid_file.xml";
try {
final boolean optional = true;
createSuppressionFilter(fileName, optional);
fail("Exception is expected");
}
catch (CheckstyleException ex) {
assertEquals("Unable to parse " + fileName + " - invalid files or checks format",
ex.getMessage());
}
}
@Test
public void testExistingSuppressionFileWithTrueOptional() throws Exception {
final String fileName = "src/test/resources/com/puppycrawl/tools/checkstyle/filters/"
+ "suppressions_none.xml";
final boolean optional = true;
final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
assertTrue(filter.accept(ev));
}
@Test
public void testExistingConfigWithTrueOptionalThrowsIoErrorWhileClosing()
throws Exception {
final InputStream inputStream = PowerMockito.mock(InputStream.class);
Mockito.doThrow(IOException.class).when(inputStream).close();
final URL url = PowerMockito.mock(URL.class);
BDDMockito.given(url.openStream()).willReturn(inputStream);
final URI uri = PowerMockito.mock(URI.class);
BDDMockito.given(uri.toURL()).willReturn(url);
PowerMockito.mockStatic(CommonUtils.class);
final String fileName = "src/test/resources/com/puppycrawl/tools/checkstyle/filters/"
+ "suppressions_none.xml";
BDDMockito.given(CommonUtils.getUriByFilename(fileName)).willReturn(uri);
final boolean optional = true;
final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
assertTrue(filter.accept(ev));
}
@Test
public void testNonExistingSuppressionFileWithTrueOptional() throws Exception {
final String fileName = "src/test/resources/com/puppycrawl/tools/checkstyle/filters/"
+ "non_existing_suppression_file.xml";
final boolean optional = true;
final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
assertTrue(filter.accept(ev));
}
@Test
public void testNonExistingSuppressionUrlWithTrueOptional() throws Exception {
final String fileName =
"http://checkstyle.sourceforge.net/non_existing_suppression.xml";
final boolean optional = true;
final SuppressionFilter filter = createSuppressionFilter(fileName, optional);
final AuditEvent ev = new AuditEvent(this, "AnyFile.java", null);
assertTrue(filter.accept(ev));
}
@Test
public void testLocalFileExternalResourceContentDoesNotChange() throws Exception {
final DefaultConfiguration filterConfig = createCheckConfig(SuppressionFilter.class);
filterConfig.addAttribute("file", getPath("suppressions_none.xml"));
final DefaultConfiguration checkerConfig = new DefaultConfiguration("checkstyle_checks");
checkerConfig.addChild(filterConfig);
final String cacheFile = temporaryFolder.newFile().getPath();
checkerConfig.addAttribute("cacheFile", cacheFile);
final Checker checker = new Checker();
checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
checker.addListener(new BriefUtLogger(stream));
checker.configure(checkerConfig);
final String filePath = temporaryFolder.newFile("file.java").getPath();
final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
verify(checker, filePath, expected);
// One more time to use cache.
verify(checker, filePath, expected);
}
@Test
public void testRemoteFileExternalResourceContentDoesNotChange() throws Exception {
final String[] urlCandidates = {
"http://checkstyle.sourceforge.net/files/suppressions_none.xml",
"https://raw.githubusercontent.com/checkstyle/checkstyle/master/src/site/resources/"
+ "files/suppressions_none.xml",
};
String urlForTest = null;
for (String url : urlCandidates) {
if (isConnectionAvailableAndStable(url)) {
urlForTest = url;
break;
}
}
// Run the test only if connection is available and url is reachable.
// We must use an if statement over junit's rule or assume because
// powermock disrupts the assume exception and turns into a failure
// instead of a skip when it doesn't pass
if (urlForTest != null) {
final DefaultConfiguration firstFilterConfig =
createCheckConfig(SuppressionFilter.class);
firstFilterConfig.addAttribute("file", urlForTest);
final DefaultConfiguration firstCheckerConfig =
new DefaultConfiguration("checkstyle_checks");
firstCheckerConfig.addChild(firstFilterConfig);
final String cacheFile = temporaryFolder.newFile().getPath();
firstCheckerConfig.addAttribute("cacheFile", cacheFile);
final Checker checker = new Checker();
checker.setModuleClassLoader(Thread.currentThread().getContextClassLoader());
checker.configure(firstCheckerConfig);
checker.addListener(new BriefUtLogger(stream));
final String pathToEmptyFile = temporaryFolder.newFile("file.java").getPath();
final String[] expected = CommonUtils.EMPTY_STRING_ARRAY;
verify(checker, pathToEmptyFile, expected);
// One more time to use cache.
final DefaultConfiguration secondFilterConfig =
createCheckConfig(SuppressionFilter.class);
secondFilterConfig.addAttribute("file", urlForTest);
final DefaultConfiguration secondCheckerConfig =
new DefaultConfiguration("checkstyle_checks");
secondCheckerConfig.addAttribute("cacheFile", cacheFile);
secondCheckerConfig.addChild(secondFilterConfig);
checker.configure(secondCheckerConfig);
verify(checker, pathToEmptyFile, expected);
}
}
private static boolean isConnectionAvailableAndStable(String url) throws Exception {
boolean available = false;
if (isUrlReachable(url)) {
final int attemptLimit = 5;
int attemptCount = 0;
while (attemptCount <= attemptLimit) {
InputStream stream = null;
try {
final URL address = new URL(url);
stream = address.openStream();
// Attempt to read a byte in order to check whether file content is available
available = stream.read() != -1;
break;
}
catch (IOException ex) {
// for some reason Travis CI failed some times (unstable) on reading the file
if (attemptCount < attemptLimit && ex.getMessage().contains("Unable to read")) {
attemptCount++;
available = false;
// wait for bad / disconnection time to pass
Thread.sleep(1000);
}
else {
Closeables.closeQuietly(stream);
throw ex;
}
}
finally {
Closeables.closeQuietly(stream);
}
}
}
return available;
}
private static boolean isUrlReachable(String url) {
boolean result = true;
try {
final URL verifiableUrl = new URL(url);
final HttpURLConnection urlConnect = (HttpURLConnection) verifiableUrl.openConnection();
urlConnect.getContent();
}
catch (IOException ex) {
result = false;
}
return result;
}
private static SuppressionFilter createSuppressionFilter(String fileName, boolean optional)
throws CheckstyleException {
final SuppressionFilter suppressionFilter = new SuppressionFilter();
suppressionFilter.setFile(fileName);
suppressionFilter.setOptional(optional);
suppressionFilter.finishLocalSetup();
return suppressionFilter;
}
}