////////////////////////////////////////////////////////////////////////////////
// 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.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import com.google.common.io.ByteStreams;
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 })
public class PropertyCacheFileTest {
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void testCtor() {
try {
new PropertyCacheFile(null, "");
}
catch (IllegalArgumentException ex) {
assertEquals("config can not be null", ex.getMessage());
}
try {
final Configuration config = new DefaultConfiguration("myName");
new PropertyCacheFile(config, null);
}
catch (IllegalArgumentException ex) {
assertEquals("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(cache.isInCache("myFile", 1));
assertFalse(cache.isInCache("myFile", 2));
assertFalse(cache.isInCache("myFile1", 1));
}
@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(hash);
cache.reset();
assertEquals(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(hash);
// apply new external resource to clear cache
final Set<String> resources = new HashSet<>();
resources.add("dummy");
cache.putExternalResources(resources);
assertEquals(hash, cache.get(PropertyCacheFile.CONFIG_HASH_KEY));
assertFalse(cache.isInCache("myFile", 1));
}
/**
* 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(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 = "/com/puppycrawl/tools/checkstyle/java.header";
resources.add(resource);
cache.putExternalResources(resources);
assertFalse(cache.isInCache(myFile, 1));
assertFalse(cache.isInCache(resource, 1));
}
@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();
if (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(ex.getCause().getCause() instanceof NoSuchAlgorithmException);
assertEquals("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(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(externalResourceFileName);
assertNotNull(externalResourceHashes[i]);
cache.persist();
final Properties cacheDetails = new Properties();
cacheDetails.load(Files.newBufferedReader(cacheFile.toPath()));
final int expectedNumberOfObjectsInCacheFile = 2;
assertEquals(expectedNumberOfObjectsInCacheFile, cacheDetails.size());
}
assertEquals(configHashes[0], configHashes[1]);
assertEquals(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(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(externalResourceFileName);
assertNotNull(externalResourceHashes[i]);
cache.persist();
final Properties cacheDetails = new Properties();
cacheDetails.load(Files.newBufferedReader(cacheFile.toPath()));
final int expectedNumberOfObjectsInCacheFile = 2;
assertEquals(expectedNumberOfObjectsInCacheFile, cacheDetails.size());
}
assertEquals(configHashes[0], configHashes[1]);
assertNotEquals(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 = "EEF15651C2D79B29968835FC729E788938CAFE3B";
final String actualInitialConfigHash = cache.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertEquals(expectedInitialConfigHash, actualInitialConfigHash);
cache.persist();
final Properties details = new Properties();
details.load(Files.newBufferedReader(cacheFile.toPath()));
assertEquals(1, details.size());
// change in config
config.addAttribute("newAttr", "newValue");
final PropertyCacheFile cacheAfterChangeInConfig =
new PropertyCacheFile(config, cacheFile.getPath());
cacheAfterChangeInConfig.load();
final String expectedConfigHashAfterChange = "0FFFF89F6636EE8AEB904681F594B0F05E1FF795";
final String actualConfigHashAfterChange =
cacheAfterChangeInConfig.get(PropertyCacheFile.CONFIG_HASH_KEY);
assertEquals(expectedConfigHashAfterChange, actualConfigHashAfterChange);
cacheAfterChangeInConfig.persist();
final Properties detailsAfterChangeInConfig = new Properties();
detailsAfterChangeInConfig.load(Files.newBufferedReader(cacheFile.toPath()));
assertEquals(1, detailsAfterChangeInConfig.size());
}
}