package org.infernus.idea.checkstyle;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.testFramework.LightPlatformTestCase;
import org.infernus.idea.checkstyle.checker.CheckStyleChecker;
import org.infernus.idea.checkstyle.checker.ScannableFile;
import org.infernus.idea.checkstyle.csapi.CheckstyleActions;
import org.infernus.idea.checkstyle.csapi.TabWidthAndBaseDirProvider;
import org.infernus.idea.checkstyle.exception.CheckStylePluginException;
import org.infernus.idea.checkstyle.exception.CheckstyleVersionMixException;
import org.infernus.idea.checkstyle.model.ConfigurationLocation;
import org.infernus.idea.checkstyle.model.ScanScope;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import org.mockito.Mockito;
public class VersionMixExceptionTest
extends LightPlatformTestCase
{
private static final Project PROJECT = Mockito.mock(Project.class);
private static final String CONFIG_FILE = "config-ok.xml";
private static final String PROPS_FILE_NAME = "/checkstyle-idea.properties";
private static final String BASE_VERSION = readBaseVersion();
private static final String OTHER_VERSION = "6.19";
private CheckstyleProjectService csService;
@Override
protected void setUp() throws Exception {
super.setUp();
CheckStyleConfiguration mockPluginConfig = Mockito.mock(CheckStyleConfiguration.class);
Mockito.when(mockPluginConfig.getCheckstyleVersion(Mockito.anyString())).thenReturn(BASE_VERSION);
Mockito.when(mockPluginConfig.getThirdPartyClassPath()).thenReturn(null);
Mockito.when(mockPluginConfig.getProject()).thenReturn(PROJECT);
Mockito.when(mockPluginConfig.getScanScope()).thenReturn(ScanScope.AllSources);
CheckStyleConfiguration.activateMock4UnitTesting(mockPluginConfig);
csService = new CheckstyleProjectService(PROJECT);
csService.activateCheckstyleVersion(BASE_VERSION, null);
CheckstyleProjectService.activateMock4UnitTesting(csService);
}
@Override
protected void tearDown() throws Exception {
try {
CheckstyleProjectService.activateMock4UnitTesting(null);
CheckStyleConfiguration.activateMock4UnitTesting(null);
csService = null;
} finally {
super.tearDown();
}
}
/**
* Test that a {@link CheckstyleVersionMixException} is thrown when a
* {@link org.infernus.idea.checkstyle.csapi.CheckstyleInternalObject CheckstyleInternalObject} outlives its class
* loader.
*/
public void testVersionMixException() throws IOException, URISyntaxException {
Module module = Mockito.mock(Module.class);
Mockito.when(module.getProject()).thenReturn(PROJECT);
final CheckStyleChecker checker = createChecker(module);
runChecker(checker);
try {
Assert.assertNotEquals(OTHER_VERSION, BASE_VERSION);
csService.activateCheckstyleVersion(OTHER_VERSION, null); // changes class loader, cause of error
runChecker(checker);
Assert.fail("expected exception was not thrown");
} catch (CheckstyleVersionMixException e) {
// expected
final String internalClassName = "org.infernus.idea.checkstyle.service.entities.CheckerWithConfig";
Assert.assertTrue(e.getMessage().contains("Expected: " + internalClassName + ", actual: " +
internalClassName));
// Yes! Error, even though both class names are identical (but class loaders differ).
} finally {
csService.activateCheckstyleVersion(BASE_VERSION, null);
}
}
/**
* Test that everything works fine even when class loaders change, provided that
* {@link org.infernus.idea.checkstyle.csapi.CheckstyleInternalObject CheckstyleInternalObject}s associated with
* the expired class loaders are recreated.
*/
public void testSunnyDay() throws IOException, URISyntaxException {
Module module = Mockito.mock(Module.class);
Mockito.when(module.getProject()).thenReturn(PROJECT);
CheckStyleChecker checker = createChecker(module);
runChecker(checker);
try {
Assert.assertNotEquals(OTHER_VERSION, BASE_VERSION);
csService.activateCheckstyleVersion(OTHER_VERSION, null);
checker = createChecker(module);
runChecker(checker);
} finally {
csService.activateCheckstyleVersion(BASE_VERSION, null);
}
}
private CheckStyleChecker createChecker(@NotNull final Module pModule) throws URISyntaxException, IOException {
final ConfigurationLocation configLoc = new StringConfigurationLocation(readFile(CONFIG_FILE));
final TabWidthAndBaseDirProvider configurations = Mockito.mock(TabWidthAndBaseDirProvider.class);
Mockito.when(configurations.tabWidth()).thenReturn(2);
Mockito.when(configurations.baseDir()).thenReturn( //
Optional.of(new File(getClass().getResource(CONFIG_FILE).toURI()).getParent()));
final CheckStyleChecker checker = csService.getCheckstyleInstance().createChecker(pModule, configLoc,
Collections.emptyMap(), configurations, getClass().getClassLoader());
return checker;
}
private void runChecker(@NotNull final CheckStyleChecker pChecker) throws URISyntaxException {
final File sourceFile = new File(getClass().getResource("SourceFile.java").toURI());
final ScannableFile file1 = Mockito.mock(ScannableFile.class);
Mockito.when(file1.getFile()).thenReturn(sourceFile);
final List<ScannableFile> filesToScan = Collections.singletonList(file1);
final CheckstyleActions csInstance = csService.getCheckstyleInstance();
csInstance.scan(pChecker.getCheckerWithConfig4UnitTest(), filesToScan, false, 2, //
Optional.of(sourceFile.getParent()));
}
private String readFile(@NotNull final String pFilename) throws IOException, URISyntaxException {
URL url = getClass().getResource(pFilename);
if (url == null) {
url = Thread.currentThread().getContextClassLoader().getResource(pFilename);
}
Assert.assertNotNull(url);
return new String(Files.readAllBytes(Paths.get(url.toURI())), StandardCharsets.UTF_8);
}
@NotNull
private static String readBaseVersion() {
String result = null;
try (InputStream is = VersionMixExceptionTest.class.getResourceAsStream(PROPS_FILE_NAME)) {
Properties props = new Properties();
props.load(is);
result = props.getProperty("baseVersion");
} catch (IOException e) {
throw new CheckStylePluginException("internal error - Failed to read property file: " + PROPS_FILE_NAME, e);
}
Assert.assertNotNull(result);
return result;
}
}