1 /*
2 * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2018, 2020 SAP SE. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 /**
26 * @test
27 * @summary Validate and test -?, -h and --help flags. All tools in the jdk
28 * should take the same flags to display the help message. These
29 * flags should be documented in the printed help message. The
30 * tool should quit without error code after displaying the
31 * help message (if there is no other problem with the command
32 * line).
33 * Also check that tools that used to accept -help still do
34 * so. Test that tools that never accepted -help don't do so
35 * in future. I.e., check that the tool returns with the same
36 * return code as called with an invalid flag, and does not
37 * print anything containing '-help' in that case.
38 * @compile HelpFlagsTest.java
39 * @run main HelpFlagsTest
40 */
41
42 import java.io.File;
43
44 public class HelpFlagsTest extends TestHelper {
45
46 // Tools that should not be tested because a usage message is pointless.
47 static final String[] TOOLS_NOT_TO_TEST = {
48 "jaccessinspector", // gui, don't test, win only
49 "jaccessinspector-32", // gui, don't test, win-32 only
50 "jaccesswalker", // gui, don't test, win only
51 "jaccesswalker-32", // gui, don't test, win-32 only
52 "jconsole", // gui, don't test
53 "servertool", // none. Shell, don't test.
54 "javaw", // don't test, win only
55 // These shall have a help message that resembles that of
56 // MIT's tools. Thus -?, -h and --help are supported, but not
57 // mentioned in the help text.
58 "kinit",
59 "klist",
60 "ktab",
61 // Oracle proprietary tools without help message.
62 "javacpl",
63 "jmc",
64 "jweblauncher",
65 "jcontrol",
66 "ssvagent"
67 };
68
69 // Lists which tools support which flags.
70 private static class ToolHelpSpec {
71 String toolname;
72
73 // How the flags supposed to be supported are handled.
74 //
75 // These flags are supported, i.e.,
76 // * the tool accepts the flag
77 // * the tool prints a help message if the flag is specified
78 // * this help message lists the flag
79 // * the tool exits with exit code '0'.
80 boolean supportsQuestionMark;
81 boolean supportsH;
82 boolean supportsHelp;
83
84 // One tool returns with exit code != '0'.
85 int exitcodeOfHelp;
86
87 // How legacy -help is handled.
88 //
89 // Tools that so far support -help should still do so, but
90 // not print documentation about it. Tools that do not
91 // support -help should not do so in future.
92 //
93 // The tools accepts legacy -help. -help should not be
94 // documented in the usage message.
95 boolean supportsLegacyHelp;
96
97 // Java itself documents -help. -help prints to stderr,
98 // while --help prints to stdout. Leave as is.
99 boolean documentsLegacyHelp;
100
101 // The exit code of the tool if an invalid argument is passed to it.
102 // An exit code != 0 would be expected, but not all tools handle it
103 // that way.
104 int exitcodeOfWrongFlag;
105
106 ToolHelpSpec(String n, int q, int h, int hp, int ex1, int l, int dl, int ex2) {
107 toolname = n;
108 supportsQuestionMark = ( q == 1 ? true : false );
109 supportsH = ( h == 1 ? true : false );
110 supportsHelp = ( hp == 1 ? true : false );
111 exitcodeOfHelp = ex1;
112
113 supportsLegacyHelp = ( l == 1 ? true : false );
114 documentsLegacyHelp = ( dl == 1 ? true : false );
115 exitcodeOfWrongFlag = ex2;
116 }
117 }
118
119 static ToolHelpSpec[] jdkTools = {
120 // name -? -h --help exitcode -help -help exitcode
121 // of help docu of wrong
122 // mented flag
123 new ToolHelpSpec("jabswitch", 0, 0, 0, 0, 0, 0, 0), // /?, prints help message anyways, win only
124 new ToolHelpSpec("jar", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
125 new ToolHelpSpec("jarsigner", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
126 new ToolHelpSpec("java", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
127 new ToolHelpSpec("javac", 1, 0, 1, 0, 1, 1, 2), // -?, --help -help, Documents -help, -h is already taken for "native header output directory".
128 new ToolHelpSpec("javadoc", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
129 new ToolHelpSpec("javap", 1, 1, 1, 0, 1, 1, 2), // -?, -h, --help -help, Documents -help
130 new ToolHelpSpec("javaw", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help, win only
131 new ToolHelpSpec("jcmd", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
132 new ToolHelpSpec("jdb", 1, 1, 1, 0, 1, 1, 0), // -?, -h, --help -help, Documents -help
133 new ToolHelpSpec("jdeprscan", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
134 new ToolHelpSpec("jdeps", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
135 new ToolHelpSpec("jfr", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
136 new ToolHelpSpec("jhsdb", 0, 0, 0, 0, 0, 0, 0), // none, prints help message anyways.
137 new ToolHelpSpec("jimage", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
138 new ToolHelpSpec("jinfo", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
139 new ToolHelpSpec("jjs", 0, 1, 1, 100, 0, 0, 100), // -h, --help, return code 100
140 new ToolHelpSpec("jlink", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help
141 new ToolHelpSpec("jmap", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
142 new ToolHelpSpec("jmod", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented.
143 new ToolHelpSpec("jnativescan", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
144 new ToolHelpSpec("jps", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
145 new ToolHelpSpec("jrunscript", 1, 1, 1, 0, 1, 1, 7), // -?, -h, --help -help, Documents -help
146 new ToolHelpSpec("jshell", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented.
147 new ToolHelpSpec("jstack", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
148 new ToolHelpSpec("jstat", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help
149 new ToolHelpSpec("jstatd", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help
150 new ToolHelpSpec("keytool", 1, 1, 1, 0, 1, 0, 1), // none, prints help message anyways.
151 new ToolHelpSpec("rmiregistry", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
152 new ToolHelpSpec("serialver", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways.
153 new ToolHelpSpec("jpackage", 0, 1, 1, 0, 0, 1, 1), // -h, --help,
154 new ToolHelpSpec("jwebserver", 1, 1, 1, 0, 0, 1, 1), // -?, -h, --help
155 };
156
157 // Returns corresponding object from jdkTools array.
158 static ToolHelpSpec getToolHelpSpec(String tool) {
159 for (ToolHelpSpec x : jdkTools) {
160 if (tool.toLowerCase().equals(x.toolname) ||
161 tool.toLowerCase().equals(x.toolname + ".exe"))
162 return x;
163 }
164 return null;
165 }
166
167 // Check whether 'flag' appears in 'line' as a word of itself. It must not
168 // be a substring of a word, as then similar flags might be matched.
169 // E.g.: --help matches in the documentation of --help-extra.
170 // This works only with english locale, as some tools have translated
171 // usage messages.
172 static boolean findFlagInLine(String line, String flag) {
173 if (line.contains(flag) &&
174 !line.contains("nknown") && // Some tools say 'Unknown option "<flag>"',
175 !line.contains("invalid flag") && // 'invalid flag: <flag>'
176 !line.contains("invalid option") && // or 'invalid option: <flag>'. Skip that.
177 !line.contains("FileNotFoundException: -help") && // Special case for idlj.
178 !line.contains("-h requires an argument") && // Special case for javac.
179 !line.contains("port argument,")) { // Special case for rmiregistry.
180 // There might be several appearances of 'flag' in
181 // 'line'. (-h as substring of --help).
182 int flagLen = flag.length();
183 int lineLen = line.length();
184 for (int i = line.indexOf(flag); i >= 0; i = line.indexOf(flag, i+1)) {
185 // There should be a space before 'flag' in 'line', or it's right at the beginning.
186 if (i > 0 &&
187 line.charAt(i-1) != ' ' &&
188 line.charAt(i-1) != '[' && // jarsigner
189 line.charAt(i-1) != '|' && // jstatd
190 line.charAt(i-1) != '\t') { // jjs
191 continue;
192 }
193 // There should be a space or comma after 'flag' in 'line', or it's just at the end.
194 int posAfter = i + flagLen;
195 if (posAfter < lineLen &&
196 line.charAt(posAfter) != ' ' &&
197 line.charAt(posAfter) != ',' &&
198 line.charAt(posAfter) != '[' && // jar
199 line.charAt(posAfter) != ']' && // jarsigner
200 line.charAt(posAfter) != ')' && // jfr
201 line.charAt(posAfter) != '|' && // jstatd
202 line.charAt(posAfter) != ':' && // jps
203 line.charAt(posAfter) != '"') { // keytool
204 continue;
205 }
206 return true;
207 }
208 }
209 return false;
210 }
211
212 static TestResult runToolWithFlag(File f, String flag) {
213 String x = f.getAbsolutePath();
214 TestResult tr = doExec(x, flag);
215 System.out.println("Testing " + f.getName());
216 System.out.println("#> " + x + " " + flag);
217 tr.testOutput.forEach(System.out::println);
218 System.out.println("#> echo $?");
219 System.out.println(tr.exitValue);
220
221 return tr;
222 }
223
224 // Checks whether tool supports flag 'flag' and documents it
225 // in the help message.
226 static String testTool(File f, String flag, int exitcode) {
227 String result = "";
228 TestResult tr = runToolWithFlag(f, flag);
229
230 // Check that the tool accepted the flag.
231 if (exitcode == 0 && !tr.isOK()) {
232 System.out.println("failed");
233 result = "failed: " + f.getName() + " " + flag + " has exit code " + tr.exitValue + ".\n";
234 }
235
236 // Check there is a help message listing the flag.
237 boolean foundFlag = false;
238 for (String y : tr.testOutput) {
239 if (!foundFlag && findFlagInLine(y, flag)) { // javac
240 foundFlag = true;
241 System.out.println("Found documentation of '" + flag + "': '" + y.trim() +"'");
242 }
243 }
244 if (!foundFlag) {
245 result += "failed: " + f.getName() + " does not document " +
246 flag + " in help message.\n";
247 }
248
249 if (!result.isEmpty())
250 System.out.println(result);
251
252 return result;
253 }
254
255 // Test the tool supports legacy option -help, but does
256 // not document it.
257 static String testLegacyFlag(File f, int exitcode) {
258 String result = "";
259 TestResult tr = runToolWithFlag(f, "-help");
260
261 // Check that the tool accepted the flag.
262 if (exitcode == 0 && !tr.isOK()) {
263 System.out.println("failed");
264 result = "failed: " + f.getName() + " -help has exit code " + tr.exitValue + ".\n";
265 }
266
267 // Check there is _no_ documentation of -help.
268 boolean foundFlag = false;
269 for (String y : tr.testOutput) {
270 if (!foundFlag && findFlagInLine(y, "-help")) { // javac
271 foundFlag = true;
272 System.out.println("Found documentation of '-help': '" + y.trim() +"'");
273 }
274 }
275 if (foundFlag) {
276 result += "failed: " + f.getName() + " does document -help " +
277 "in help message. This legacy flag should not be documented.\n";
278 }
279
280 if (!result.isEmpty())
281 System.out.println(result);
282
283 return result;
284 }
285
286 // Test that the tool exits with the exit code expected for
287 // invalid flags. In general, one would expect this to be != 0,
288 // but currently a row of tools exit with 0 in this case.
289 // The output should not ask to get help with flag '-help'.
290 static String testInvalidFlag(File f, String flag, int exitcode, boolean documentsLegacyHelp) {
291 String result = "";
292 TestResult tr = runToolWithFlag(f, flag);
293
294 // Check that the tool did exit with the expected return code.
295 if (!((exitcode == tr.exitValue) ||
296 // Windows reports -1 where unix reports 255.
297 (tr.exitValue < 0 && exitcode == tr.exitValue + 256))) {
298 System.out.println("failed");
299 result = "failed: " + f.getName() + " " + flag + " should not be " +
300 "accepted. But it has exit code " + tr.exitValue + ".\n";
301 }
302
303 if (!documentsLegacyHelp) {
304 // Check there is _no_ documentation of -help.
305 boolean foundFlag = false;
306 for (String y : tr.testOutput) {
307 if (!foundFlag && findFlagInLine(y, "-help")) { // javac
308 foundFlag = true;
309 System.out.println("Found documentation of '-help': '" + y.trim() +"'");
310 }
311 }
312 if (foundFlag) {
313 result += "failed: " + f.getName() + " does document -help " +
314 "in error message. This legacy flag should not be documented.\n";
315 }
316 }
317
318 if (!result.isEmpty())
319 System.out.println(result);
320
321 return result;
322 }
323
324 public static void main(String[] args) {
325 String errorMessage = "";
326
327 // The test analyses the help messages printed. It assumes englisch
328 // help messages. Thus it only works with english locale.
329 if (!isEnglishLocale()) { return; }
330
331 for (File f : new File(JAVA_BIN).listFiles(new ToolFilter(TOOLS_NOT_TO_TEST))) {
332 String toolName = f.getName();
333
334 ToolHelpSpec tool = getToolHelpSpec(toolName);
335 if (tool == null) {
336 errorMessage += "Tool " + toolName + " not covered by this test. " +
337 "Add specification to jdkTools array!\n";
338 continue;
339 }
340
341 // Test for help flags to be supported.
342 if (tool.supportsQuestionMark == true) {
343 errorMessage += testTool(f, "-?", tool.exitcodeOfHelp);
344 } else {
345 System.out.println("Skip " + tool.toolname + ". It does not support -?.");
346 }
347 if (tool.supportsH == true) {
348 errorMessage += testTool(f, "-h", tool.exitcodeOfHelp);
349 } else {
350 System.out.println("Skip " + tool.toolname + ". It does not support -h.");
351 }
352 if (tool.supportsHelp == true) {
353 errorMessage += testTool(f, "--help", tool.exitcodeOfHelp);
354 } else {
355 System.out.println("Skip " + tool.toolname + ". It does not support --help.");
356 }
357
358 // Check that the return code listing in jdkTools[] is
359 // correct for an invalid flag.
360 errorMessage += testInvalidFlag(f, "-asdfxgr", tool.exitcodeOfWrongFlag, tool.documentsLegacyHelp);
361
362 // Test for legacy -help flag.
363 if (!tool.documentsLegacyHelp) {
364 if (tool.supportsLegacyHelp == true) {
365 errorMessage += testLegacyFlag(f, tool.exitcodeOfHelp);
366 } else {
367 errorMessage += testInvalidFlag(f, "-help", tool.exitcodeOfWrongFlag, false);
368 }
369 }
370 }
371
372 if (errorMessage.isEmpty()) {
373 System.out.println("All help string tests: PASS");
374 } else {
375 throw new AssertionError("HelpFlagsTest failed:\n" + errorMessage);
376 }
377 }
378 }