/* * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @summary Validate and test -?, -h and --help flags. All tools in the jdk * should take the same flags to display the help message. These * flags should be documented in the printed help message. The * tool should quit without error code after displaying the * help message (if there is no other problem with the command * line). * Also check that tools that used to accept -help still do * so. Test that tools that never accepted -help don't do so * in future. I.e., check that the tool returns with the same * return code as called with an invalid flag, and does not * print anything containing '-help' in that case. * @compile HelpFlagsTest.java * @run main HelpFlagsTest */ import java.io.File; import java.io.FileFilter; import java.util.Map; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.HashSet; import java.util.Set; public class HelpFlagsTest extends TestHelper { // Tools that should not be tested because a usage message is pointless. static final String[] TOOLS_NOT_TO_TEST = { "appletviewer", // deprecated, don't test "jaccessinspector", // gui, don't test, win only "jaccesswalker", // gui, don't test, win only "jconsole", // gui, don't test "servertool", // none. Shell, don't test. "javaw", // don't test, win only // The flags of these tools need to be fixed in Java EE. // The tools are deprecated for removal in Java SE. Don't test. "idlj", "orbd", "schemagen", "tnameserv", "wsgen", "wsimport", "xjc", // These shall have a help message that resembles that of // MIT's tools. Thus -?, -h and --help are supported, but not // mentioned in the help text. "kinit", "klist", "ktab", // Oracle proprietary tools without help message. "javacpl", "jmc", "jweblauncher", "jcontrol", "ssvagent" }; // Lists which tools support which flags. private static class ToolHelpSpec { String toolname; // How the flags supposed to be supported are handled. // // These flags are supported, i.e., // * the tool accepts the flag // * the tool prints a help message if the flag is specified // * this help message lists the flag // * the tool exits with exit code '0'. boolean supportsQuestionMark; boolean supportsH; boolean supportsHelp; // One tool returns with exit code != '0'. int exitcodeOfHelp; // How legacy -help is handled. // // Tools that so far support -help should still do so, but // not print documentation about it. Tools that do not // support -help should not do so in future. // // The tools accepts legacy -help. -help should not be // documented in the usage message. boolean supportsLegacyHelp; // Java itself documents -help. -help prints to stderr, // while --help prints to stdout. Leave as is. boolean documentsLegacyHelp; // The exit code of the tool if an invalid argument is passed to it. // An exit code != 0 would be expected, but not all tools handle it // that way. int exitcodeOfWrongFlag; ToolHelpSpec(String n, int q, int h, int hp, int ex1, int l, int dl, int ex2) { toolname = n; supportsQuestionMark = ( q == 1 ? true : false ); supportsH = ( h == 1 ? true : false ); supportsHelp = ( hp == 1 ? true : false ); exitcodeOfHelp = ex1; supportsLegacyHelp = ( l == 1 ? true : false ); documentsLegacyHelp = ( dl == 1 ? true : false ); exitcodeOfWrongFlag = ex2; } } static ToolHelpSpec[] jdkTools = { // name -? -h --help exitcode -help -help exitcode // of help docu of wrong // mented flag new ToolHelpSpec("jabswitch", 0, 0, 0, 0, 0, 0, 0), // /?, prints help message anyways, win only new ToolHelpSpec("jaotc", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help new ToolHelpSpec("jar", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help new ToolHelpSpec("jarsigner", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("java", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("javac", 1, 0, 1, 0, 1, 1, 2), // -?, --help -help, Documents -help, -h is already taken for "native header output directory". new ToolHelpSpec("javadoc", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("javap", 1, 1, 1, 0, 1, 1, 2), // -?, -h, --help -help, Documents -help new ToolHelpSpec("javaw", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help, win only new ToolHelpSpec("jcmd", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jdb", 1, 1, 1, 0, 1, 1, 0), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jdeprscan", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help new ToolHelpSpec("jdeps", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jhsdb", 0, 0, 0, 0, 0, 0, 0), // none, prints help message anyways. new ToolHelpSpec("jimage", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help new ToolHelpSpec("jinfo", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jjs", 0, 1, 1, 100, 0, 0, 100), // -h, --help, return code 100 new ToolHelpSpec("jlink", 1, 1, 1, 0, 0, 0, 2), // -?, -h, --help new ToolHelpSpec("jmap", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jmod", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jps", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jrunscript", 1, 1, 1, 0, 1, 1, 7), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jshell", 1, 1, 1, 0, 1, 0, 1), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("jstack", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jstat", 1, 1, 1, 0, 1, 1, 1), // -?, -h, --help -help, Documents -help new ToolHelpSpec("jstatd", 1, 1, 1, 0, 0, 0, 1), // -?, -h, --help new ToolHelpSpec("keytool", 1, 1, 1, 0, 1, 0, 1), // none, prints help message anyways. new ToolHelpSpec("pack200", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. new ToolHelpSpec("rmic", 0, 0, 0, 0, 0, 0, 1), // none, pirnts help message anyways. new ToolHelpSpec("rmid", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. new ToolHelpSpec("rmiregistry", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. new ToolHelpSpec("serialver", 0, 0, 0, 0, 0, 0, 1), // none, prints help message anyways. new ToolHelpSpec("unpack200", 1, 1, 1, 0, 1, 0, 2), // -?, -h, --help, -help accepted but not documented. // Oracle proprietary tools: new ToolHelpSpec("javapackager",0, 0, 0, 0, 1, 0, 255), // -help accepted but not documented. }; // Returns true if the file is not a tool. static boolean notATool(String file) { if (isWindows && !file.endsWith(EXE_FILE_EXT)) return true; return false; } // Returns true if tool is listed in TOOLS_NOT_TO_TEST. static boolean dontTestTool(String tool) { tool = tool.toLowerCase(); for (String x : TOOLS_NOT_TO_TEST) { if (tool.toLowerCase().startsWith(x)) return true; } return false; } // Returns corresponding object from jdkTools array. static ToolHelpSpec getToolHelpSpec(String tool) { for (ToolHelpSpec x : jdkTools) { if (tool.toLowerCase().equals(x.toolname) || tool.toLowerCase().equals(x.toolname + ".exe")) return x; } return null; } // Check whether 'flag' appears in 'line' as a word of itself. It must not // be a substring of a word, as then similar flags might be matched. // E.g.: --help matches in the documentation of --help-extra. // This works only with english locale, as some tools have translated // usage messages. static boolean findFlagInLine(String line, String flag) { if (line.contains(flag) && !line.contains("nknown") && // Some tools say 'Unknown option ""', !line.contains("invalid flag") && // 'invalid flag: ' !line.contains("invalid option") && // or 'invalid option: '. Skip that. !line.contains("FileNotFoundException: -help") && // Special case for idlj. !line.contains("-h requires an argument") && // Special case for javac. !line.contains("port argument,")) { // Special case for rmiregistry. // There might be several appearances of 'flag' in // 'line'. (-h as substring of --help). int flagLen = flag.length(); int lineLen = line.length(); for (int i = line.indexOf(flag); i >= 0; i = line.indexOf(flag, i+1)) { // There should be a space before 'flag' in 'line', or it's right at the beginning. if (i > 0 && line.charAt(i-1) != ' ' && line.charAt(i-1) != '[' && // jarsigner line.charAt(i-1) != '|' && // jstatd line.charAt(i-1) != '\t') { // jjs continue; } // There should be a space or comma after 'flag' in 'line', or it's just at the end. int posAfter = i + flagLen; if (posAfter < lineLen && line.charAt(posAfter) != ' ' && line.charAt(posAfter) != ',' && line.charAt(posAfter) != '[' && // jar line.charAt(posAfter) != ']' && // jarsigner line.charAt(posAfter) != '|' && // jstatd line.charAt(posAfter) != ':' && // jps line.charAt(posAfter) != '"') { // keytool continue; } return true; } } return false; } static TestResult runToolWithFlag(File f, String flag) { String x = f.getAbsolutePath(); TestResult tr = doExec(x, flag); System.out.println("Testing " + f.getName()); System.out.println("#> " + x + " " + flag); tr.testOutput.forEach(System.out::println); System.out.println("#> echo $?"); System.out.println(tr.exitValue); return tr; } // Checks whether tool supports flag 'flag' and documents it // in the help message. static String testTool(File f, String flag, int exitcode) { String result = ""; TestResult tr = runToolWithFlag(f, flag); // Check that the tool accepted the flag. if (exitcode == 0 && !tr.isOK()) { System.out.println("failed"); result = "failed: " + f.getName() + " " + flag + " has exit code " + tr.exitValue + ".\n"; } // Check there is a help message listing the flag. boolean foundFlag = false; for (String y : tr.testOutput) { if (!foundFlag && findFlagInLine(y, flag)) { // javac foundFlag = true; System.out.println("Found documentation of '" + flag + "': '" + y.trim() +"'"); } } if (!foundFlag) { result += "failed: " + f.getName() + " does not document " + flag + " in help message.\n"; } if (!result.isEmpty()) System.out.println(result); return result; } // Test the tool supports legacy option -help, but does // not document it. static String testLegacyFlag(File f, int exitcode) { String result = ""; TestResult tr = runToolWithFlag(f, "-help"); // Check that the tool accepted the flag. if (exitcode == 0 && !tr.isOK()) { System.out.println("failed"); result = "failed: " + f.getName() + " -help has exit code " + tr.exitValue + ".\n"; } // Check there is _no_ documentation of -help. boolean foundFlag = false; for (String y : tr.testOutput) { if (!foundFlag && findFlagInLine(y, "-help")) { // javac foundFlag = true; System.out.println("Found documentation of '-help': '" + y.trim() +"'"); } } if (foundFlag) { result += "failed: " + f.getName() + " does document -help " + "in help message. This legacy flag should not be documented.\n"; } if (!result.isEmpty()) System.out.println(result); return result; } // Test that the tool exits with the exit code expected for // invalid flags. In general, one would expect this to be != 0, // but currently a row of tools exit with 0 in this case. // The output should not ask to get help with flag '-help'. static String testInvalidFlag(File f, String flag, int exitcode, boolean documentsLegacyHelp) { String result = ""; TestResult tr = runToolWithFlag(f, flag); // Check that the tool did exit with the expected return code. if (!((exitcode == tr.exitValue) || // Windows reports -1 where unix reports 255. (tr.exitValue < 0 && exitcode == tr.exitValue + 256))) { System.out.println("failed"); result = "failed: " + f.getName() + " " + flag + " should not be " + "accepted. But it has exit code " + tr.exitValue + ".\n"; } if (!documentsLegacyHelp) { // Check there is _no_ documentation of -help. boolean foundFlag = false; for (String y : tr.testOutput) { if (!foundFlag && findFlagInLine(y, "-help")) { // javac foundFlag = true; System.out.println("Found documentation of '-help': '" + y.trim() +"'"); } } if (foundFlag) { result += "failed: " + f.getName() + " does document -help " + "in error message. This legacy flag should not be documented.\n"; } } if (!result.isEmpty()) System.out.println(result); return result; } public static void main(String[] args) { String errorMessage = ""; // The test analyses the help messages printed. It assumes englisch // help messages. Thus it only works with english locale. if (!isEnglishLocale()) { return; } for (File f : new File(JAVA_BIN).listFiles()) { String toolName = f.getName(); if (notATool(toolName)) { continue; } if (dontTestTool(toolName)) { System.out.println("Skipping test of tool " + toolName + ". Tool has no help message."); continue; } ToolHelpSpec tool = getToolHelpSpec(toolName); if (tool == null) { errorMessage += "Tool " + toolName + " not covered by this test. " + "Add specification to jdkTools array!\n"; continue; } // Test for help flags to be supported. if (tool.supportsQuestionMark == true) { errorMessage += testTool(f, "-?", tool.exitcodeOfHelp); } else { System.out.println("Skip " + tool.toolname + ". It does not support -?."); } if (tool.supportsH == true) { errorMessage += testTool(f, "-h", tool.exitcodeOfHelp); } else { System.out.println("Skip " + tool.toolname + ". It does not support -h."); } if (tool.supportsHelp == true) { errorMessage += testTool(f, "--help", tool.exitcodeOfHelp); } else { System.out.println("Skip " + tool.toolname + ". It does not support --help."); } // Check that the return code listing in jdkTools[] is // correct for an invalid flag. errorMessage += testInvalidFlag(f, "-asdfxgr", tool.exitcodeOfWrongFlag, tool.documentsLegacyHelp); // Test for legacy -help flag. if (!tool.documentsLegacyHelp) { if (tool.supportsLegacyHelp == true) { errorMessage += testLegacyFlag(f, tool.exitcodeOfHelp); } else { errorMessage += testInvalidFlag(f, "-help", tool.exitcodeOfWrongFlag, false); } } } if (errorMessage.isEmpty()) { System.out.println("All help string tests: PASS"); } else { throw new AssertionError("HelpFlagsTest failed:\n" + errorMessage); } } }