CommandLineOptionTest.java revision 2224:2a8815d86b93
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 Collections.addAll(finalOptions, options); 203 204 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 205 unexpectedMessages, exitErrorMessage, 206 wrongWarningMessage, exitCode, false, 207 finalOptions.toArray(new String[finalOptions.size()])); 208 } 209 210 /** 211 * Verifies that value of specified JVM option is the same as 212 * expected value. 213 * This method filter out option with {@code optionName} 214 * name from test java options. 215 * 216 * @param optionName a name of tested option. 217 * @param expectedValue expected value of tested option. 218 * @param optionErrorString message will be shown if option value is not as 219 * expected. 220 * @param additionalVMOpts additional options that should be 221 * passed to JVM. 222 * @throws Throwable if verification fails or some other issues occur. 223 */ 224 public static void verifyOptionValue(String optionName, 225 String expectedValue, String optionErrorString, 226 String... additionalVMOpts) throws Throwable { 227 verifyOptionValue(optionName, expectedValue, optionErrorString, 228 true, additionalVMOpts); 229 } 230 231 /** 232 * Verifies that value of specified JVM option is the same as 233 * expected value. 234 * This method filter out option with {@code optionName} 235 * name from test java options. 236 * 237 * @param optionName a name of tested option. 238 * @param expectedValue expected value of tested option. 239 * @param addTestVmOptions if {@code true}, then test VM options 240 * will be used. 241 * @param optionErrorString message will be shown if option value is not as 242 * expected. 243 * @param additionalVMOpts additional options that should be 244 * passed to JVM. 245 * @throws Throwable if verification fails or some other issues 246 * occur. 247 */ 248 public static void verifyOptionValue(String optionName, 249 String expectedValue, String optionErrorString, 250 boolean addTestVmOptions, String... additionalVMOpts) 251 throws Throwable { 252 List<String> vmOpts = new ArrayList<>(); 253 254 if (addTestVmOptions) { 255 Collections.addAll(vmOpts, 256 Utils.getFilteredTestJavaOpts(optionName)); 257 } 258 Collections.addAll(vmOpts, additionalVMOpts); 259 Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); 260 261 ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( 262 vmOpts.toArray(new String[vmOpts.size()])); 263 264 OutputAnalyzer outputAnalyzer 265 = new OutputAnalyzer(processBuilder.start()); 266 267 try { 268 outputAnalyzer.shouldHaveExitValue(0); 269 } catch (RuntimeException e) { 270 String errorMessage = String.format( 271 "JVM should start with option '%s' without errors.", 272 optionName); 273 throw new AssertionError(errorMessage, e); 274 } 275 verifyOptionValue(optionName, expectedValue, optionErrorString, 276 outputAnalyzer); 277 } 278 279 /** 280 * Verifies that value of specified JVM option is the same as 281 * expected value. 282 * 283 * @param optionName a name of tested option. 284 * @param expectedValue expected value of tested option. 285 * @param optionErrorString message will be shown if option value is not 286 * as expected. 287 * @param outputAnalyzer OutputAnalyzer instance 288 * @throws AssertionError if verification fails 289 */ 290 public static void verifyOptionValue(String optionName, 291 String expectedValue, String optionErrorString, 292 OutputAnalyzer outputAnalyzer) { 293 try { 294 outputAnalyzer.shouldMatch(String.format( 295 CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, 296 optionName, expectedValue)); 297 } catch (RuntimeException e) { 298 String errorMessage = String.format( 299 "Option '%s' is expected to have '%s' value%n%s", 300 optionName, expectedValue, 301 optionErrorString); 302 throw new AssertionError(errorMessage, e); 303 } 304 } 305 306 /** 307 * Start VM with given options and values. 308 * Generates command line option flags from 309 * {@code optionNames} and {@code optionValues}. 310 * 311 * @param optionNames names of options to pass in 312 * @param optionValues values of option 313 * @param additionalVMOpts additional options that should be 314 * passed to JVM. 315 * @return output from vm process 316 */ 317 public static OutputAnalyzer startVMWithOptions(String[] optionNames, 318 String[] optionValues, 319 String... additionalVMOpts) throws Throwable { 320 List<String> vmOpts = new ArrayList<>(); 321 if (optionNames == null || optionValues == null || optionNames.length != optionValues.length) { 322 throw new IllegalArgumentException("optionNames and/or optionValues"); 323 } 324 325 for (int i = 0; i < optionNames.length; i++) { 326 vmOpts.add(prepareFlag(optionNames[i], optionValues[i])); 327 } 328 Collections.addAll(vmOpts, additionalVMOpts); 329 Collections.addAll(vmOpts, "-version"); 330 331 ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( 332 vmOpts.toArray(new String[vmOpts.size()])); 333 334 return new OutputAnalyzer(processBuilder.start()); 335 } 336 337 /** 338 * Verifies from the output that values of specified JVM options were the same as 339 * expected values. 340 * 341 * @param outputAnalyzer search output for expect options and values. 342 * @param optionNames names of tested options. 343 * @param expectedValues expected values of tested options. 344 * @throws Throwable if verification fails or some other issues occur. 345 */ 346 public static void verifyOptionValuesFromOutput(OutputAnalyzer outputAnalyzer, 347 String[] optionNames, 348 String[] expectedValues) throws Throwable { 349 outputAnalyzer.shouldHaveExitValue(0); 350 for (int i = 0; i < optionNames.length; i++) { 351 outputAnalyzer.shouldMatch(String.format( 352 CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, 353 optionNames[i], expectedValues[i])); 354 } 355 } 356 357 /** 358 * Verifies that value of specified JVM options are the same as 359 * expected values. 360 * Generates command line option flags from 361 * {@code optionNames} and {@code expectedValues}. 362 * 363 * @param optionNames names of tested options. 364 * @param expectedValues expected values of tested options. 365 * @throws Throwable if verification fails or some other issues occur. 366 */ 367 public static void verifyOptionValues(String[] optionNames, 368 String[] expectedValues) throws Throwable { 369 OutputAnalyzer outputAnalyzer = startVMWithOptions(optionNames, expectedValues, "-XX:+PrintFlagsFinal"); 370 verifyOptionValuesFromOutput(outputAnalyzer, optionNames, expectedValues); 371 } 372 373 /** 374 * Verifies that value of specified JVM when type of newly started VM 375 * is the same as the type of current. 376 * This method filter out option with {@code optionName} 377 * name from test java options. 378 * Only mode flag will be passed to VM in addition to 379 * {@code additionalVMOpts} 380 * 381 * @param optionName name of tested option. 382 * @param expectedValue expected value of tested option. 383 * @param optionErrorString message to show if option has another value 384 * @param additionalVMOpts additional options that should be 385 * passed to JVM. 386 * @throws Throwable if verification fails or some other issues occur. 387 */ 388 public static void verifyOptionValueForSameVM(String optionName, 389 String expectedValue, String optionErrorString, 390 String... additionalVMOpts) throws Throwable { 391 List<String> finalOptions = new ArrayList<>(); 392 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 393 Collections.addAll(finalOptions, additionalVMOpts); 394 395 CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, 396 optionErrorString, false, 397 finalOptions.toArray(new String[finalOptions.size()])); 398 } 399 400 /** 401 * Prepares boolean command line flag with name {@code name} according 402 * to it's {@code value}. 403 * 404 * @param name the name of option to be prepared 405 * @param value the value of option 406 * @return prepared command line flag 407 */ 408 public static String prepareBooleanFlag(String name, boolean value) { 409 return String.format("-XX:%c%s", (value ? '+' : '-'), name); 410 } 411 412 /** 413 * Prepares numeric command line flag with name {@code name} by setting 414 * it's value to {@code value}. 415 * 416 * @param name the name of option to be prepared 417 * @param value the value of option 418 * @return prepared command line flag 419 */ 420 public static String prepareNumericFlag(String name, Number value) { 421 return String.format("-XX:%s=%s", name, value.toString()); 422 } 423 424 /** 425 * Prepares generic command line flag with name {@code name} by setting 426 * it's value to {@code value}. 427 * 428 * @param name the name of option to be prepared 429 * @param value the value of option ("+" or "-" can be used instead of "true" or "false") 430 * @return prepared command line flag 431 */ 432 public static String prepareFlag(String name, String value) { 433 if (value.equals("+") || value.equalsIgnoreCase("true")) { 434 return "-XX:+" + name; 435 } else if (value.equals("-") || value.equalsIgnoreCase("false")) { 436 return "-XX:-" + name; 437 } else { 438 return "-XX:" + name + "=" + value; 439 } 440 } 441 442 /** 443 * Returns message that should occur in VM output if option 444 * {@code optionName} if unrecognized. 445 * 446 * @param optionName the name of option for which message should be returned 447 * @return message saying that option {@code optionName} is unrecognized 448 */ 449 public static String getUnrecognizedOptionErrorMessage(String optionName) { 450 return String.format( 451 CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT, 452 optionName); 453 } 454 455 /** 456 * Returns message that should occur in VM output if option 457 * {@code optionName} is experimental and 458 * -XX:+UnlockExperimentalVMOptions was not passed to VM. 459 * 460 * @param optionName the name of option for which message should be returned 461 * @return message saying that option {@code optionName} is experimental 462 */ 463 public static String getExperimentalOptionErrorMessage(String optionName) { 464 return String.format( 465 CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT, 466 optionName); 467 } 468 469 /** 470 * Returns message that should occur in VM output if option 471 * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions 472 * was not passed to VM. 473 * 474 * @param optionName the name of option for which message should be returned 475 * @return message saying that option {@code optionName} is diganostic 476 */ 477 public static String getDiagnosticOptionErrorMessage(String optionName) { 478 return String.format( 479 CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT, 480 optionName); 481 } 482 483 /** 484 * @return option required to start a new VM with the same type as current. 485 * @throws RuntimeException when VM type is unknown. 486 */ 487 private static String getVMTypeOption() { 488 if (Platform.isServer()) { 489 return "-server"; 490 } else if (Platform.isClient()) { 491 return "-client"; 492 } else if (Platform.isMinimal()) { 493 return "-minimal"; 494 } else if (Platform.isGraal()) { 495 return "-graal"; 496 } 497 throw new RuntimeException("Unknown VM mode."); 498 } 499 500 private final BooleanSupplier predicate; 501 502 /** 503 * Constructs new CommandLineOptionTest that will be executed only if 504 * predicate {@code predicate} return {@code true}. 505 * @param predicate a predicate responsible for test's preconditions check. 506 */ 507 public CommandLineOptionTest(BooleanSupplier predicate) { 508 this.predicate = predicate; 509 } 510 511 /** 512 * Runs command line option test. 513 */ 514 public final void test() throws Throwable { 515 if (predicate.getAsBoolean()) { 516 runTestCases(); 517 } 518 } 519 520 /** 521 * @throws Throwable if some issue happened during test cases execution. 522 */ 523 protected abstract void runTestCases() throws Throwable; 524} 525