1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.internal;
21
22 import static com.google.common.truth.Truth.assertWithMessage;
23
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 import java.util.stream.Collectors;
35
36 import org.junit.jupiter.api.Test;
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
40
41 import com.puppycrawl.tools.checkstyle.internal.utils.XmlUtil;
42 import picocli.CommandLine;
43 import picocli.CommandLine.Model.OptionSpec;
44
45 public class CliOptionsXdocsSyncTest {
46
47 @Test
48 public void validateCliDocSections() throws Exception {
49 final NodeList sections = getSectionsFromXdoc("src/site/xdoc/cmdline.xml.vm");
50 final Node cmdUsageSection = sections.item(2);
51 final Map<String, String> cmdOptions = getOptions(cmdUsageSection);
52
53 final Class<?> cliOptions = Class.forName("com.puppycrawl.tools.checkstyle"
54 + ".Main$CliOptions");
55 final CommandLine commandLine = new CommandLine(cliOptions);
56 final List<OptionSpec> optionSpecList = commandLine.getCommandSpec().options();
57
58 for (OptionSpec opt : optionSpecList) {
59 final String option = opt.names()[0];
60 if ("-h".equals(option) || "-V".equals(option)) {
61 cmdOptions.remove(option);
62 continue;
63 }
64 final String descXdoc = cmdOptions.get(option);
65 final String descMain = opt.description()[0];
66 assertWithMessage("CLI Option: " + option + " present in "
67 + "Main.java but not documented in cmdline.xml.vm")
68 .that(descXdoc)
69 .isNotNull();
70 assertWithMessage("CLI options descriptions in xdoc: "
71 + " should match that of in Main.java")
72 .that(descMain)
73 .isEqualTo(descXdoc);
74 cmdOptions.remove(option);
75 }
76 assertWithMessage("CLI Options: %s present in cmdline.xml.vm, not documented in Main.java",
77 cmdOptions)
78 .that(cmdOptions)
79 .isEmpty();
80 }
81
82 @Test
83 public void validateCliUsageSection() throws Exception {
84 final NodeList sections = getSectionsFromXdoc("src/site/xdoc/cmdline.xml.vm");
85 final Node usageSource = XmlUtil.getFirstChildElement(sections.item(2));
86 final String usageText = XmlUtil.getFirstChildElement(usageSource).getTextContent();
87
88 final Set<String> shortParamsXdoc = getParameters(usageText, "-[a-zA-CE-X]\\b");
89 final Set<String> longParamsXdoc = getParameters(usageText, "-(-\\w+)+");
90
91 final Class<?> cliOptions = Class.forName("com.puppycrawl.tools.checkstyle"
92 + ".Main$CliOptions");
93 final CommandLine commandLine = new CommandLine(cliOptions);
94 final Set<String> shortParamsMain = commandLine.getCommandSpec().options()
95 .stream()
96 .map(OptionSpec::shortestName)
97 .collect(Collectors.toUnmodifiableSet());
98 final Set<String> longParamsMain = commandLine.getCommandSpec().options()
99 .stream()
100 .map(OptionSpec::longestName)
101 .filter(names -> names.length() != 2)
102 .collect(Collectors.toUnmodifiableSet());
103
104 assertWithMessage("Short parameters in Main.java and cmdline"
105 + ".xml.vm should match")
106 .that(shortParamsMain)
107 .isEqualTo(shortParamsXdoc);
108 assertWithMessage("Long parameters in Main.java and cmdline"
109 + ".xml.vm should match")
110 .that(longParamsMain)
111 .isEqualTo(longParamsXdoc);
112 }
113
114 private static Set<String> getParameters(String text, String regex) {
115 final Set<String> result = new HashSet<>();
116 final Pattern pattern = Pattern.compile(regex);
117 final Matcher matcher = pattern.matcher(text);
118 while (matcher.find()) {
119 result.add(matcher.group());
120 }
121 return result;
122 }
123
124 private static NodeList getSectionsFromXdoc(String xdocPath) throws Exception {
125 final Path path = Path.of(xdocPath);
126 final String input = Files.readString(path);
127 final Document document = XmlUtil.getRawXml(path.getFileName().toString(), input, input);
128 return document.getElementsByTagName("section");
129 }
130
131 private static Map<String, String> getOptions(Node subSection) {
132 final Map<String, String> result = new HashMap<>();
133 final Node subsectionNode = XmlUtil.findChildElementById(subSection,
134 "Command_line_usage_Command_Line_Options");
135 if (subsectionNode != null) {
136 final Node divNode = XmlUtil.getFirstChildElement(subsectionNode);
137 final Node tableNode = XmlUtil.getFirstChildElement(divNode);
138 final Node tbodyNode = XmlUtil.findChildElementById(tableNode, "body");
139 final Set<Node> rows = XmlUtil.findChildElementsByTag(tbodyNode, "tr");
140 final List<List<Node>> columns = rows.stream()
141 .map(row -> new ArrayList<>(XmlUtil.getChildrenElements(row)))
142 .collect(Collectors.toUnmodifiableList());
143 for (List<Node> column : columns) {
144 final Node command = column.get(1);
145 final Node description = column.get(2);
146 if (command != null && description != null) {
147 result.put(command.getTextContent().trim().substring(0, 2),
148 description.getTextContent().trim().replaceAll("\\s+", " "));
149 }
150 }
151 }
152 return result;
153 }
154 }