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