blob: d391e64ea0cac6755b1eba4f52a943f87bc46fd8 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
// 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 org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.times;
import static org.powermock.api.mockito.PowerMockito.doNothing;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;
import static org.powermock.api.mockito.PowerMockito.when;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.google.common.io.BaseEncoding;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import com.google.common.io.Flushables;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
@RunWith(PowerMockRunner.class)
@PrepareForTest({ PropertyCacheFile.class, ByteStreams.class,
CommonUtils.class, Closeables.class, Flushables.class})
public class PropertyCacheFileTest extends AbstractPathTestSupport {
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
@Override
protected String getPackageLocation() {
return "com/puppycrawl/tools/checkstyle/propertycachefile";
}
@Test
public void testCtor() {
try {
final Object test = new PropertyCacheFile(null, "");
fail("exception expected but got " + test);
}
catch (IllegalArgumentException ex) {
assertEquals("Invalid exception message",
"config can not be null", ex.getMessage());
}
try {
final Configuration config = new DefaultConfiguration("myName");
final Object test = new PropertyCacheFile(config, null);
fail("exception expected but got " + test);
}
catch (IllegalArgumentException ex) {
assertEquals("Invalid exception message",
"fileName can not be null", ex.getMessage());
}
}
@Test
public void testInCache() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = temporaryFolder.newFile().getPath();
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
cache.put("myFile", 1);
assertTrue("Should return true wnen file is in cache",
cache.isInCache("myFile", 1));
assertFalse("Should return false wnen file is not in cache",
cache.isInCache("myFile", 2));
assertFalse("Should return false wnen file is not in cache",
cache.isInCache("myFile1", 1));
}
@Test
public void testResetIfFileDoesNotExist() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final PropertyCacheFile cache = new PropertyCacheFile(config, "fileDoesNotExist.txt");
cache.load();
assertNotNull("Config hash key should not be null",
cache.get(PropertyCacheFile.CONFIG_HASH_KEY));
}
@Test
public void testCloseAndFlushOutputStreamAfterCreatingHashCode() throws IOException {
mockStatic(Closeables.class);
doNothing().when(Closeables.class);
Closeables.close(any(ObjectOutputStream.class), Matchers.eq(false));
mockStatic(Flushables.class);
doNothing().when(Flushables.class);
Flushables.flush(any(ObjectOutputStream.class), Matchers.eq(false));
final Configuration config = new DefaultConfiguration("myName");
final PropertyCacheFile cache = new PropertyCacheFile(config, "fileDoesNotExist.txt");
cache.load();
verifyStatic(times(1));
Closeables.close(any(ObjectOutputStream.class), Matchers.eq(false));
verifyStatic(times(1));
Flushables.flush(any(ObjectOutputStream.class), Matchers.eq(false));
}
@Test
public void testPopulateDetails() throws IOException {
mockStatic(Closeables.class);
doNothing().when(Closeables.class);
Closeables.closeQuietly(any(FileInputStream.class));
final Configuration config = new DefaultConfiguration("myName");
final PropertyCacheFile cache = new PropertyCacheFile(config,
getPath("InputPropertyCacheFile"));
cache.load();
final String hash = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertNotNull("Config hash key should not be null", hash);
assertNull("Should return null if key is not in cache", cache.get("key"));
cache.load();
assertEquals("Invalid config hash key", hash, cache.get(PropertyCacheFile.CONFIG_HASH_KEY));
assertEquals("Invalid cache value", "value", cache.get("key"));
assertNotNull("Config hash key should not be null",
cache.get(PropertyCacheFile.CONFIG_HASH_KEY));
verifyStatic(times(2));
Closeables.closeQuietly(any(FileInputStream.class));
}
@Test
public void testConfigHashOnReset() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = temporaryFolder.newFile().getPath();
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
cache.load();
final String hash = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertNotNull("Config hash key should not be null", hash);
cache.reset();
assertEquals("Invalid config hash key",
hash, cache.get(PropertyCacheFile.CONFIG_HASH_KEY));
}
@Test
public void testConfigHashRemainsOnResetExternalResources() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = temporaryFolder.newFile().getPath();
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
// create cache with one file
cache.load();
cache.put("myFile", 1);
final String hash = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertNotNull("Config hash key should not be null", hash);
// apply new external resource to clear cache
final Set<String> resources = new HashSet<>();
resources.add("dummy");
cache.putExternalResources(resources);
assertEquals("Invalid config hash key",
hash, cache.get(PropertyCacheFile.CONFIG_HASH_KEY));
assertFalse("Should return false in file is not in cache",
cache.isInCache("myFile", 1));
}
@Test
public void testExternalResourseIsSavedInCache() throws Exception {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = temporaryFolder.newFile().getPath();
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
cache.load();
final Set<String> resources = new HashSet<>();
final String pathToResource = getPath("InputPropertyCacheFileExternal.properties");
resources.add(pathToResource);
cache.putExternalResources(resources);
final MessageDigest digest = MessageDigest.getInstance("SHA-1");
final URI uri = CommonUtils.getUriByFilename(pathToResource);
final byte[] input =
ByteStreams.toByteArray(new BufferedInputStream(uri.toURL().openStream()));
final ByteArrayOutputStream out = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
oos.writeObject(input);
}
digest.update(out.toByteArray());
final String expected = BaseEncoding.base16().upperCase().encode(digest.digest());
assertEquals("Hashes are not equal", expected,
cache.get("module-resource*?:" + pathToResource));
}
/**
* This SuppressWarning("unchecked") required to suppress
* "Unchecked generics array creation for varargs parameter" during mock
* @throws IOException when smth wrong with file creation or cache.load
*/
@SuppressWarnings("unchecked")
@Test
public void testNonExistingResource() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = temporaryFolder.newFile().getPath();
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
// create cache with one file
cache.load();
final String myFile = "myFile";
cache.put(myFile, 1);
final String hash = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertNotNull("Config hash key should not be null", hash);
mockStatic(ByteStreams.class);
when(ByteStreams.toByteArray(any(BufferedInputStream.class)))
.thenThrow(IOException.class);
// apply new external resource to clear cache
final Set<String> resources = new HashSet<>();
final String resource = getPath("InputPropertyCacheFile.header");
resources.add(resource);
cache.putExternalResources(resources);
assertFalse("Should return false in file is not in cache",
cache.isInCache(myFile, 1));
assertFalse("Should return false in file is not in cache",
cache.isInCache(resource, 1));
}
@Test
public void testFlushAndCloseCacheFileOutputStream() throws IOException {
mockStatic(Closeables.class);
doNothing().when(Closeables.class);
Closeables.close(any(FileOutputStream.class), Matchers.eq(false));
mockStatic(Flushables.class);
doNothing().when(Flushables.class);
Flushables.flush(any(FileOutputStream.class), Matchers.eq(false));
final Configuration config = new DefaultConfiguration("myName");
final PropertyCacheFile cache = new PropertyCacheFile(config,
temporaryFolder.newFile().getPath());
cache.put("CheckedFileName.java", System.currentTimeMillis());
cache.persist();
verifyStatic(times(1));
Closeables.close(any(FileOutputStream.class), Matchers.eq(false));
verifyStatic(times(1));
Flushables.flush(any(FileOutputStream.class), Matchers.eq(false));
}
@Test
public void testCacheDirectoryDoesNotExistAndShouldBeCreated() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = String.format(Locale.getDefault(), "%s%2$stemp%2$scache.temp",
temporaryFolder.getRoot(), File.separator);
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
// no exception expected, cache directory should be created
cache.persist();
assertTrue("cache exists in directory", new File(filePath).exists());
}
@Test
public void testPathToCacheContainsOnlyFileName() throws IOException {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = "temp.cache";
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
// no exception expected
cache.persist();
assertTrue("Cache file does not exist", Files.exists(Paths.get(filePath)));
Files.delete(Paths.get(filePath));
}
@Test
@SuppressWarnings("unchecked")
public void testExceptionNoSuchAlgorithmException() throws Exception {
final Configuration config = new DefaultConfiguration("myName");
final String filePath = temporaryFolder.newFile().getPath();
final PropertyCacheFile cache = new PropertyCacheFile(config, filePath);
cache.put("myFile", 1);
mockStatic(MessageDigest.class);
when(MessageDigest.getInstance("SHA-1"))
.thenThrow(NoSuchAlgorithmException.class);
final Class<?>[] param = new Class<?>[1];
param[0] = Serializable.class;
final Method method =
PropertyCacheFile.class.getDeclaredMethod("getHashCodeBasedOnObjectContent", param);
method.setAccessible(true);
try {
method.invoke(cache, config);
fail("InvocationTargetException is expected");
}
catch (InvocationTargetException ex) {
assertTrue("Invalid exception cause",
ex.getCause().getCause() instanceof NoSuchAlgorithmException);
assertEquals("Invalid exception message",
"Unable to calculate hashcode.", ex.getCause().getMessage());
}
}
@Test
public void testPutNonExsistingExternalResourceSameExceptionBetweenRuns() throws Exception {
final File cacheFile = temporaryFolder.newFile();
// We mock getUriByFilename method of CommonUtils to garantee that it will
// throw CheckstyleException with the specific content.
mockStatic(CommonUtils.class);
final CheckstyleException mockException =
new CheckstyleException("Cannot get URL for cache file " + cacheFile.getAbsolutePath());
when(CommonUtils.getUriByFilename(any(String.class)))
.thenThrow(mockException);
// We invoke 'putExternalResources' twice to invalidate cache
// and have two identical exceptions whith the equal content
final int numberOfRuns = 2;
final String[] configHashes = new String[numberOfRuns];
final String[] externalResourceHashes = new String[numberOfRuns];
for (int i = 0; i < numberOfRuns; i++) {
final Configuration config = new DefaultConfiguration("myConfig");
final PropertyCacheFile cache = new PropertyCacheFile(config, cacheFile.getPath());
cache.load();
configHashes[i] = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertNotNull("Config hash key should not be null", configHashes[i]);
final Set<String> nonExistingExternalResources = new HashSet<>();
final String externalResourceFileName = "non_existing_file.xml";
nonExistingExternalResources.add(externalResourceFileName);
cache.putExternalResources(nonExistingExternalResources);
externalResourceHashes[i] = cache.get(PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
+ externalResourceFileName);
assertNotNull("External resource hashes should not be null",
externalResourceHashes[i]);
cache.persist();
final Properties cacheDetails = new Properties();
cacheDetails.load(Files.newBufferedReader(cacheFile.toPath()));
final int expectedNumberOfObjectsInCacheFile = 2;
assertEquals("Unexpected number of objects in cache",
expectedNumberOfObjectsInCacheFile, cacheDetails.size());
}
assertEquals("Invalid config hash", configHashes[0], configHashes[1]);
assertEquals("Invalid external resource hashes",
externalResourceHashes[0], externalResourceHashes[1]);
}
/**
* It is OK to have long test method name here as it describes the test purpose.
* @noinspection InstanceMethodNamingConvention
*/
@Test
public void testPutNonExsistingExternalResourceDifferentExceptionsBetweenRuns()
throws Exception {
final File cacheFile = temporaryFolder.newFile();
// We invoke 'putExternalResources' twice to invalidate cache
// and have two different exceptions with different content.
final int numberOfRuns = 2;
final String[] configHashes = new String[numberOfRuns];
final String[] externalResourceHashes = new String[numberOfRuns];
for (int i = 0; i < numberOfRuns; i++) {
final Configuration config = new DefaultConfiguration("myConfig");
final PropertyCacheFile cache = new PropertyCacheFile(config, cacheFile.getPath());
// We mock getUriByFilename method of CommonUtils to garantee that it will
// throw CheckstyleException with the specific content.
mockStatic(CommonUtils.class);
final CheckstyleException mockException = new CheckstyleException("Exception #" + i);
when(CommonUtils.getUriByFilename(any(String.class)))
.thenThrow(mockException);
cache.load();
configHashes[i] = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertNotNull("Config hash key should not be null", configHashes[i]);
final Set<String> nonExistingExternalResources = new HashSet<>();
final String externalResourceFileName = "non_existing_file.xml";
nonExistingExternalResources.add(externalResourceFileName);
cache.putExternalResources(nonExistingExternalResources);
externalResourceHashes[i] = cache.get(PropertyCacheFile.EXTERNAL_RESOURCE_KEY_PREFIX
+ externalResourceFileName);
assertNotNull("External resource hashes should not be null",
externalResourceHashes[i]);
cache.persist();
final Properties cacheDetails = new Properties();
cacheDetails.load(Files.newBufferedReader(cacheFile.toPath()));
final int expectedNumberOfObjectsInCacheFile = 2;
assertEquals("Unexpected number of objects in cache",
expectedNumberOfObjectsInCacheFile, cacheDetails.size());
}
assertEquals("Invalid config hash", configHashes[0], configHashes[1]);
assertNotEquals("Invalid external resource hashes",
externalResourceHashes[0], externalResourceHashes[1]);
}
@Test
public void testChangeInConfig() throws Exception {
final DefaultConfiguration config = new DefaultConfiguration("myConfig");
config.addAttribute("attr", "value");
final File cacheFile = temporaryFolder.newFile();
final PropertyCacheFile cache = new PropertyCacheFile(config, cacheFile.getPath());
cache.load();
final String expectedInitialConfigHash = "91753B970AFDF9F5F3DFA0D258064841949D3C6B";
final String actualInitialConfigHash = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertEquals("Invalid config hash", expectedInitialConfigHash, actualInitialConfigHash);
cache.persist();
final Properties details = new Properties();
details.load(Files.newBufferedReader(cacheFile.toPath()));
assertEquals("Invalid details size", 1, details.size());
// change in config
config.addAttribute("newAttr", "newValue");
final PropertyCacheFile cacheAfterChangeInConfig =
new PropertyCacheFile(config, cacheFile.getPath());
cacheAfterChangeInConfig.load();
final String expectedConfigHashAfterChange = "4CF5EC78955B81D76153ACC2CA6D60CB77FDCB2A";
final String actualConfigHashAfterChange =
cacheAfterChangeInConfig.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertEquals("Invalid config hash",
expectedConfigHashAfterChange, actualConfigHashAfterChange);
cacheAfterChangeInConfig.persist();
final Properties detailsAfterChangeInConfig = new Properties();
detailsAfterChangeInConfig.load(Files.newBufferedReader(cacheFile.toPath()));
assertEquals("Invalid cache size", 1, detailsAfterChangeInConfig.size());
}
}