1 /* 2 * Copyright (c) 2014, 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 com.oracle.java.testlibrary.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 com.oracle.java.testlibrary.*; 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 behaviour 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 exitCode expected exit code. 62 * @throws Throwable if verification fails or some other issues occur. 63 */ 64 public static void verifyJVMStartup(String option, 65 String expectedMessages[], String unexpectedMessages[], 66 ExitCode exitCode) throws Throwable { 67 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 68 unexpectedMessages, exitCode, false, option); 69 } 70 71 /** 72 * Verifies that JVM startup behaviour matches our expectations. 73 * 74 * @param expectedMessages an array of patterns that should occur 75 * in JVM output. If {@code null} then 76 * JVM output could be empty. 77 * @param unexpectedMessages an array of patterns that should not 78 * occur in JVM output. If {@code null} then 79 * JVM output could be empty. 80 * @param exitCode expected exit code. 81 * @param addTestVMOptions if {@code true} then test VM options will be 82 * passed to VM. 83 * @param options options that should be passed to VM in addition to mode 84 * flag. 85 * @throws Throwable if verification fails or some other issues occur. 86 */ 87 public static void verifyJVMStartup(String expectedMessages[], 88 String unexpectedMessages[], ExitCode exitCode, 89 boolean addTestVMOptions, String... options) throws Throwable { 90 List<String> finalOptions = new ArrayList<>(); 91 if (addTestVMOptions) { 92 Collections.addAll(finalOptions, Utils.getTestJavaOpts()); 93 } 94 Collections.addAll(finalOptions, options); 95 finalOptions.add("-version"); 96 97 ProcessBuilder processBuilder 98 = ProcessTools.createJavaProcessBuilder(finalOptions.toArray( 99 new String[finalOptions.size()])); 100 OutputAnalyzer outputAnalyzer 101 = new OutputAnalyzer(processBuilder.start()); 102 outputAnalyzer.shouldHaveExitValue(exitCode.value); 103 104 if (expectedMessages != null) { 105 for (String expectedMessage : expectedMessages) { 106 outputAnalyzer.shouldMatch(expectedMessage); 107 } 108 } 109 110 if (unexpectedMessages != null) { 111 for (String unexpectedMessage : unexpectedMessages) { 112 outputAnalyzer.shouldNotMatch(unexpectedMessage); 113 } 114 } 115 } 116 117 /** 118 * Verifies that JVM startup behaviour matches our expectations when type 119 * of newly started VM is the same as the type of current. 120 * 121 * @param expectedMessages an array of patterns that should occur 122 * in JVM output. If {@code null} then 123 * JVM output could be empty. 124 * @param unexpectedMessages an array of patterns that should not 125 * occur in JVM output. If {@code null} then 126 * JVM output could be empty. 127 * @param exitCode expected exit code. 128 * @param options options that should be passed to VM in addition to mode 129 * flag. 130 * @throws Throwable if verification fails or some other issues occur. 131 */ 132 public static void verifySameJVMStartup(String expectedMessages[], 133 String unexpectedMessages[], ExitCode exitCode, String... options) 134 throws Throwable { 135 List<String> finalOptions = new ArrayList<>(); 136 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 137 Collections.addAll(finalOptions, options); 138 139 CommandLineOptionTest.verifyJVMStartup(expectedMessages, 140 unexpectedMessages, exitCode, false, 141 finalOptions.toArray(new String[finalOptions.size()])); 142 } 143 144 /** 145 * Verifies that value of specified JVM option is the same as 146 * expected value. 147 * This method filter out option with {@code optionName} 148 * name from test java options. 149 * 150 * @param optionName a name of tested option. 151 * @param expectedValue expected value of tested option. 152 * @param additionalVMOpts additional options that should be 153 * passed to JVM. 154 * @throws Throwable if verification fails or some other issues occur. 155 */ 156 public static void verifyOptionValue(String optionName, 157 String expectedValue, String... additionalVMOpts) throws Throwable { 158 verifyOptionValue(optionName, expectedValue, true, additionalVMOpts); 159 } 160 161 /** 162 * Verifies that value of specified JVM option is the same as 163 * expected value. 164 * This method filter out option with {@code optionName} 165 * name from test java options. 166 * 167 * @param optionName a name of tested option. 168 * @param expectedValue expected value of tested option. 169 * @param addTestVmOptions if {@code true}, then test VM options 170 * will be used. 171 * @param additionalVMOpts additional options that should be 172 * passed to JVM. 173 * @throws Throwable if verification fails or some other issues 174 * occur. 175 */ 176 public static void verifyOptionValue(String optionName, 177 String expectedValue, boolean addTestVmOptions, 178 String... additionalVMOpts) throws Throwable { 179 List<String> vmOpts = new ArrayList<>(); 180 181 if (addTestVmOptions) { 182 Collections.addAll(vmOpts, 183 Utils.getFilteredTestJavaOpts(optionName)); 184 } 185 Collections.addAll(vmOpts, additionalVMOpts); 186 Collections.addAll(vmOpts, "-XX:+PrintFlagsFinal", "-version"); 187 188 ProcessBuilder processBuilder = ProcessTools.createJavaProcessBuilder( 189 vmOpts.toArray(new String[vmOpts.size()])); 190 191 OutputAnalyzer outputAnalyzer 192 = new OutputAnalyzer(processBuilder.start()); 193 194 outputAnalyzer.shouldHaveExitValue(0); 195 outputAnalyzer.shouldMatch(String.format( 196 CommandLineOptionTest.PRINT_FLAGS_FINAL_FORMAT, 197 optionName, expectedValue)); 198 } 199 200 /** 201 * Verifies that value of specified JVM when type of newly started VM 202 * is the same as the type of current. 203 * This method filter out option with {@code optionName} 204 * name from test java options. 205 * Only mode flag will be passed to VM in addition to 206 * {@code additionalVMOpts} 207 * 208 * @param optionName name of tested option. 209 * @param expectedValue expected value of tested option. 210 * @param additionalVMOpts additional options that should be 211 * passed to JVM. 212 * @throws Throwable if verification fails or some other issues occur. 213 */ 214 public static void verifyOptionValueForSameVM(String optionName, 215 String expectedValue, String... additionalVMOpts) throws Throwable { 216 List<String> finalOptions = new ArrayList<>(); 217 finalOptions.add(CommandLineOptionTest.getVMTypeOption()); 218 Collections.addAll(finalOptions, additionalVMOpts); 219 220 CommandLineOptionTest.verifyOptionValue(optionName, expectedValue, 221 false, finalOptions.toArray(new String[finalOptions.size()])); 222 } 223 224 /** 225 * Prepares boolean command line flag with name {@code name} according 226 * to it's {@code value}. 227 * 228 * @param name the name of option to be prepared 229 * @param value the value of option 230 * @return prepared command line flag 231 */ 232 public static String prepareBooleanFlag(String name, boolean value) { 233 return String.format("-XX:%c%s", (value ? '+' : '-'), name); 234 } 235 236 /** 237 * Prepares numeric command line flag with name {@code name} by setting 238 * it's value to {@code value}. 239 * 240 * @param name the name of option to be prepared 241 * @param value the value of option 242 * @return prepared command line flag 243 */ 244 public static String prepareNumericFlag(String name, Number value) { 245 return String.format("-XX:%s=%s", name, value.toString()); 246 } 247 248 /** 249 * Returns message that should occur in VM output if option 250 * {@code optionName} if unrecognized. 251 * 252 * @param optionName the name of option for which message should be returned 253 * @return message saying that option {@code optionName} is unrecognized 254 */ 255 public static String getUnrecognizedOptionErrorMessage(String optionName) { 256 return String.format( 257 CommandLineOptionTest.UNRECOGNIZED_OPTION_ERROR_FORMAT, 258 optionName); 259 } 260 261 /** 262 * Returns message that should occur in VM output if option 263 * {@code optionName} is experimental and 264 * -XX:+UnlockExperimentalVMOptions was not passed to VM. 265 * 266 * @param optionName the name of option for which message should be returned 267 * @return message saying that option {@code optionName} is experimental 268 */ 269 public static String getExperimentalOptionErrorMessage(String optionName) { 270 return String.format( 271 CommandLineOptionTest.EXPERIMENTAL_OPTION_ERROR_FORMAT, 272 optionName); 273 } 274 275 /** 276 * Returns message that should occur in VM output if option 277 * {@code optionName} is diagnostic and -XX:+UnlockDiagnosticVMOptions 278 * was not passed to VM. 279 * 280 * @param optionName the name of option for which message should be returned 281 * @return message saying that option {@code optionName} is diganostic 282 */ 283 public static String getDiagnosticOptionErrorMessage(String optionName) { 284 return String.format( 285 CommandLineOptionTest.DIAGNOSTIC_OPTION_ERROR_FORMAT, 286 optionName); 287 } 288 289 /** 290 * @return option required to start a new VM with the same type as current. 291 * @throws RuntimeException when VM type is unknown. 292 */ 293 private static String getVMTypeOption() { 294 if (Platform.isServer()) { 295 return "-server"; 296 } else if (Platform.isClient()) { 297 return "-client"; 298 } else if (Platform.isMinimal()) { 299 return "-minimal"; 300 } else if (Platform.isGraal()) { 301 return "-graal"; 302 } 303 throw new RuntimeException("Unknown VM mode."); 304 } 305 306 private final BooleanSupplier predicate; 307 308 /** 309 * Constructs new CommandLineOptionTest that will be executed only if 310 * predicate {@code predicate} return {@code true}. 311 * @param predicate a predicate responsible for test's preconditions check. 312 */ 313 public CommandLineOptionTest(BooleanSupplier predicate) { 314 this.predicate = predicate; 315 } 316 317 /** 318 * Runs command line option test. 319 */ 320 public final void test() throws Throwable { 321 if (predicate.getAsBoolean()) { 322 runTestCases(); 323 } 324 } 325 326 /** 327 * @throws Throwable if some issue happened during test cases execution. 328 */ 329 protected abstract void runTestCases() throws Throwable; 330 }