1 /* 2 * Copyright (c) 2014, 2015, 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 package jdk.test.lib.cli; 25 26 import java.util.List; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.function.BooleanSupplier; 30 31 import jdk.test.lib.*; 32 33 /** 34 * Base class for command line option tests. 35 */ 36 public abstract class CommandLineOptionTest { 37 public static final String UNLOCK_DIAGNOSTIC_VM_OPTIONS 38 = "-XX:+UnlockDiagnosticVMOptions"; 39 public static final String UNLOCK_EXPERIMENTAL_VM_OPTIONS 40 = "-XX:+UnlockExperimentalVMOptions"; 41 protected static final String UNRECOGNIZED_OPTION_ERROR_FORMAT 42 = "Unrecognized VM option '[+-]?%s(=.*)?'"; 43 protected static final String EXPERIMENTAL_OPTION_ERROR_FORMAT 44 = "VM option '%s' is experimental and must be enabled via " 45 + "-XX:\\+UnlockExperimentalVMOptions."; 46 protected static final String DIAGNOSTIC_OPTION_ERROR_FORMAT 47 = " VM option '%s' is diagnostic and must be enabled via " 48 + "-XX:\\+UnlockDiagnosticVMOptions."; 49 private static final String PRINT_FLAGS_FINAL_FORMAT = "%s\\s*:?=\\s*%s"; 50 51 /** 52 * Verifies that JVM startup behavior matches our expectations. 53 * 54 * @param option an option that should be passed to JVM 55 * @param expectedMessages an array of patterns that should occur 56 * in JVM output. If {@code null} then 57 * JVM output could be empty. 58 * @param unexpectedMessages an array of patterns that should not 59 * occur in JVM output. If {@code null} then 60 * JVM output could be empty. 61 * @param exitErrorMessage message that will be shown if exit code is not 62 * as expected. 63 * @param wrongWarningMessage message that will be shown if warning 64 * messages are not as expected. 65 * @param exitCode expected exit code. 66 * @throws Throwable if verification fails or some other issues occur. 67 */ 68 public static void verifyJVMStartup(String option, 69 String expectedMessages[], String unexpectedMessages[], 70 String exitErrorMessage, String wrongWarningMessage, 71 ExitCode exitCode) throws Throwable { 72 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 73 unexpectedMessages, exitErrorMessage, 74 wrongWarningMessage, exitCode, false, option); 75 } 76 77 /** 78 * Verifies that JVM startup behavior matches our expectations. 79 * 80 * @param expectedMessages an array of patterns that should occur 81 * in JVM output. If {@code null} then 82 * JVM output could be empty. 83 * @param unexpectedMessages an array of patterns that should not 84 * occur in JVM output. If {@code null} then 85 * JVM output could be empty. 86 * @param exitErrorMessage message that will be shown if exit code is not 87 * as expected. 88 * @param wrongWarningMessage message that will be shown if warning 89 * messages are not as expected. 90 * @param exitCode expected exit code. 91 * @param addTestVMOptions if {@code true} then test VM options will be 92 * passed to VM. 93 * @param options options that should be passed to VM in addition to mode 94 * flag. 95 * @throws Throwable if verification fails or some other issues occur. 96 */ 97 public static void verifyJVMStartup(String expectedMessages[], 98 String unexpectedMessages[], String exitErrorMessage, 99 String wrongWarningMessage, ExitCode exitCode, 100 boolean addTestVMOptions, String... options) 101 throws Throwable { 102 List<String> finalOptions = new ArrayList<>(); 103 if (addTestVMOptions) { 104 Collections.addAll(finalOptions, Utils.getTestJavaOpts()); 105 } 106 Collections.addAll(finalOptions, options); 107 finalOptions.add("-version"); 108 109 ProcessBuilder processBuilder 110 = ProcessTools.createJavaProcessBuilder(finalOptions.toArray( 111 new String[finalOptions.size()])); 112 OutputAnalyzer outputAnalyzer 113 = new OutputAnalyzer(processBuilder.start()); 114 115 try { 116 outputAnalyzer.shouldHaveExitValue(exitCode.value); 117 } catch (RuntimeException e) { 118 String errorMessage = String.format( 119 "JVM process should have exit value '%d'.%n%s", 120 exitCode.value, exitErrorMessage); 121 throw new AssertionError(errorMessage, e); 122 } 123 124 125 if (expectedMessages != null) { 126 for (String expectedMessage : expectedMessages) { 127 try { 128 outputAnalyzer.shouldMatch(expectedMessage); 129 } catch (RuntimeException e) { 130 String errorMessage = String.format( 131 "Expected message not found: '%s'.%n%s", 132 expectedMessage, wrongWarningMessage); 133 throw new AssertionError(errorMessage, e); 134 } 135 } 136 } 137 138 if (unexpectedMessages != null) { 139 for (String unexpectedMessage : unexpectedMessages) { 140 try { 141 outputAnalyzer.shouldNotMatch(unexpectedMessage); 142 } catch (RuntimeException e) { 143 String errorMessage = String.format( 144 "Unexpected message found: '%s'.%n%s", 145 unexpectedMessage, wrongWarningMessage); 146 throw new AssertionError(errorMessage, e); 147 } 148 } 149 } 150 } 151 152 /** 153 * Verifies that JVM startup behavior matches our expectations when type 154 * of newly started VM is the same as the type of current. 155 * 156 * @param expectedMessages an array of patterns that should occur 157 * in JVM output. If {@code null} then 158 * JVM output could be empty. 159 * @param unexpectedMessages an array of patterns that should not 160 * occur in JVM output. If {@code null} then 161 * JVM output could be empty. 162 * @param exitErrorMessage Message that will be shown if exit value is not 163 * as expected. 164 * @param wrongWarningMessage message that will be shown if warning 165 * messages are not as expected. 166 * @param exitCode expected exit code. 167 * @param options options that should be passed to VM in addition to mode 168 * flag. 169 * @throws Throwable if verification fails or some other issues occur. 170 */ 171 public static void verifySameJVMStartup(String expectedMessages[], 172 String unexpectedMessages[], String exitErrorMessage, 173 String wrongWarningMessage, ExitCode exitCode, String... options) 174 throws Throwable { 175 List<String> finalOptions = new ArrayList<>(); 176 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 177 Collections.addAll(finalOptions, options); 178 179 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 180 unexpectedMessages, exitErrorMessage, 181 wrongWarningMessage, exitCode, false, 182 finalOptions.toArray(new String[finalOptions.size()])); 183 } 184 185 /** 186 * Verifies that value of specified JVM option is the same as 187 * expected value. 188 * This method filter out option with {@code optionName} 189 * name from test java options. 190 * 191 * @param optionName a name of tested option. 192 * @param expectedValue expected value of tested option. 193 * @param optionErrorString message will be shown if option value is not as 194 * expected. 195 * @param additionalVMOpts additional options that should be 196 * passed to JVM. 197 * @throws Throwable if verification fails or some other issues occur. 198 */ 199 public static void verifyOptionValue(String optionName, 200 String expectedValue, String optionErrorString, 201 String... additionalVMOpts) throws Throwable { 202 verifyOptionValue(optionName, expectedValue, optionErrorString, 203 true, additionalVMOpts); 204 } 205 206 /** 207 * Verifies that value of specified JVM option is the same as 208 * expected value. 209 * This method filter out option with {@code optionName} 210 * name from test java options. 211 * 212 * @param optionName a name of tested option. 213 * @param expectedValue expected value of tested option. 214 * @param addTestVmOptions if {@code true}, then test VM options 215 * will be used. 216 * @param optionErrorString message will be shown if option value is not as 217 * expected. 218 * @param additionalVMOpts additional options that should be 219 * passed to JVM. 220 * @throws Throwable if verification fails or some other issues 221 * occur. 222 */ 223 public static void verifyOptionValue(String optionName, 224 String expectedValue, String optionErrorString, 225 boolean addTestVmOptions, String... additionalVMOpts) 226 throws Throwable { 227 List<String> vmOpts = new ArrayList<>(); 228 229 if (addTestVmOptions) { 230 Collections.addAll(vmOpts, 231 Utils.getFilteredTestJavaOpts(optionName)); 232 } 233 Collections.addAll(vmOpts, additionalVMOpts); 234 Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); 235 236 ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( 237 vmOpts.toArray(new String[vmOpts.size()])); 238 239 OutputAnalyzer outputAnalyzer 240 = new OutputAnalyzer(processBuilder.start()); 241 242 try { 243 outputAnalyzer.shouldHaveExitValue(0); 244 } catch (RuntimeException e) { 245 String errorMessage = String.format( 246 "JVM should start with option '%s' without errors.", 247 optionName); 248 throw new AssertionError(errorMessage, e); 249 } 250 try { 251 outputAnalyzer.shouldMatch(String.format( 252 CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, 253 optionName, expectedValue)); 254 } catch (RuntimeException e) { 255 String errorMessage = String.format( 256 "Option '%s' is expected to have '%s' value%n%s", 257 optionName, expectedValue, 258 optionErrorString); 259 throw new AssertionError(errorMessage, e); 260 } 261 } 262 263 /** 264 * Verifies that value of specified JVM when type of newly started VM 265 * is the same as the type of current. 266 * This method filter out option with {@code optionName} 267 * name from test java options. 268 * Only mode flag will be passed to VM in addition to 269 * {@code additionalVMOpts} 270 * 271 * @param optionName name of tested option. 272 * @param expectedValue expected value of tested option. 273 * @param optionErrorString message to show if option has another value 274 * @param additionalVMOpts additional options that should be 275 * passed to JVM. 276 * @throws Throwable if verification fails or some other issues occur. 277 */ 278 public static void verifyOptionValueForSameVM(String optionName, 279 String expectedValue, String optionErrorString, 280 String... additionalVMOpts) throws Throwable { 281 List<String> finalOptions = new ArrayList<>(); 282 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 283 Collections.addAll(finalOptions, additionalVMOpts); 284 285 CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, 286 optionErrorString, false, 287 finalOptions.toArray(new String[finalOptions.size()])); 288 } 289 290 /** 291 * Prepares boolean command line flag with name {@code name} according 292 * to it's {@code value}. 293 * 294 * @param name the name of option to be prepared 295 * @param value the value of option 296 * @return prepared command line flag 297 */ 298 public static String prepareBooleanFlag(String name, boolean value) { 299 return String.format("-XX:%c%s", (value ? '+' : '-'), name); 300 } 301 302 /** 303 * Prepares numeric command line flag with name {@code name} by setting 304 * it's value to {@code value}. 305 * 306 * @param name the name of option to be prepared 307 * @param value the value of option 308 * @return prepared command line flag 309 */ 310 public static String prepareNumericFlag(String name, Number value) { 311 return String.format("-XX:%s=%s", name, value.toString()); 312 } 313 314 /** 315 * Returns message that should occur in VM output if option 316 * {@code optionName} if unrecognized. 317 * 318 * @param optionName the name of option for which message should be returned 319 * @return message saying that option {@code optionName} is unrecognized 320 */ 321 public static String getUnrecognizedOptionErrorMessage(String optionName) { 322 return String.format( 323 CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT, 324 optionName); 325 } 326 327 /** 328 * Returns message that should occur in VM output if option 329 * {@code optionName} is experimental and 330 * -XX:+UnlockExperimentalVMOptions was not passed to VM. 331 * 332 * @param optionName the name of option for which message should be returned 333 * @return message saying that option {@code optionName} is experimental 334 */ 335 public static String getExperimentalOptionErrorMessage(String optionName) { 336 return String.format( 337 CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT, 338 optionName); 339 } 340 341 /** 342 * Returns message that should occur in VM output if option 343 * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions 344 * was not passed to VM. 345 * 346 * @param optionName the name of option for which message should be returned 347 * @return message saying that option {@code optionName} is diganostic 348 */ 349 public static String getDiagnosticOptionErrorMessage(String optionName) { 350 return String.format( 351 CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT, 352 optionName); 353 } 354 355 /** 356 * @return option required to start a new VM with the same type as current. 357 * @throws RuntimeException when VM type is unknown. 358 */ 359 private static String getVMTypeOption() { 360 if (Platform.isServer()) { 361 return "-server"; 362 } else if (Platform.isClient()) { 363 return "-client"; 364 } else if (Platform.isMinimal()) { 365 return "-minimal"; 366 } else if (Platform.isGraal()) { 367 return "-graal"; 368 } 369 throw new RuntimeException("Unknown VM mode."); 370 } 371 372 private final BooleanSupplier predicate; 373 374 /** 375 * Constructs new CommandLineOptionTest that will be executed only if 376 * predicate {@code predicate} return {@code true}. 377 * @param predicate a predicate responsible for test's preconditions check. 378 */ 379 public CommandLineOptionTest(BooleanSupplier predicate) { 380 this.predicate = predicate; 381 } 382 383 /** 384 * Runs command line option test. 385 */ 386 public final void test() throws Throwable { 387 if (predicate.getAsBoolean()) { 388 runTestCases(); 389 } 390 } 391 392 /** 393 * @throws Throwable if some issue happened during test cases execution. 394 */ 395 protected abstract void runTestCases() throws Throwable; 396 }