// ===================================================================== // // Copyright (C) 2012 - 2016, Philip Graf // // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // which accompanies this distribution, and is available at // http://www.eclipse.org/legal/epl-v10.html // // ===================================================================== package ch.acanda.eclipse.pmd.java.resolution; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IMarker; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Position; import org.eclipse.text.edits.MalformedTreeException; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import ch.acanda.eclipse.pmd.java.resolution.QuickFixTestData.TestParameters; import ch.acanda.eclipse.pmd.marker.PMDMarker; import ch.acanda.eclipse.pmd.marker.WrappingPMDMarker; import ch.acanda.eclipse.pmd.ui.util.PMDPluginImages; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; /** * Base class for testing quick fix tests based on {@link ASTQuickFix}. An extending class must provide a static method * with the annotation {@link org.junit.runners.Parameterized.Parameters} that returns the parameters for the test case, * e.g: * * <pre> * @Parameters * public static Collection<Object[]> getTestData() { * return createTestData(ExtendsObjectQuickFixTest.class.getResourceAsStream("ExtendsObject.xml")); * } * </pre> * * The easiest way to implement this method is to use {@link QuickFixTestData#createTestData(InputStream)} and provide * an {@code InputStream} to an XML file containing all the test data. See {@link QuickFixTestData} for the format of * the XML file. * * See {@link ch.acanda.eclipse.pmd.java.resolution.basic.ExtendsObjectQuickFixTest ExtendsObjectQuickFixTest} for a * complete example. * * @author Philip Graf * @param <T> The type of the quick fix. */ @RunWith(value = Parameterized.class) @SuppressWarnings({ "PMD.CommentSize", "PMD.AbstractClassWithoutAbstractMethod" }) public abstract class TextEditQuickFixTestCase<T extends ASTRewriteQuickFix<? extends ASTNode>> { private final TestParameters params; public TextEditQuickFixTestCase(final TestParameters parameters) { params = parameters; } @SuppressWarnings("unchecked") private ASTRewriteQuickFix<ASTNode> getQuickFix() { try { final Type typeArgument = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; final Class<T> quickFixClass = (Class<T>) typeArgument; final IMarker marker = mock(IMarker.class); when(marker.getAttribute(eq("ruleName"), isA(String.class))).thenReturn(params.rulename.orNull()); final String markerText = params.source.substring(params.offset, params.offset + params.length); when(marker.getAttribute(eq("markerText"), isA(String.class))).thenReturn(markerText); return (ASTRewriteQuickFix<ASTNode>) quickFixClass.getConstructor(PMDMarker.class).newInstance(new WrappingPMDMarker(marker)); } catch (SecurityException | ReflectiveOperationException e) { throw new IllegalArgumentException(e); } } public static List<Object[]> createTestData(final InputStream testCase) { return Lists.transform(QuickFixTestData.createTestData(testCase), params -> new Object[] { params }); } @Test public void apply() throws MalformedTreeException, BadLocationException, JavaModelException { final ASTRewriteQuickFix<ASTNode> quickFix = getQuickFix(); final org.eclipse.jface.text.Document document = new org.eclipse.jface.text.Document(params.source); final CompilationUnit ast = createAST(document); final ASTNode node = findNode(params, ast, quickFix); final ASTRewrite rewrite = ASTRewrite.create(node.getAST()); final boolean isSuccessful = quickFix.rewrite(node, rewrite); assertTrue("The quick fix should be able to successfully rewrite", isSuccessful); rewrite.rewriteAST(document, getOptions()).apply(document); final String actual = document.get(); assertEquals("Result of applying the quick fix " + quickFix.getClass().getSimpleName() + " to the test " + params.name, params.expectedSource, actual); } private ASTNode findNode(final TestParameters params, final CompilationUnit ast, final ASTRewriteQuickFix<ASTNode> quickFix) { final Class<? extends ASTNode> nodeType = quickFix.getNodeType(); final NodeFinder<CompilationUnit, ASTNode> finder = quickFix.getNodeFinder(new Position(params.offset, params.length)); final Optional<ASTNode> node = finder.findNode(ast); assertTrue("Couldn't find node of type " + nodeType.getSimpleName() + "." + " Check the position of the marker in test " + params.name + ".", node.isPresent()); return node.get(); } private CompilationUnit createAST(final org.eclipse.jface.text.Document document) { final ASTParser astParser = ASTParser.newParser(AST.JLS4); astParser.setKind(ASTParser.K_COMPILATION_UNIT); astParser.setSource(document.get().toCharArray()); astParser.setCompilerOptions(ImmutableMap.<String, String>builder().put(JavaCore.COMPILER_SOURCE, "1.7").build()); final CompilationUnit ast = (CompilationUnit) astParser.createAST(null); ast.recordModifications(); return ast; } private Map<String, String> getOptions() { final Map<String, String> options = new HashMap<>(); options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE); options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4"); options.put(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_SWITCH, DefaultCodeFormatterConstants.TRUE); options.put(DefaultCodeFormatterConstants.FORMATTER_INDENT_SWITCHSTATEMENTS_COMPARE_TO_CASES, DefaultCodeFormatterConstants.TRUE); options.put(DefaultCodeFormatterConstants.FORMATTER_INDENT_BREAKS_COMPARE_TO_CASES, DefaultCodeFormatterConstants.TRUE); return options; } @Test public void getImage() throws IllegalAccessException, NoSuchFieldException, SecurityException { final ImageDescriptor imageDescriptor = getQuickFix().getImageDescriptor(); if (params.expectedImage.isPresent()) { final Field field = PMDPluginImages.class.getDeclaredField(params.expectedImage.get()); assertEquals("Quick fix image descriptor in test " + params.name, field.get(null), imageDescriptor); } else { assertNotNull("Quick fix image descriptor must not be null (test " + params.name + ")", imageDescriptor); } } @Test public void getLabel() { final String label = getQuickFix().getLabel(); if (params.expectedLabel.isPresent()) { assertEquals("Quick fix label in test " + params.name, params.expectedLabel.get(), label); } else { assertNotNull("Quick fix label must not be null (test " + params.name + ")", label); } } @Test public void getDescription() { final String description = getQuickFix().getDescription(); if (params.expectedDescription.isPresent()) { assertEquals("Quick fix description in test " + params.name, params.expectedDescription.get(), description); } else { assertNotNull("Quick fix description must not be null (test " + params.name + ")", description); } } }