[doc] Add unit test for rule doc generator
Therefore the file writer has been mocked. The rulesets to be documented are now given, rather than determined by the generator.
This commit is contained in:
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class DefaultFileWriter implements FileWriter {
|
||||
@Override
|
||||
public void write(Path path, List<String> lines) throws IOException {
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.write(path, lines, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public interface FileWriter {
|
||||
|
||||
void write(Path path, List<String> lines) throws IOException;
|
||||
}
|
@ -6,18 +6,27 @@ package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleSetNotFoundException;
|
||||
|
||||
public class GenerateRuleDocsCmd {
|
||||
private GenerateRuleDocsCmd() {
|
||||
// Utility class
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws RuleSetNotFoundException {
|
||||
long start = System.currentTimeMillis();
|
||||
RuleDocGenerator generator = new RuleDocGenerator();
|
||||
Path output = FileSystems.getDefault().getPath(args[0]).resolve("..").toAbsolutePath().normalize();
|
||||
System.out.println("Generating docs into " + output);
|
||||
generator.generate(output);
|
||||
RuleDocGenerator generator = new RuleDocGenerator(new DefaultFileWriter(), output);
|
||||
|
||||
RuleSetFactory ruleSetFactory = new RuleSetFactory();
|
||||
Iterator<RuleSet> registeredRuleSets = ruleSetFactory.getRegisteredRuleSets();
|
||||
|
||||
generator.generate(registeredRuleSets);
|
||||
System.out.println("Generated docs in " + (System.currentTimeMillis() - start) + " ms");
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -29,7 +28,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import net.sourceforge.pmd.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.Rule;
|
||||
import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleSetNotFoundException;
|
||||
import net.sourceforge.pmd.lang.Language;
|
||||
import net.sourceforge.pmd.lang.rule.XPathRule;
|
||||
@ -40,21 +38,25 @@ public class RuleDocGenerator {
|
||||
private static final String RULESET_INDEX_FILENAME_PATTERN = "docs/pages/pmd/rules/${language.tersename}/${ruleset.name}.md";
|
||||
private static final String RULESET_INDEX_PERMALINK_PATTERN = "pmd_rules_${language.tersename}_${ruleset.name}.html";
|
||||
|
||||
private Path root;
|
||||
private final Path root;
|
||||
private final FileWriter writer;
|
||||
|
||||
public void generate(Path root) {
|
||||
public RuleDocGenerator(FileWriter writer, Path root) {
|
||||
this.root = Objects.requireNonNull(root, "Root directory must be provided");
|
||||
this.writer = Objects.requireNonNull(writer, "A file writer must be provided");
|
||||
|
||||
Path docsDir = root.resolve("docs");
|
||||
if (!Files.exists(docsDir) || !Files.isDirectory(docsDir)) {
|
||||
throw new IllegalArgumentException("Couldn't find \"docs\" subdirectory");
|
||||
}
|
||||
}
|
||||
|
||||
Map<Language, List<RuleSet>> rulesets;
|
||||
public void generate(Iterator<RuleSet> rulesets) {
|
||||
Map<Language, List<RuleSet>> sortedRulesets;
|
||||
try {
|
||||
rulesets = loadAndSortRulesets();
|
||||
generateLanguageIndex(rulesets);
|
||||
generateRuleSetIndex(rulesets);
|
||||
sortedRulesets = sortRulesets(rulesets);
|
||||
generateLanguageIndex(sortedRulesets);
|
||||
generateRuleSetIndex(sortedRulesets);
|
||||
|
||||
} catch (RuleSetNotFoundException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -65,23 +67,20 @@ public class RuleDocGenerator {
|
||||
return root.resolve(FilenameUtils.normalize(filename));
|
||||
}
|
||||
|
||||
private Map<Language, List<RuleSet>> loadAndSortRulesets() throws RuleSetNotFoundException {
|
||||
RuleSetFactory ruleSetFactory = new RuleSetFactory();
|
||||
Iterator<RuleSet> registeredRuleSets = ruleSetFactory.getRegisteredRuleSets();
|
||||
private Map<Language, List<RuleSet>> sortRulesets(Iterator<RuleSet> rulesets) throws RuleSetNotFoundException {
|
||||
Map<Language, List<RuleSet>> rulesetsByLanguage = new HashMap<>();
|
||||
|
||||
Map<Language, List<RuleSet>> rulesets = new HashMap<>();
|
||||
|
||||
while (registeredRuleSets.hasNext()) {
|
||||
RuleSet ruleset = registeredRuleSets.next();
|
||||
while (rulesets.hasNext()) {
|
||||
RuleSet ruleset = rulesets.next();
|
||||
Language language = getRuleSetLanguage(ruleset);
|
||||
|
||||
if (!rulesets.containsKey(language)) {
|
||||
rulesets.put(language, new ArrayList<RuleSet>());
|
||||
if (!rulesetsByLanguage.containsKey(language)) {
|
||||
rulesetsByLanguage.put(language, new ArrayList<RuleSet>());
|
||||
}
|
||||
rulesets.get(language).add(ruleset);
|
||||
rulesetsByLanguage.get(language).add(ruleset);
|
||||
}
|
||||
|
||||
for (List<RuleSet> rulesetsOfOneLanguage : rulesets.values()) {
|
||||
for (List<RuleSet> rulesetsOfOneLanguage : rulesetsByLanguage.values()) {
|
||||
Collections.sort(rulesetsOfOneLanguage, new Comparator<RuleSet>() {
|
||||
@Override
|
||||
public int compare(RuleSet o1, RuleSet o2) {
|
||||
@ -89,7 +88,7 @@ public class RuleDocGenerator {
|
||||
}
|
||||
});
|
||||
}
|
||||
return rulesets;
|
||||
return rulesetsByLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,7 +151,7 @@ public class RuleDocGenerator {
|
||||
}
|
||||
|
||||
System.out.println("Generated " + path);
|
||||
Files.write(path, lines, StandardCharsets.UTF_8);
|
||||
writer.write(path, lines);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,6 +218,7 @@ public class RuleDocGenerator {
|
||||
|
||||
for (Rule rule : getSortedRules(ruleset)) {
|
||||
lines.add("## " + rule.getName());
|
||||
lines.add("");
|
||||
if (rule.getSince() != null) {
|
||||
lines.add("**Since:** " + rule.getSince());
|
||||
lines.add("");
|
||||
@ -230,6 +230,7 @@ public class RuleDocGenerator {
|
||||
lines.add("");
|
||||
if (!rule.getExamples().isEmpty()) {
|
||||
lines.add("**Example(s):**");
|
||||
lines.add("");
|
||||
for (String example : rule.getExamples()) {
|
||||
lines.add("```");
|
||||
lines.add(StringUtils.stripToEmpty(example));
|
||||
@ -260,8 +261,7 @@ public class RuleDocGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
Files.createDirectories(path.getParent());
|
||||
Files.write(path, lines, StandardCharsets.UTF_8);
|
||||
writer.write(path, lines);
|
||||
System.out.println("Generated " + path);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class MockedFileWriter implements FileWriter {
|
||||
|
||||
public static class FileEntry {
|
||||
private String filename;
|
||||
private String content;
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
private List<FileEntry> data = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void write(Path path, List<String> lines) throws IOException {
|
||||
FileEntry entry = new FileEntry();
|
||||
entry.filename = path.toString();
|
||||
entry.content = StringUtils.join(lines, System.getProperty("line.separator"));
|
||||
data.add(entry);
|
||||
}
|
||||
|
||||
public List<FileEntry> getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
data.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleSetNotFoundException;
|
||||
import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry;
|
||||
|
||||
public class RuleDocGeneratorTest {
|
||||
|
||||
private MockedFileWriter writer = new MockedFileWriter();
|
||||
private Path root;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
writer.reset();
|
||||
|
||||
root = Files.createTempDirectory("pmd-ruledocgenerator-test");
|
||||
Files.createDirectory(root.resolve("docs"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() throws IOException {
|
||||
Files.delete(root.resolve("docs"));
|
||||
Files.delete(root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleRuleset() throws RuleSetNotFoundException, IOException {
|
||||
RuleDocGenerator generator = new RuleDocGenerator(writer, root);
|
||||
|
||||
RuleSetFactory rsf = new RuleSetFactory();
|
||||
RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml");
|
||||
|
||||
generator.generate(Arrays.asList(ruleset).iterator());
|
||||
|
||||
assertEquals(2, writer.getData().size());
|
||||
FileEntry languageIndex = writer.getData().get(0);
|
||||
assertTrue(languageIndex.getFilename().endsWith("docs/pages/pmd/rules/java.md"));
|
||||
assertEquals(IOUtils.toString(RuleDocGeneratorTest.class.getResourceAsStream("/expected/java.md")),
|
||||
languageIndex.getContent());
|
||||
|
||||
FileEntry ruleSetIndex = writer.getData().get(1);
|
||||
assertTrue(ruleSetIndex.getFilename().endsWith("docs/pages/pmd/rules/java/sample.md"));
|
||||
assertEquals(IOUtils.toString(RuleDocGeneratorTest.class.getResourceAsStream("/expected/sample.md")),
|
||||
ruleSetIndex.getContent());
|
||||
}
|
||||
}
|
12
pmd-doc/src/test/resources/expected/java.md
Normal file
12
pmd-doc/src/test/resources/expected/java.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Java Rules
|
||||
permalink: pmd_rules_java.html
|
||||
folder: pmd/rules
|
||||
---
|
||||
List of rulesets and rules contained in each ruleset.
|
||||
|
||||
* [Sample](pmd_rules_java_sample.html): Sample ruleset to test rule doc generation.
|
||||
|
||||
## Sample
|
||||
* [JumbledIncrementer](pmd_rules_java_sample.html#jumbledincrementer): Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
|
||||
* [OverrideBothEqualsAndHashcode](pmd_rules_java_sample.html#overridebothequalsandhashcode): Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or ov...
|
62
pmd-doc/src/test/resources/expected/sample.md
Normal file
62
pmd-doc/src/test/resources/expected/sample.md
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Sample
|
||||
summary: Sample ruleset to test rule doc generation.
|
||||
permalink: pmd_rules_java_sample.html
|
||||
folder: pmd/rules/java
|
||||
sidebaractiveurl: /pmd_rules_java.html
|
||||
editmepath: ../rulesets/ruledoctest/sample.xml
|
||||
---
|
||||
## JumbledIncrementer
|
||||
|
||||
**Since:** 1.0
|
||||
|
||||
**Priority:** Medium (3)
|
||||
|
||||
Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
|
||||
|
||||
**Example(s):**
|
||||
|
||||
```
|
||||
public class JumbledIncrementerRule1 {
|
||||
public void foo() {
|
||||
for (int i = 0; i < 10; i++) { // only references 'i'
|
||||
for (int k = 0; k < 20; i++) { // references both 'i' and 'k'
|
||||
System.out.println("Hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## OverrideBothEqualsAndHashcode
|
||||
|
||||
**Since:** 0.4
|
||||
|
||||
**Priority:** Medium (3)
|
||||
|
||||
Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass.
|
||||
|
||||
**Example(s):**
|
||||
|
||||
```
|
||||
public class Bar { // poor, missing a hashcode() method
|
||||
public boolean equals(Object o) {
|
||||
// do some comparison
|
||||
}
|
||||
}
|
||||
|
||||
public class Baz { // poor, missing an equals() method
|
||||
public int hashCode() {
|
||||
// return some hash value
|
||||
}
|
||||
}
|
||||
|
||||
public class Foo { // perfect, both methods provided
|
||||
public boolean equals(Object other) {
|
||||
// do some comparison
|
||||
}
|
||||
public int hashCode() {
|
||||
// return some hash value
|
||||
}
|
||||
}
|
||||
```
|
85
pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml
Normal file
85
pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml
Normal file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<ruleset name="Sample"
|
||||
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
|
||||
<description>
|
||||
Sample ruleset to test rule doc generation.
|
||||
</description>
|
||||
|
||||
<rule name="OverrideBothEqualsAndHashcode"
|
||||
language="java"
|
||||
since="0.4"
|
||||
message="Ensure you override both equals() and hashCode()"
|
||||
class="net.sourceforge.pmd.lang.java.rule.basic.OverrideBothEqualsAndHashcodeRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_sample.html#overridebothequalsandhashcode">
|
||||
<description>
|
||||
Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class Bar { // poor, missing a hashcode() method
|
||||
public boolean equals(Object o) {
|
||||
// do some comparison
|
||||
}
|
||||
}
|
||||
|
||||
public class Baz { // poor, missing an equals() method
|
||||
public int hashCode() {
|
||||
// return some hash value
|
||||
}
|
||||
}
|
||||
|
||||
public class Foo { // perfect, both methods provided
|
||||
public boolean equals(Object other) {
|
||||
// do some comparison
|
||||
}
|
||||
public int hashCode() {
|
||||
// return some hash value
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
|
||||
<rule name="JumbledIncrementer"
|
||||
language="java"
|
||||
since="1.0"
|
||||
message="Avoid modifying an outer loop incrementer in an inner loop for update expression"
|
||||
class="net.sourceforge.pmd.lang.rule.XPathRule"
|
||||
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_sample.html#jumbledincrementer">
|
||||
<description>
|
||||
Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//ForStatement
|
||||
[
|
||||
ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image
|
||||
=
|
||||
ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image
|
||||
]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
<example>
|
||||
<![CDATA[
|
||||
public class JumbledIncrementerRule1 {
|
||||
public void foo() {
|
||||
for (int i = 0; i < 10; i++) { // only references 'i'
|
||||
for (int k = 0; k < 20; i++) { // references both 'i' and 'k'
|
||||
System.out.println("Hello");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</example>
|
||||
</rule>
|
||||
</ruleset>
|
Reference in New Issue
Block a user