package jetbrains.buildServer.tools.test;
import jetbrains.buildServer.tools.ErrorKind;
import jetbrains.buildServer.tools.ErrorReporting;
import jetbrains.buildServer.tools.ScanFile;
import jetbrains.buildServer.tools.checkers.StaticFieldsChecker;
import jetbrains.buildServer.tools.rules.StaticRuleSettings;
import jetbrains.buildServer.tools.util.ClassPathUtil;
import org.jetbrains.annotations.NotNull;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.api.Invocation;
import org.jmock.lib.action.CustomAction;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import testData.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
/**
* Created 15.05.13 14:23
*
* @author Eugene Petrenko (eugene.petrenko@jetbrains.com)
*/
public class StaticFieldsCheckerTest {
private Mockery m;
private StaticFieldsChecker myChecker;
private ErrorReporting myErrors;
private List<String> myLoggerErrors;
@BeforeMethod
public void setUp() {
m = new Mockery();
myErrors = m.mock(ErrorReporting.class);
myChecker = new StaticFieldsChecker(new StaticRuleSettings());
myLoggerErrors = new ArrayList<String>();
m.checking(new Expectations(){{
allowing(myErrors).postCheckError(with(any(ScanFile.class)), with(equal(ErrorKind.STATIC)), with(any(String.class)), with(any(String.class)));
will(new CustomAction("log") {
public Object invoke(Invocation invocation) throws Throwable {
String error = (String) invocation.getParameter(2);
myLoggerErrors.add(error);
System.out.println(error);
error = (String) invocation.getParameter(3);
myLoggerErrors.add(error);
System.out.println(error);
return null;
}
});
}});
}
@AfterMethod
public void tearDown() {
m.assertIsSatisfied();
}
@Test
public void test_report_static_final() throws IOException {
processClass(StaticFieldsTest_StaticFinal.class, "Class 'testData.StaticFieldsTest_StaticFinal' contains final static field 'LOG[1-4]' of type 'testData.StaticFieldsTest_StaticFinal$Logger'");
}
@Test
public void test_report_static() throws IOException {
processClass(StaticFieldsTest_Static.class, "Class 'testData.StaticFieldsTest_Static' contains non-final static field 'i?LOG[1-4]'");
}
@Test
public void test_report_static_nestedClasses() throws IOException {
processClass(StaticFieldsTest_NestedClasses.class,
"Class 'testData.StaticFieldsTest_NestedClasses$Foo' contains non-final static field 'foo'",
"Class 'testData.StaticFieldsTest_NestedClasses$N$M$Q' contains non-final static field '[nmq]2'",
"Class 'testData.StaticFieldsTest_NestedClasses$N$M$Q' contains final static field '[nmq]1' of type 'testData.StaticFieldsTest_NestedClasses$N($M($Q)?)?'"
);
}
@Test
public void test_report_static_no_issues() throws IOException {
processClass(StaticFieldsTest_NotAnIssues.class);
}
@Test
public void test_report_static_enums() throws IOException {
processClass(StaticFieldsTest_Enum.class);
}
@Test
public void test_report_static_interface() throws IOException {
processClass(StaticFieldsTest_Interface.class, "Class 'testData.StaticFieldsTest_Interface' contains final static field 'singleton' of type 'testData.StaticFieldsTest_Interface'");
}
@Test
public void test_report_static_allowed_primitive_types() throws IOException {
processClass(StaticFieldsTest_Allowed_StaticFinal.class,
"Class contains 'testData.StaticFieldsTest_Allowed_StaticFinal.xi[1-9]' static final field");
}
private void processClass(@NotNull final Class<?> clazz, @NotNull String... errors) throws IOException {
final File home = ClassPathUtil.getClassFile(clazz);
Assert.assertNotNull(home);
final File parentFile = home.getParentFile();
Assert.assertNotNull(parentFile);
System.out.println("Class home: " + parentFile);
final File[] allFiles = parentFile.listFiles(new FilenameFilter() {
public boolean accept(@NotNull File dir, @NotNull String name) {
return name.equals(clazz.getSimpleName() + ".class") || name.startsWith(clazz.getSimpleName() + "$");
}
});
Assert.assertNotNull(allFiles);
Arrays.sort(allFiles);
for (File cf : allFiles) {
System.out.println("Checking: " + cf);
myChecker.process(file(cf), myErrors);
}
System.out.flush();
assertErrors(errors);
}
private void assertErrors(@NotNull String... errors) {
List<String> all = new ArrayList<String>(myLoggerErrors);
for (Iterator<String> it = all.iterator(); it.hasNext(); ) {
if (it.next().startsWith("Static usage of ")) it.remove();
}
for (String error : errors) {
Pattern pt = Pattern.compile(error.replace("$", "\\$").replace(".", "\\."));
for (Iterator<String> iterator = all.iterator(); iterator.hasNext(); ) {
if (pt.matcher(iterator.next()).matches()) iterator.remove();
}
}
final StringBuilder sb = new StringBuilder();
for (String s : all) {
sb.append(s).append("\r\n");
}
Assert.assertTrue(all.isEmpty(), sb.toString());
}
@NotNull
private ScanFile file(@NotNull final File clazz) throws IOException {
final ScanFile sf = m.mock(ScanFile.class, clazz.getPath());
m.checking(new Expectations(){{
allowing(sf).getName(); will(returnValue(clazz.getPath()));
allowing(sf).openStream(); will(new CustomAction("create stream") {
public Object invoke(Invocation invocation) throws Throwable {
return new FileInputStream(clazz);
}
});
}});
return sf;
}
}