CommandLineOptionTest.java revision 2508:1dc5f0ee3445
1/* 2 * Copyright (c) 2014, 2016, 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 24package jdk.test.lib.cli; 25 26import java.util.List; 27import java.util.ArrayList; 28import java.util.Collections; 29import java.util.function.BooleanSupplier; 30 31import jdk.test.lib.process.ExitCode; 32import jdk.test.lib.process.ProcessTools; 33import jdk.test.lib.process.OutputAnalyzer; 34import jdk.test.lib.Platform; 35import jdk.test.lib.Utils; 36 37/** 38 * Base class for command line option tests. 39 */ 40public abstract class CommandLineOptionTest { 41 public static final String UNLOCK_DIAGNOSTIC_VM_OPTIONS 42 = "-XX:+UnlockDiagnosticVMOptions"; 43 public static final String UNLOCK_EXPERIMENTAL_VM_OPTIONS 44 = "-XX:+UnlockExperimentalVMOptions"; 45 protected static final String UNRECOGNIZED_OPTION_ERROR_FORMAT 46 = "Unrecognized VM option '[+-]?%s(=.*)?'"; 47 protected static final String EXPERIMENTAL_OPTION_ERROR_FORMAT 48 = "VM option '%s' is experimental and must be enabled via " 49 + "-XX:\\+UnlockExperimentalVMOptions."; 50 protected static final String DIAGNOSTIC_OPTION_ERROR_FORMAT 51 = " VM option '%s' is diagnostic and must be enabled via " 52 + "-XX:\\+UnlockDiagnosticVMOptions."; 53 private static final String PRINT_FLAGS_FINAL_FORMAT = "%s\\s*:?=\\s*%s"; 54 55 /** 56 * Verifies that JVM startup behavior matches our expectations. 57 * 58 * @param option an option that should be passed to JVM 59 * @param expectedMessages an array of patterns that should occur 60 * in JVM output. If {@code null} then 61 * JVM output could be empty. 62 * @param unexpectedMessages an array of patterns that should not 63 * occur in JVM output. If {@code null} then 64 * JVM output could be empty. 65 * @param exitErrorMessage message that will be shown if exit code is not 66 * as expected. 67 * @param wrongWarningMessage message that will be shown if warning 68 * messages are not as expected. 69 * @param exitCode expected exit code. 70 * @throws Throwable if verification fails or some other issues occur. 71 */ 72 public static void verifyJVMStartup(String option, 73 String expectedMessages[], String unexpectedMessages[], 74 String exitErrorMessage, String wrongWarningMessage, 75 ExitCode exitCode) throws Throwable { 76 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 77 unexpectedMessages, exitErrorMessage, 78 wrongWarningMessage, exitCode, false, option); 79 } 80 81 /** 82 * Verifies that JVM startup behavior matches our expectations. 83 * 84 * @param expectedMessages an array of patterns that should occur 85 * in JVM output. If {@code null} then 86 * JVM output could be empty. 87 * @param unexpectedMessages an array of patterns that should not 88 * occur in JVM output. If {@code null} then 89 * JVM output could be empty. 90 * @param exitErrorMessage message that will be shown if exit code is not 91 * as expected. 92 * @param wrongWarningMessage message that will be shown if warning 93 * messages are not as expected. 94 * @param exitCode expected exit code. 95 * @param addTestVMOptions if {@code true} then test VM options will be 96 * passed to VM. 97 * @param options options that should be passed to VM in addition to mode 98 * flag. 99 * @throws Throwable if verification fails or some other issues occur. 100 */ 101 public static void verifyJVMStartup(String expectedMessages[], 102 String unexpectedMessages[], String exitErrorMessage, 103 String wrongWarningMessage, ExitCode exitCode, 104 boolean addTestVMOptions, String... options) 105 throws Throwable { 106 List<String> finalOptions = new ArrayList<>(); 107 if (addTestVMOptions) { 108 Collections.addAll(finalOptions, ProcessTools.getVmInputArgs()); 109 Collections.addAll(finalOptions, Utils.getTestJavaOpts()); 110 } 111 Collections.addAll(finalOptions, options); 112 finalOptions.add("-version"); 113 114 ProcessBuilder processBuilder 115 = ProcessTools.createJavaProcessBuilder(finalOptions.toArray( 116 new String[finalOptions.size()])); 117 OutputAnalyzer outputAnalyzer 118 = new OutputAnalyzer(processBuilder.start()); 119 120 try { 121 outputAnalyzer.shouldHaveExitValue(exitCode.value); 122 } catch (RuntimeException e) { 123 String errorMessage = String.format( 124 "JVM process should have exit value '%d'.%n%s", 125 exitCode.value, exitErrorMessage); 126 throw new AssertionError(errorMessage, e); 127 } 128 129 verifyOutput(expectedMessages, unexpectedMessages, 130 wrongWarningMessage, outputAnalyzer); 131 } 132 133 /** 134 * Verifies that JVM startup behavior matches our expectations. 135 * 136 * @param expectedMessages an array of patterns that should occur in JVM 137 * output. If {@code null} then 138 * JVM output could be empty. 139 * @param unexpectedMessages an array of patterns that should not occur 140 * in JVM output. If {@code null} then 141 * JVM output could be empty. 142 * @param wrongWarningMessage message that will be shown if messages are 143 * not as expected. 144 * @param outputAnalyzer OutputAnalyzer instance 145 * @throws AssertionError if verification fails. 146 */ 147 public static void verifyOutput(String[] expectedMessages, 148 String[] unexpectedMessages, String wrongWarningMessage, 149 OutputAnalyzer outputAnalyzer) { 150 if (expectedMessages != null) { 151 for (String expectedMessage : expectedMessages) { 152 try { 153 outputAnalyzer.shouldMatch(expectedMessage); 154 } catch (RuntimeException e) { 155 String errorMessage = String.format( 156 "Expected message not found: '%s'.%n%s", 157 expectedMessage, wrongWarningMessage); 158 throw new AssertionError(errorMessage, e); 159 } 160 } 161 } 162 163 if (unexpectedMessages != null) { 164 for (String unexpectedMessage : unexpectedMessages) { 165 try { 166 outputAnalyzer.shouldNotMatch(unexpectedMessage); 167 } catch (RuntimeException e) { 168 String errorMessage = String.format( 169 "Unexpected message found: '%s'.%n%s", 170 unexpectedMessage, wrongWarningMessage); 171 throw new AssertionError(errorMessage, e); 172 } 173 } 174 } 175 } 176 177 /** 178 * Verifies that JVM startup behavior matches our expectations when type 179 * of newly started VM is the same as the type of current. 180 * 181 * @param expectedMessages an array of patterns that should occur 182 * in JVM output. If {@code null} then 183 * JVM output could be empty. 184 * @param unexpectedMessages an array of patterns that should not 185 * occur in JVM output. If {@code null} then 186 * JVM output could be empty. 187 * @param exitErrorMessage Message that will be shown if exit value is not 188 * as expected. 189 * @param wrongWarningMessage message that will be shown if warning 190 * messages are not as expected. 191 * @param exitCode expected exit code. 192 * @param options options that should be passed to VM in addition to mode 193 * flag. 194 * @throws Throwable if verification fails or some other issues occur. 195 */ 196 public static void verifySameJVMStartup(String expectedMessages[], 197 String unexpectedMessages[], String exitErrorMessage, 198 String wrongWarningMessage, ExitCode exitCode, String... options) 199 throws Throwable { 200 List<String> finalOptions = new ArrayList<>(); 201 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 202 String extraFlagForEmulated = CommandLineOptionTest.getVMTypeOptionForEmulated(); 203 if (extraFlagForEmulated != null) { 204 finalOptions.add(extraFlagForEmulated); 205 } 206 Collections.addAll(finalOptions, options); 207 208 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 209 unexpectedMessages, exitErrorMessage, 210 wrongWarningMessage, exitCode, false, 211 finalOptions.toArray(new String[finalOptions.size()])); 212 } 213 214 /** 215 * Verifies that value of specified JVM option is the same as 216 * expected value. 217 * This method filter out option with {@code optionName} 218 * name from test java options. 219 * 220 * @param optionName a name of tested option. 221 * @param expectedValue expected value of tested option. 222 * @param optionErrorString message will be shown if option value is not as 223 * expected. 224 * @param additionalVMOpts additional options that should be 225 * passed to JVM. 226 * @throws Throwable if verification fails or some other issues occur. 227 */ 228 public static void verifyOptionValue(String optionName, 229 String expectedValue, String optionErrorString, 230 String... additionalVMOpts) throws Throwable { 231 verifyOptionValue(optionName, expectedValue, optionErrorString, 232 true, additionalVMOpts); 233 } 234 235 /** 236 * Verifies that value of specified JVM option is the same as 237 * expected value. 238 * This method filter out option with {@code optionName} 239 * name from test java options. 240 * 241 * @param optionName a name of tested option. 242 * @param expectedValue expected value of tested option. 243 * @param addTestVmOptions if {@code true}, then test VM options 244 * will be used. 245 * @param optionErrorString message will be shown if option value is not as 246 * expected. 247 * @param additionalVMOpts additional options that should be 248 * passed to JVM. 249 * @throws Throwable if verification fails or some other issues 250 * occur. 251 */ 252 public static void verifyOptionValue(String optionName, 253 String expectedValue, String optionErrorString, 254 boolean addTestVmOptions, String... additionalVMOpts) 255 throws Throwable { 256 List<String> vmOpts = new ArrayList<>(); 257 258 if (addTestVmOptions) { 259 Collections.addAll(vmOpts, 260 Utils.getFilteredTestJavaOpts(optionName)); 261 } 262 Collections.addAll(vmOpts, additionalVMOpts); 263 Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); 264 265 ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( 266 vmOpts.toArray(new String[vmOpts.size()])); 267 268 OutputAnalyzer outputAnalyzer 269 = new OutputAnalyzer(processBuilder.start()); 270 271 try { 272 outputAnalyzer.shouldHaveExitValue(0); 273 } catch (RuntimeException e) { 274 String errorMessage = String.format( 275 "JVM should start with option '%s' without errors.", 276 optionName); 277 throw new AssertionError(errorMessage, e); 278 } 279 verifyOptionValue(optionName, expectedValue, optionErrorString, 280 outputAnalyzer); 281 } 282 283 /** 284 * Verifies that value of specified JVM option is the same as 285 * expected value. 286 * 287 * @param optionName a name of tested option. 288 * @param expectedValue expected value of tested option. 289 * @param optionErrorString message will be shown if option value is not 290 * as expected. 291 * @param outputAnalyzer OutputAnalyzer instance 292 * @throws AssertionError if verification fails 293 */ 294 public static void verifyOptionValue(String optionName, 295 String expectedValue, String optionErrorString, 296 OutputAnalyzer outputAnalyzer) { 297 try { 298 outputAnalyzer.shouldMatch(String.format( 299 CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, 300 optionName, expectedValue)); 301 } catch (RuntimeException e) { 302 String errorMessage = String.format( 303 "Option '%s' is expected to have '%s' value%n%s", 304 optionName, expectedValue, 305 optionErrorString); 306 throw new AssertionError(errorMessage, e); 307 } 308 } 309 310 /** 311 * Start VM with given options and values. 312 * Generates command line option flags from 313 * {@code optionNames} and {@code optionValues}. 314 * 315 * @param optionNames names of options to pass in 316 * @param optionValues values of option 317 * @param additionalVMOpts additional options that should be 318 * passed to JVM. 319 * @return output from vm process 320 */ 321 public static OutputAnalyzer startVMWithOptions(String[] optionNames, 322 String[] optionValues, 323 String... additionalVMOpts) throws Throwable { 324 List<String> vmOpts = new ArrayList<>(); 325 if (optionNames == null || optionValues == null || optionNames.length != optionValues.length) { 326 throw new IllegalArgumentException("optionNames and/or optionValues"); 327 } 328 329 for (int i = 0; i < optionNames.length; i++) { 330 vmOpts.add(prepareFlag(optionNames[i], optionValues[i])); 331 } 332 Collections.addAll(vmOpts, additionalVMOpts); 333 Collections.addAll(vmOpts, "-version"); 334 335 ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( 336 vmOpts.toArray(new String[vmOpts.size()])); 337 338 return new OutputAnalyzer(processBuilder.start()); 339 } 340 341 /** 342 * Verifies from the output that values of specified JVM options were the same as 343 * expected values. 344 * 345 * @param outputAnalyzer search output for expect options and values. 346 * @param optionNames names of tested options. 347 * @param expectedValues expected values of tested options. 348 * @throws Throwable if verification fails or some other issues occur. 349 */ 350 public static void verifyOptionValuesFromOutput(OutputAnalyzer outputAnalyzer, 351 String[] optionNames, 352 String[] expectedValues) throws Throwable { 353 outputAnalyzer.shouldHaveExitValue(0); 354 for (int i = 0; i < optionNames.length; i++) { 355 outputAnalyzer.shouldMatch(String.format( 356 CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, 357 optionNames[i], expectedValues[i])); 358 } 359 } 360 361 /** 362 * Verifies that value of specified JVM options are the same as 363 * expected values. 364 * Generates command line option flags from 365 * {@code optionNames} and {@code expectedValues}. 366 * 367 * @param optionNames names of tested options. 368 * @param expectedValues expected values of tested options. 369 * @throws Throwable if verification fails or some other issues occur. 370 */ 371 public static void verifyOptionValues(String[] optionNames, 372 String[] expectedValues) throws Throwable { 373 OutputAnalyzer outputAnalyzer = startVMWithOptions(optionNames, expectedValues, "-XX:+PrintFlagsFinal"); 374 verifyOptionValuesFromOutput(outputAnalyzer, optionNames, expectedValues); 375 } 376 377 /** 378 * Verifies that value of specified JVM when type of newly started VM 379 * is the same as the type of current. 380 * This method filter out option with {@code optionName} 381 * name from test java options. 382 * Only mode flag will be passed to VM in addition to 383 * {@code additionalVMOpts} 384 * 385 * @param optionName name of tested option. 386 * @param expectedValue expected value of tested option. 387 * @param optionErrorString message to show if option has another value 388 * @param additionalVMOpts additional options that should be 389 * passed to JVM. 390 * @throws Throwable if verification fails or some other issues occur. 391 */ 392 public static void verifyOptionValueForSameVM(String optionName, 393 String expectedValue, String optionErrorString, 394 String... additionalVMOpts) throws Throwable { 395 List<String> finalOptions = new ArrayList<>(); 396 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 397 String extraFlagForEmulated = CommandLineOptionTest.getVMTypeOptionForEmulated(); 398 if (extraFlagForEmulated != null) { 399 finalOptions.add(extraFlagForEmulated); 400 } 401 Collections.addAll(finalOptions, additionalVMOpts); 402 403 CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, 404 optionErrorString, false, 405 finalOptions.toArray(new String[finalOptions.size()])); 406 } 407 408 /** 409 * Prepares boolean command line flag with name {@code name} according 410 * to it's {@code value}. 411 * 412 * @param name the name of option to be prepared 413 * @param value the value of option 414 * @return prepared command line flag 415 */ 416 public static String prepareBooleanFlag(String name, boolean value) { 417 return String.format("-XX:%c%s", (value ? '+' : '-'), name); 418 } 419 420 /** 421 * Prepares numeric command line flag with name {@code name} by setting 422 * it's value to {@code value}. 423 * 424 * @param name the name of option to be prepared 425 * @param value the value of option 426 * @return prepared command line flag 427 */ 428 public static String prepareNumericFlag(String name, Number value) { 429 return String.format("-XX:%s=%s", name, value.toString()); 430 } 431 432 /** 433 * Prepares generic command line flag with name {@code name} by setting 434 * it's value to {@code value}. 435 * 436 * @param name the name of option to be prepared 437 * @param value the value of option ("+" or "-" can be used instead of "true" or "false") 438 * @return prepared command line flag 439 */ 440 public static String prepareFlag(String name, String value) { 441 if (value.equals("+") || value.equalsIgnoreCase("true")) { 442 return "-XX:+" + name; 443 } else if (value.equals("-") || value.equalsIgnoreCase("false")) { 444 return "-XX:-" + name; 445 } else { 446 return "-XX:" + name + "=" + value; 447 } 448 } 449 450 /** 451 * Returns message that should occur in VM output if option 452 * {@code optionName} if unrecognized. 453 * 454 * @param optionName the name of option for which message should be returned 455 * @return message saying that option {@code optionName} is unrecognized 456 */ 457 public static String getUnrecognizedOptionErrorMessage(String optionName) { 458 return String.format( 459 CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT, 460 optionName); 461 } 462 463 /** 464 * Returns message that should occur in VM output if option 465 * {@code optionName} is experimental and 466 * -XX:+UnlockExperimentalVMOptions was not passed to VM. 467 * 468 * @param optionName the name of option for which message should be returned 469 * @return message saying that option {@code optionName} is experimental 470 */ 471 public static String getExperimentalOptionErrorMessage(String optionName) { 472 return String.format( 473 CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT, 474 optionName); 475 } 476 477 /** 478 * Returns message that should occur in VM output if option 479 * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions 480 * was not passed to VM. 481 * 482 * @param optionName the name of option for which message should be returned 483 * @return message saying that option {@code optionName} is diganostic 484 */ 485 public static String getDiagnosticOptionErrorMessage(String optionName) { 486 return String.format( 487 CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT, 488 optionName); 489 } 490 491 /** 492 * @return option required to start a new VM with the same type as current. 493 * @throws RuntimeException when VM type is unknown. 494 */ 495 private static String getVMTypeOption() { 496 if (Platform.isServer()) { 497 return "-server"; 498 } else if (Platform.isClient()) { 499 return "-client"; 500 } else if (Platform.isMinimal()) { 501 return "-minimal"; 502 } else if (Platform.isGraal()) { 503 return "-graal"; 504 } 505 throw new RuntimeException("Unknown VM mode."); 506 } 507 508 /** 509 * @return addtional VMoptions(Emulated related) required to start a new VM with the same type as current. 510 */ 511 private static String getVMTypeOptionForEmulated() { 512 if (Platform.isServer() && !Platform.isEmulatedClient()) { 513 return "-XX:-NeverActAsServerClassMachine"; 514 } else if (Platform.isEmulatedClient()) { 515 return "-XX:+NeverActAsServerClassMachine"; 516 } 517 return null; 518 } 519 520 private final BooleanSupplier predicate; 521 522 /** 523 * Constructs new CommandLineOptionTest that will be executed only if 524 * predicate {@code predicate} return {@code true}. 525 * @param predicate a predicate responsible for test's preconditions check. 526 */ 527 public CommandLineOptionTest(BooleanSupplier predicate) { 528 this.predicate = predicate; 529 } 530 531 /** 532 * Runs command line option test. 533 */ 534 public final void test() throws Throwable { 535 if (predicate.getAsBoolean()) { 536 runTestCases(); 537 } 538 } 539 540 /** 541 * @throws Throwable if some issue happened during test cases execution. 542 */ 543 protected abstract void runTestCases() throws Throwable; 544} 545