blob: 4a00d9f37e3682600790a1ab8424c67785903849 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.plugins.javadoc;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaMethod;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugin.testing.AbstractMojoTestCase;
import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
import org.apache.maven.plugins.javadoc.AbstractFixJavadocMojo.JavaEntityTags;
import org.apache.maven.shared.invoker.MavenInvocationException;
import org.codehaus.plexus.languages.java.version.JavaVersion;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
import static org.apache.commons.lang3.reflect.MethodUtils.invokeMethod;
/**
* @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
*/
public class FixJavadocMojoTest extends AbstractMojoTestCase {
/** The vm line separator */
private static final String EOL = System.getProperty("line.separator");
/** flag to copy repo only one time */
private static boolean TEST_REPO_CREATED = false;
/** {@inheritDoc} */
@Override
protected void setUp() throws Exception {
super.setUp();
createTestRepo();
}
/**
* Create test repository in target directory.
*
* @throws IOException if any
*/
private void createTestRepo() throws Exception {
if (TEST_REPO_CREATED) {
return;
}
File localRepo = new File(getBasedir(), "target/local-repo/");
localRepo.mkdirs();
// ----------------------------------------------------------------------
// fix-test-1.0.jar
// ----------------------------------------------------------------------
File sourceDir = new File(getBasedir(), "src/test/resources/unit/fix-test/repo/");
assertTrue(sourceDir.exists());
FileUtils.copyDirectoryStructure(sourceDir, localRepo);
// Remove SCM files
List<String> files = FileUtils.getFileAndDirectoryNames(
localRepo, FileUtils.getDefaultExcludesAsString(), null, true, true, true, true);
for (String filename : files) {
File file = new File(filename);
if (file.isDirectory()) {
FileUtils.deleteDirectory(file);
} else {
file.delete();
}
}
TEST_REPO_CREATED = true;
}
/**
* @throws Exception if any
*/
public void testFix() throws Exception {
File testPomBasedir = new File(getBasedir(), "target/test/unit/fix-test");
executeMojoAndTest(testPomBasedir, new String[] {
"ClassWithJavadoc.java",
"ClassWithNoJavadoc.java",
"InterfaceWithJavadoc.java",
"InterfaceWithNoJavadoc.java"
});
}
// ----------------------------------------------------------------------
// Test private static methods
// ----------------------------------------------------------------------
/**
* @throws Exception if any
*/
public void testAutodetectIndentation() throws Exception {
Method method = AbstractFixJavadocMojo.class.getDeclaredMethod("autodetectIndentation", String.class);
method.setAccessible(true);
String s = null;
assertEquals("", (String) method.invoke(null, s));
s = "no indentation";
assertEquals("", (String) method.invoke(null, s));
s = "no indentation with right spaces ";
assertEquals("", (String) method.invoke(null, s));
s = " indentation";
assertEquals(" ", (String) method.invoke(null, s));
s = " indentation with right spaces ";
assertEquals(" ", (String) method.invoke(null, s));
s = "\ttab indentation";
assertEquals("\t", (String) method.invoke(null, s));
s = " \n indentation with right spaces ";
assertEquals(" \n ", (String) method.invoke(null, s));
}
/**
* @throws Exception if any
*/
public void testTrimLeft() throws Exception {
Method method = AbstractFixJavadocMojo.class.getDeclaredMethod("trimLeft", String.class);
method.setAccessible(true);
assertEquals("", (String) method.invoke(null, (String) null));
assertEquals("", (String) method.invoke(null, " "));
assertEquals("", (String) method.invoke(null, " \t "));
assertEquals("a", (String) method.invoke(null, "a"));
assertEquals("a", (String) method.invoke(null, " a"));
assertEquals("a", (String) method.invoke(null, "\ta"));
assertEquals("a ", (String) method.invoke(null, " a "));
assertEquals("a\t", (String) method.invoke(null, "\ta\t"));
}
/**
* @throws Exception if any
*/
public void testTrimRight() throws Exception {
Method method = AbstractFixJavadocMojo.class.getDeclaredMethod("trimRight", String.class);
method.setAccessible(true);
assertEquals("", (String) method.invoke(null, (String) null));
assertEquals("", (String) method.invoke(null, " "));
assertEquals("", (String) method.invoke(null, " \t "));
assertEquals("a", (String) method.invoke(null, "a"));
assertEquals("a", (String) method.invoke(null, "a "));
assertEquals("a", (String) method.invoke(null, "a\t"));
assertEquals(" a", (String) method.invoke(null, " a "));
assertEquals("\ta", (String) method.invoke(null, "\ta\t"));
}
/**
* @throws Exception if any
*/
public void testHasInheritedTag() throws Exception {
Method method = AbstractFixJavadocMojo.class.getDeclaredMethod("hasInheritedTag", String.class);
method.setAccessible(true);
String content = "/** {@inheritDoc} */";
Boolean has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.TRUE, has);
content = "/**{@inheritDoc}*/";
has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.TRUE, has);
content = "/**{@inheritDoc } */";
has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.TRUE, has);
content = "/** {@inheritDoc } */";
has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.TRUE, has);
content = "/** */";
has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.FALSE, has);
content = "/**{ @inheritDoc }*/";
has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.FALSE, has);
content = "/**{@ inheritDoc}*/";
has = (Boolean) method.invoke(null, content);
assertEquals(Boolean.FALSE, has);
}
/**
* @throws Throwable if any
*/
public void testJavadocComment() throws Throwable {
String content = "/**" + EOL + " * Dummy Class."
+ EOL + " */"
+ EOL + "public class DummyClass"
+ EOL + "{"
+ EOL + " /**"
+ EOL + " *"
+ EOL + " * Dummy"
+ EOL + " *"
+ EOL + " * Method."
+ EOL + " *"
+ EOL + " * @param args not"
+ EOL + " *"
+ EOL + " * null"
+ EOL + " * @param i non negative"
+ EOL + " * @param object could"
+ EOL + " * be"
+ EOL + " * null"
+ EOL + " * @return a"
+ EOL + " * String"
+ EOL + " *"
+ EOL + " * @throws Exception if"
+ EOL + " * any"
+ EOL + " *"
+ EOL + " */"
+ EOL + " public static String dummyMethod( String[] args, int i, Object object )"
+ EOL + " throws Exception"
+ EOL + " {"
+ EOL + " return null;"
+ EOL + " }"
+ EOL + "}";
JavaProjectBuilder builder = new JavaProjectBuilder();
builder.setEncoding("UTF-8");
builder.addSource(new StringReader(content));
JavaClass clazz = builder.addSource(new StringReader(content)).getClassByName("DummyClass");
JavaMethod javaMethod = clazz.getMethods().get(0);
String javadoc = AbstractFixJavadocMojo.extractOriginalJavadoc(content, javaMethod);
assertEquals(
" /**" + EOL + " *"
+ EOL + " * Dummy"
+ EOL + " *"
+ EOL + " * Method."
+ EOL + " *"
+ EOL + " * @param args not"
+ EOL + " *"
+ EOL + " * null"
+ EOL + " * @param i non negative"
+ EOL + " * @param object could"
+ EOL + " * be"
+ EOL + " * null"
+ EOL + " * @return a"
+ EOL + " * String"
+ EOL + " *"
+ EOL + " * @throws Exception if"
+ EOL + " * any"
+ EOL + " *"
+ EOL + " */",
javadoc);
String javadocContent = AbstractFixJavadocMojo.extractOriginalJavadocContent(content, javaMethod);
assertEquals(
" *" + EOL + " * Dummy"
+ EOL + " *"
+ EOL + " * Method."
+ EOL + " *"
+ EOL + " * @param args not"
+ EOL + " *"
+ EOL + " * null"
+ EOL + " * @param i non negative"
+ EOL + " * @param object could"
+ EOL + " * be"
+ EOL + " * null"
+ EOL + " * @return a"
+ EOL + " * String"
+ EOL + " *"
+ EOL + " * @throws Exception if"
+ EOL + " * any"
+ EOL + " *",
javadocContent);
Method method = AbstractFixJavadocMojo.class.getDeclaredMethod("removeLastEmptyJavadocLines", String.class);
method.setAccessible(true);
String withoutEmptyJavadocLines = (String) method.invoke(null, javadocContent);
assertTrue(withoutEmptyJavadocLines.endsWith("any"));
String methodJavadoc = AbstractFixJavadocMojo.getJavadocComment(content, javaMethod);
assertEquals(
" *" + EOL + " * Dummy" + EOL + " *" + EOL + " * Method." + EOL + " *",
methodJavadoc);
withoutEmptyJavadocLines = (String) method.invoke(null, methodJavadoc);
assertTrue(withoutEmptyJavadocLines.endsWith("Method."));
assertEquals(5, javaMethod.getTags().size());
AbstractFixJavadocMojo mojoInstance = new FixJavadocMojo();
setVariableValueToObject(mojoInstance, "fixTagsSplitted", new String[] {"all"});
DocletTag tag = javaMethod.getTags().get(0);
String tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @param args not" + EOL + " *" + EOL + " * null", tagJavadoc);
withoutEmptyJavadocLines = (String) method.invoke(null, tagJavadoc);
assertTrue(withoutEmptyJavadocLines.endsWith("null"));
tag = javaMethod.getTags().get(1);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @param i non negative", tagJavadoc);
withoutEmptyJavadocLines = (String) method.invoke(null, tagJavadoc);
assertTrue(withoutEmptyJavadocLines.endsWith("negative"));
tag = javaMethod.getTags().get(2);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @param object could" + EOL + " * be" + EOL + " * null", tagJavadoc);
withoutEmptyJavadocLines = (String) method.invoke(null, tagJavadoc);
assertTrue(withoutEmptyJavadocLines.endsWith("null"));
tag = javaMethod.getTags().get(3);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @return a" + EOL + " * String" + EOL + " *", tagJavadoc);
withoutEmptyJavadocLines = (String) method.invoke(null, tagJavadoc);
assertTrue(withoutEmptyJavadocLines.endsWith("String"));
tag = javaMethod.getTags().get(4);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @throws Exception if" + EOL + " * any" + EOL + " *", tagJavadoc);
withoutEmptyJavadocLines = (String) method.invoke(null, tagJavadoc);
assertTrue(withoutEmptyJavadocLines.endsWith("any"));
}
public void testJavadocCommentJdk5() throws Exception {
String content = "/**" + EOL + " * Dummy Class."
+ EOL + " */"
+ EOL + "public class DummyClass"
+ EOL + "{"
+ EOL + " /**"
+ EOL + " * Dummy method."
+ EOL + " *"
+ EOL + " * @param <K> The Key type for the method"
+ EOL + " * @param <V> The Value type for the method"
+ EOL + " * @param name The name."
+ EOL + " * @return A map configured."
+ EOL + " */"
+ EOL + " public <K, V> java.util.Map<K, V> dummyMethod( String name )"
+ EOL + " {"
+ EOL + " return null;"
+ EOL + " }"
+ EOL + "}";
JavaProjectBuilder builder = new JavaProjectBuilder();
builder.setEncoding("UTF-8");
JavaClass clazz = builder.addSource(new StringReader(content)).getClassByName("DummyClass");
JavaMethod javaMethod = clazz.getMethods().get(0);
String methodJavadoc = AbstractFixJavadocMojo.getJavadocComment(content, javaMethod);
assertEquals(" * Dummy method." + EOL + " *", methodJavadoc);
assertEquals(4, javaMethod.getTags().size());
AbstractFixJavadocMojo mojoInstance = new FixJavadocMojo();
setVariableValueToObject(mojoInstance, "fixTagsSplitted", new String[] {"all"});
DocletTag tag = javaMethod.getTags().get(0);
String tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @param <K> The Key type for the method", tagJavadoc);
tag = javaMethod.getTags().get(1);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @param <V> The Value type for the method", tagJavadoc);
tag = javaMethod.getTags().get(2);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @param name The name.", tagJavadoc);
tag = javaMethod.getTags().get(3);
tagJavadoc = mojoInstance.getJavadocComment(content, javaMethod, tag);
assertEquals(" * @return A map configured.", tagJavadoc);
}
public void testInitParameters() throws Throwable {
AbstractFixJavadocMojo mojoInstance = new FixJavadocMojo();
setVariableValueToObject(mojoInstance, "fixTags", "author, version, since, param, return, throws, link");
setVariableValueToObject(mojoInstance, "defaultSince", "1.0");
setVariableValueToObject(mojoInstance, "level", "protected");
invokeMethod(mojoInstance, true, "init");
String[] fixTags = (String[]) getVariableValueFromObject(mojoInstance, "fixTagsSplitted");
assertEquals("author", fixTags[0]);
assertEquals("version", fixTags[1]);
assertEquals("since", fixTags[2]);
assertEquals("param", fixTags[3]);
assertEquals("return", fixTags[4]);
assertEquals("throws", fixTags[5]);
assertEquals("link", fixTags[6]);
assertEquals(7, fixTags.length);
setVariableValueToObject(mojoInstance, "fixTags", "return, fake_value");
invokeMethod(mojoInstance, true, "init");
fixTags = (String[]) getVariableValueFromObject(mojoInstance, "fixTagsSplitted");
assertEquals("return", fixTags[0]);
assertEquals(1, fixTags.length);
}
public void testRemoveUnknownExceptions() throws Exception {
AbstractFixJavadocMojo mojoInstance = new FixJavadocMojo();
setVariableValueToObject(mojoInstance, "fixTagsSplitted", new String[] {"all"});
setVariableValueToObject(mojoInstance, "project", new MavenProjectStub());
String source = "package a.b.c;" + EOL
+ "public class Clazz {" + EOL
+ " /**" + EOL
+ " * @throws java.lang.RuntimeException" + EOL
+ " * @throws NumberFormatException" + EOL
+ " * @throws java.lang.Exception" + EOL // not thrown and no RTE -> remove
+ " * @throws com.foo.FatalException" + EOL // not on classpath (?!) -> see removeUnknownThrows
+ " */" + EOL
+ " public void method() {}" + EOL
+ "}";
JavaProjectBuilder builder = new JavaProjectBuilder();
JavaMethod javaMethod = builder.addSource(new StringReader(source))
.getClassByName("Clazz")
.getMethods()
.get(0);
JavaEntityTags javaEntityTags = mojoInstance.parseJavadocTags(source, javaMethod, "", true);
StringBuilder sb = new StringBuilder();
mojoInstance.writeThrowsTag(
sb, javaMethod, javaEntityTags, Collections.singletonList("java.lang" + ".RuntimeException"));
assertEquals(" * @throws java.lang.RuntimeException", sb.toString());
sb = new StringBuilder();
mojoInstance.writeThrowsTag(sb, javaMethod, javaEntityTags, Collections.singletonList("NumberFormatException"));
assertEquals(" * @throws java.lang.NumberFormatException", sb.toString());
sb = new StringBuilder();
mojoInstance.writeThrowsTag(sb, javaMethod, javaEntityTags, Collections.singletonList("java.lang.Exception"));
assertEquals("", sb.toString());
setVariableValueToObject(mojoInstance, "removeUnknownThrows", true);
sb = new StringBuilder();
mojoInstance.writeThrowsTag(
sb, javaMethod, javaEntityTags, Collections.singletonList("com.foo.FatalException"));
assertEquals("", sb.toString());
setVariableValueToObject(mojoInstance, "removeUnknownThrows", false);
sb = new StringBuilder();
mojoInstance.writeThrowsTag(
sb, javaMethod, javaEntityTags, Collections.singletonList("com.foo.FatalException"));
assertEquals(" * @throws com.foo.FatalException if any.", sb.toString());
}
// ----------------------------------------------------------------------
// private methods
// ----------------------------------------------------------------------
/**
* @param testPomBasedir the basedir for the test project
* @param clazzToCompare an array of the classes name to compare
* @throws Exception if any
*/
private void executeMojoAndTest(File testPomBasedir, String[] clazzToCompare) throws Exception {
prepareTestProjects(testPomBasedir.getName());
File testPom = new File(testPomBasedir, "pom.xml");
assertTrue(testPom.getAbsolutePath() + " should exist", testPom.exists());
FixJavadocMojo mojo = (FixJavadocMojo) lookupMojo("fix", testPom);
assertNotNull(mojo);
MavenSession session = newMavenSession(mojo.getProject());
// Ensure remote repo connection uses SSL
File globalSettingsFile = new File(getBasedir(), "target/test-classes/unit/settings.xml");
session.getRequest().setGlobalSettingsFile(globalSettingsFile);
setVariableValueToObject(mojo, "session", session);
// compile the test project
invokeCompileGoal(testPom, globalSettingsFile, mojo.getLog());
assertTrue(new File(testPomBasedir, "target/classes").exists());
mojo.execute();
File expectedDir = new File(testPomBasedir, "expected/src/main/java/fix/test");
assertTrue(expectedDir.exists());
File generatedDir = new File(testPomBasedir, "target/generated/fix/test");
assertTrue(generatedDir.exists());
for (String className : clazzToCompare) {
assertEquals(new File(expectedDir, className), new File(generatedDir, className));
}
}
/**
* Invoke the compilation on the given pom file.
*
* @param testPom not null
* @param log not null
* @throws MavenInvocationException if any
*/
private void invokeCompileGoal(File testPom, File globalSettingsFile, Log log) throws Exception {
List<String> goals = new ArrayList<>();
goals.add("clean");
goals.add("compile");
File invokerDir = new File(getBasedir(), "target/invoker");
invokerDir.mkdirs();
File invokerLogFile = FileUtils.createTempFile("FixJavadocMojoTest", ".txt", invokerDir);
Properties properties = new Properties();
if (JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast("12")) {
properties.put("maven.compiler.source", "1.7");
properties.put("maven.compiler.target", "1.7");
} else if (JavaVersion.JAVA_SPECIFICATION_VERSION.isAtLeast("9")) {
properties.put("maven.compiler.source", "1.6");
properties.put("maven.compiler.target", "1.6");
}
// @todo unittests shouldn't need to go remote
if (JavaVersion.JAVA_SPECIFICATION_VERSION.isBefore("8")) {
// ensure that Java7 picks up TLSv1.2 when connecting with Central
properties.put("https.protocols", "TLSv1.2");
}
JavadocUtil.invokeMaven(
log,
new File(getBasedir(), "target/local-repo"),
testPom,
goals,
properties,
invokerLogFile,
globalSettingsFile);
}
// ----------------------------------------------------------------------
// static methods
// ----------------------------------------------------------------------
/**
* Asserts that files are equal. If they are not an AssertionFailedError is thrown.
*
* @throws IOException if any
*/
private static void assertEquals(File expected, File actual) throws Exception {
assertTrue(" Expected file DNE: " + expected, expected.exists());
String expectedContent = StringUtils.unifyLineSeparators(readFile(expected));
assertTrue(" Actual file DNE: " + actual, actual.exists());
String actualContent = StringUtils.unifyLineSeparators(readFile(actual));
assertEquals(
"Expected file: " + expected.getAbsolutePath() + ", actual file: " + actual.getAbsolutePath(),
expectedContent,
actualContent);
}
/**
* @param testProjectDirName not null
* @throws IOException if any
*/
private static void prepareTestProjects(String testProjectDirName) throws Exception {
File testPomBasedir = new File(getBasedir(), "target/test/unit/" + testProjectDirName);
// Using unit test dir
FileUtils.copyDirectoryStructure(
new File(getBasedir(), "src/test/resources/unit/" + testProjectDirName), testPomBasedir);
List<String> scmFiles = FileUtils.getDirectoryNames(testPomBasedir, "**/.svn", null, true);
for (String filename : scmFiles) {
File dir = new File(filename);
if (dir.isDirectory()) {
FileUtils.deleteDirectory(dir);
}
}
}
/**
* @param file not null
* @return the content of the given file
* @throws IOException if any
*/
private static String readFile(File file) throws Exception {
String content = FileUtils.fileRead(file, "UTF-8");
return content;
}
}