blob: 96c3bbb2109f9588b9f160a76508e4654f4c1de2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.pde.api.tools.util.tests;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.pde.api.tools.internal.ApiDescriptionProcessor;
import org.eclipse.pde.api.tools.internal.util.Signatures;
import org.eclipse.pde.api.tools.model.tests.TestSuiteHelper;
import org.eclipse.pde.api.tools.tests.AbstractApiTest;
import org.eclipse.pde.api.tools.tests.util.FileUtils;
import org.eclipse.pde.api.tools.tests.util.ProjectUtils;
import org.eclipse.pde.api.tools.ui.internal.ApiUIPlugin;
import org.eclipse.pde.api.tools.ui.internal.wizards.ApiToolingSetupRefactoring;
import org.eclipse.pde.api.tools.ui.internal.wizards.WizardMessages;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* This class tests the {@link ApiDescriptionProcessor}
*
* @since 1.0.0
*/
public class ApiDescriptionProcessorTests extends AbstractApiTest {
/**
* The source directory for the javadoc updating test source
*/
private static String JAVADOC_SRC_DIR = null;
static {
JAVADOC_SRC_DIR = TestSuiteHelper.getPluginDirectoryPath().append("test-source").append("javadoc").toOSString(); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Visitor used to inspect the 'after' class files once they have had tags
* added to ensure the tags as specified in the component.xml file were
* added.
*/
class ChangeVisitor extends ASTVisitor {
String type, membername, signature, innertypename;
String[] expectedtags = null;
boolean processed = false;
public ChangeVisitor(String type, String innertypename, String membername, String signature, String[] expectedtags) {
this.type = type;
this.membername = membername;
this.signature = signature;
this.expectedtags = expectedtags;
this.innertypename = innertypename;
}
@Override
public boolean visit(FieldDeclaration node) {
if (signature != null) {
// means we are looking for a method
return false;
}
List<VariableDeclarationFragment> fields = node.fragments();
VariableDeclarationFragment fragment = null;
for (Iterator<VariableDeclarationFragment> iter = fields.iterator(); iter.hasNext();) {
fragment = iter.next();
if (fragment.getName().getFullyQualifiedName().equals(membername)) {
Javadoc docnode = node.getJavadoc();
assertNotNull("the field: " + membername + " must have a javadoc node", docnode); //$NON-NLS-1$ //$NON-NLS-2$
assertTrue("the field: " + membername + " should contain all of the tags: " + getStringValue(expectedtags), containsAllTags(docnode.tags())); //$NON-NLS-1$ //$NON-NLS-2$
processed = true;
}
}
return false;
}
@Override
public boolean visit(MethodDeclaration node) {
if (node.getName().getFullyQualifiedName().equals(membername)) {
String sig = Signatures.getMethodSignatureFromNode(node, true);
if (signature.equals(sig)) {
Javadoc docnode = node.getJavadoc();
assertNotNull("the method: " + membername + " [" + signature + "] must have a javadoc node", docnode); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
assertTrue("the method: " + membername + " [" + signature + "] should contain all of the tags: " + getStringValue(expectedtags), containsAllTags(docnode.tags())); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
processed = true;
}
}
return false;
}
@Override
public boolean visit(TypeDeclaration node) {
if (membername == null && signature == null) {
String name = node.getName().getFullyQualifiedName();
if ((innertypename == null && name.equals(type)) || name.equals(innertypename)) {
Javadoc docnode = node.getJavadoc();
assertNotNull("the type: " + name + " must have a javadoc node", docnode); //$NON-NLS-1$ //$NON-NLS-2$
assertTrue("the type: " + name + " should contain all of the tags: " + getStringValue(expectedtags), containsAllTags(docnode.tags())); //$NON-NLS-1$ //$NON-NLS-2$
processed = true;
}
}
return membername != null || innertypename != null;
}
/**
* Determines if the listing of doc tags from the declaration contains
* all of the expected tags
*
* @param tags
* @return true if the tag list contains all of the expected tags, false
* otherwise
*/
private boolean containsAllTags(List<TagElement> tags) {
boolean allfound = true;
TagElement element = null;
for (String expectedtag : expectedtags) {
boolean contained = false;
for (TagElement tag : tags) {
element = tag;
if (expectedtag.equals(element.getTagName())) {
contained = true;
}
}
allfound &= contained;
}
return allfound;
}
private String getStringValue(String[] tags) {
StringBuilder buffer = new StringBuilder();
for (int i = 0, max = tags.length; i < max; i++) {
if (i > 0) {
buffer.append(',');
}
buffer.append(tags[i]);
}
return String.valueOf(buffer);
}
}
private static IPath ROOT_PATH = TestSuiteHelper.getPluginDirectoryPath().append("test-source").append("javadoc"); //$NON-NLS-1$ //$NON-NLS-2$
private static File componentxml = new File(ROOT_PATH.append("component.xml").toOSString()); //$NON-NLS-1$
@Before
public void setUp() throws Exception {
createProject(TESTING_PROJECT_NAME, null);
IJavaProject project = getTestingJavaProject(TESTING_PROJECT_NAME);
assertNotNull("The java project must have been created", project); //$NON-NLS-1$
IPackageFragmentRoot srcroot = ProjectUtils.addSourceContainer(project, ProjectUtils.SRC_FOLDER);
assertNotNull("the src root must have been created", srcroot); //$NON-NLS-1$
File src = new File(JAVADOC_SRC_DIR);
assertTrue("the source dir must exist", src.exists()); //$NON-NLS-1$
assertTrue("the source dir must be a directory", src.isDirectory()); //$NON-NLS-1$
assertNotNull("the srcroot for the test java project must not be null", srcroot); //$NON-NLS-1$
FileUtils.importFilesFromDirectory(src, srcroot.getPath().append("javadoc"), new NullProgressMonitor()); //$NON-NLS-1$
ApiToolingSetupRefactoring refactoring = new ApiToolingSetupRefactoring();
CompositeChange change = new CompositeChange("Test"); //$NON-NLS-1$
createTagChanges(change, project, componentxml);
refactoring.addChange(change);
performRefactoring(refactoring);
}
@After
public void tearDown() throws Exception {
deleteProject(TESTING_PROJECT_NAME);
}
/**
* Tests that the component.xml file is parsed if it is provided in one of
* the doc'd three forms:
* <ol>
* <li>a directory containing the component.xml file</li>
* <li>a component.xml file</li>
* <li>a jar file containing the component.xml file</li>
* </ol>
*/
@Test
public void testSerializeComponentXml() {
String xml = ApiDescriptionProcessor.serializeComponentXml(new File(ROOT_PATH.toOSString()));
assertNotNull("The component xml file must exist and be parsable from a root directory", xml); //$NON-NLS-1$
xml = ApiDescriptionProcessor.serializeComponentXml(componentxml);
assertNotNull("The component xml file must exist and be parsable from a component.xml file", xml); //$NON-NLS-1$
xml = ApiDescriptionProcessor.serializeComponentXml(new File(ROOT_PATH.append("component.jar").toOSString())); //$NON-NLS-1$
assertNotNull("The component xml file must exist and be parsable from a jar file", xml); //$NON-NLS-1$
}
/**
* Creates all of the text edit changes collected from the processor. The
* collected edits are arranged as multi-edits for the one file that they
* belong to
*
* @param projectchange
* @param project
* @param cxml
*/
private void createTagChanges(CompositeChange projectchange, IJavaProject project, File cxml) {
try {
HashMap<IFile, Set<TextEdit>> map = new HashMap<>();
ApiDescriptionProcessor.collectTagUpdates(project, cxml, map);
IFile file = null;
TextFileChange change = null;
MultiTextEdit multiedit = null;
Set<TextEdit> alledits = null;
TextEdit edit = null;
for (Entry<IFile, Set<TextEdit>> entry : map.entrySet()) {
file = entry.getKey();
change = new TextFileChange(MessageFormat.format(WizardMessages.JavadocTagRefactoring_2, new Object[] { file.getName() }), file);
multiedit = new MultiTextEdit();
change.setEdit(multiedit);
alledits = entry.getValue();
if (alledits != null) {
for (Iterator<TextEdit> iter2 = alledits.iterator(); iter2.hasNext();) {
edit = iter2.next();
multiedit.addChild(edit);
}
}
if (change != null) {
projectchange.add(change);
}
}
} catch (CoreException e) {
ApiUIPlugin.log(e);
} catch (IOException e) {
ApiUIPlugin.log(e);
}
}
/**
* Processes the change from original to updated
*
* @param typename the name of the type to query
* @param membername the name of the member
* @param signature the signature of the member
* @param the tags we expect to see
*/
protected void processUpdatedItem(String typename, String innertypename, String membername, String signature, String[] expectedtags) throws Exception {
try {
IJavaProject project = getTestingJavaProject(TESTING_PROJECT_NAME);
IType type = project.findType("javadoc", typename); //$NON-NLS-1$
assertNotNull("the type for javadoc." + typename + " must exist", type); //$NON-NLS-1$ //$NON-NLS-2$
ASTParser parser = ASTParser.newParser(AST.JLS_Latest);
parser.setSource(type.getCompilationUnit());
CompilationUnit cunit = (CompilationUnit) parser.createAST(new NullProgressMonitor());
ChangeVisitor visitor = new ChangeVisitor(typename, innertypename, membername, signature, expectedtags);
cunit.accept(visitor);
assertTrue("the specified node should have been processed", visitor.processed); //$NON-NLS-1$
} catch (JavaModelException jme) {
fail("the test class javadoc." + typename + " had problems loading"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
/**
* Tests the addition of a javadoc tag to a class. Uses
* <code>JavadocTestClass1</code>
*/
@Test
public void testProcessClassAddition() throws Exception {
processUpdatedItem("JavadocTestClass1", null, null, null, new String[] { "@noinstantiate" }); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Tests the addition of a javadoc tag to a class that does not have a
* javadoc section yet Uses <code>JavadocTestClass7</code>
*/
@Test
public void testProcessClassAdditionNoDocElement() throws Exception {
processUpdatedItem("JavadocTestClass7", null, null, null, new String[] { //$NON-NLS-1$
"@noextend", "@noinstantiate" }); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Tests the addition of a javadoc tag to a method that does not have a
* javadoc section yet Uses <code>JavadocTestClass7</code>
*/
@Test
public void testProcessMethodAdditionNoDocElement() throws Exception {
processUpdatedItem("JavadocTestClass7", null, "m1", "()V", new String[] { "@nooverride" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
/**
* Tests the addition of a javadoc tag to a field that does not have a
* javadoc section yet Uses <code>JavadocTestClass7</code>
*/
@Test
public void testProcessFieldAdditionNoDocElement() throws Exception {
processUpdatedItem("JavadocTestClass7", null, "f1", null, new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Tests the addition of a javadoc tag to an inner class. Uses
* <code>JavadocTestClass2</code>
*/
@Test
public void testProcessInnerClassAddition() throws Exception {
processUpdatedItem("JavadocTestClass2", "Inner", null, null, new String[] { "@noinstantiate" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Tests the addition of a javadoc tags to methods. Uses
* <code>JavadocTestClass3</code>
*/
@Test
public void testProcessMethodAddition() throws Exception {
processUpdatedItem("JavadocTestClass3", null, "m1", "()V", new String[] { "@nooverride" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
processUpdatedItem("JavadocTestClass3", null, "m2", "()V", new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
/**
* Tests the addition of a javadoc tags to fields. Uses
* <code>JavadocTestClass4</code>
*/
@Test
public void testProcessFieldAddition() throws Exception {
processUpdatedItem("JavadocTestClass4", null, "f1", null, new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
processUpdatedItem("JavadocTestClass4", null, "f2", null, new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Tests the addition of a javadoc tags to methods in inner classes. Uses
* <code>JavadocTestClass6</code>
*/
@Test
public void testProcessInnerMethodAddition() throws Exception {
processUpdatedItem("JavadocTestClass6", "Inner2", "m1", "()V", new String[] { "@nooverride" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
processUpdatedItem("JavadocTestClass6", "Inner2", "m2", "()V", new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
/**
* Tests the addition of a javadoc tags to fields in inner classes. Uses
* <code>JavadocTestClass5</code>
*/
@Test
public void testProcessInnerFieldAddition() throws Exception {
processUpdatedItem("JavadocTestClass5", "Inner2", "f1", null, new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
processUpdatedItem("JavadocTestClass5", "Inner2", "f2", null, new String[] { "@noreference" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
/**
* Tests that the old 'subclass' attribute is correctly resolved to
* 'noextend' restriction for tag updating. Tests the case of bug 210786
* (https://bugs.eclipse.org/bugs/show_bug.cgi?id=210786) Uses
* <code>JavadocTestClass8</code>
*/
@Test
public void testProcessSubclassAttribute() throws Exception {
processUpdatedItem("JavadocTestClass8", null, null, null, new String[] { "@noextend" }); //$NON-NLS-1$ //$NON-NLS-2$
}
}