1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2016 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 /* 5 ******************************************************************************* 6 * Copyright (C) 2015, International Business Machines Corporation and * 7 * others. All Rights Reserved. * 8 ******************************************************************************* 9 */ 10 package ohos.global.icu.dev.test.format; 11 12 import java.io.BufferedReader; 13 import java.io.ByteArrayOutputStream; 14 import java.io.IOException; 15 import java.io.PrintStream; 16 import java.util.ArrayList; 17 import java.util.List; 18 19 import ohos.global.icu.dev.test.TestFmwk; 20 import ohos.global.icu.dev.test.TestUtil; 21 import ohos.global.icu.impl.Utility; 22 23 24 /** 25 * A collection of methods to run the data driven number format test suite. 26 */ 27 28 public class DataDrivenNumberFormatTestUtility extends TestFmwk { 29 30 /** 31 * Base class for code under test. 32 */ 33 public static abstract class CodeUnderTest { 34 35 /** 36 * Returns the ID of the code under test. This ID is used to identify 37 * tests that are known to fail for this particular code under test. 38 * This implementation returns null which means that by default all 39 * tests should work with this code under test. 40 * @return 'J' means ICU4J, 'K' means JDK 41 */ Id()42 public Character Id() { 43 return null; 44 } 45 46 /** 47 * Runs a single formatting test. On success, returns null. 48 * On failure, returns the error. This implementation just returns null. 49 * Subclasses should override. 50 * @param tuple contains the parameters of the format test. 51 */ format(DataDrivenNumberFormatTestData tuple)52 public String format(DataDrivenNumberFormatTestData tuple) { 53 if (tuple.output != null && tuple.output.equals("fail")) return "fail"; 54 return null; 55 } 56 57 /** 58 * Runs a single toPattern test. On success, returns null. 59 * On failure, returns the error. This implementation just returns null. 60 * Subclasses should override. 61 * @param tuple contains the parameters of the format test. 62 */ toPattern(DataDrivenNumberFormatTestData tuple)63 public String toPattern(DataDrivenNumberFormatTestData tuple) { 64 if (tuple.output != null && tuple.output.equals("fail")) return "fail"; 65 return null; 66 } 67 68 /** 69 * Runs a single parse test. On success, returns null. 70 * On failure, returns the error. This implementation just returns null. 71 * Subclasses should override. 72 * @param tuple contains the parameters of the format test. 73 */ parse(DataDrivenNumberFormatTestData tuple)74 public String parse(DataDrivenNumberFormatTestData tuple) { 75 if (tuple.output != null && tuple.output.equals("fail")) return "fail"; 76 return null; 77 } 78 79 /** 80 * Runs a single parse currency test. On success, returns null. 81 * On failure, returns the error. This implementation just returns null. 82 * Subclasses should override. 83 * @param tuple contains the parameters of the format test. 84 */ parseCurrency(DataDrivenNumberFormatTestData tuple)85 public String parseCurrency(DataDrivenNumberFormatTestData tuple) { 86 if (tuple.output != null && tuple.output.equals("fail")) return "fail"; 87 return null; 88 } 89 90 /** 91 * Runs a single select test. On success, returns null. 92 * On failure, returns the error. This implementation just returns null. 93 * Subclasses should override. 94 * @param tuple contains the parameters of the format test. 95 */ select(DataDrivenNumberFormatTestData tuple)96 public String select(DataDrivenNumberFormatTestData tuple) { 97 if (tuple.output != null && tuple.output.equals("fail")) return "fail"; 98 return null; 99 } 100 } 101 102 private static enum RunMode { 103 SKIP_KNOWN_FAILURES, 104 INCLUDE_KNOWN_FAILURES, 105 CHECK_FOR_UNKNOWN_IDS 106 } 107 108 private final CodeUnderTest codeUnderTest; 109 private final String allowedIDs; 110 private String fileLine = null; 111 private int fileLineNumber = 0; 112 private String fileTestName = ""; 113 private DataDrivenNumberFormatTestData tuple = new DataDrivenNumberFormatTestData(); 114 115 /** 116 * Runs all the tests in the data driven test suite against codeUnderTest. 117 * @param fileName The name of the test file. A relative file name under 118 * com/ibm/icu/dev/data such as "data.txt" 119 * @param codeUnderTest the code under test 120 */ 121 runSuite( String fileName, CodeUnderTest codeUnderTest)122 public static void runSuite( 123 String fileName, CodeUnderTest codeUnderTest) { 124 new DataDrivenNumberFormatTestUtility(codeUnderTest) 125 .run(fileName, RunMode.SKIP_KNOWN_FAILURES); 126 } 127 128 /** 129 * Runs every format test in data driven test suite including those 130 * that are known to fail. If a test is supposed to fail but actually 131 * passes, an error is printed. 132 * 133 * @param fileName The name of the test file. A relative file name under 134 * com/ibm/icu/dev/data such as "data.txt" 135 * @param codeUnderTest the code under test 136 */ runFormatSuiteIncludingKnownFailures( String fileName, CodeUnderTest codeUnderTest)137 public static void runFormatSuiteIncludingKnownFailures( 138 String fileName, CodeUnderTest codeUnderTest) { 139 new DataDrivenNumberFormatTestUtility(codeUnderTest) 140 .run(fileName, RunMode.INCLUDE_KNOWN_FAILURES); 141 } 142 143 /** 144 * Checks the data file for no unknown IDs in "breaks" columns. 145 */ checkNoUnknownIDs(String fileName, String allowedIDs)146 public static void checkNoUnknownIDs(String fileName, String allowedIDs) { 147 new DataDrivenNumberFormatTestUtility(allowedIDs).run(fileName, RunMode.CHECK_FOR_UNKNOWN_IDS); 148 } 149 DataDrivenNumberFormatTestUtility(CodeUnderTest codeUnderTest)150 private DataDrivenNumberFormatTestUtility(CodeUnderTest codeUnderTest) { 151 this.codeUnderTest = codeUnderTest; 152 this.allowedIDs = null; 153 } 154 DataDrivenNumberFormatTestUtility(String allowedIDs)155 private DataDrivenNumberFormatTestUtility(String allowedIDs) { 156 this.codeUnderTest = null; 157 this.allowedIDs = allowedIDs; 158 } 159 run(String fileName, RunMode runMode)160 private void run(String fileName, RunMode runMode) { 161 char codeUnderTestId = (codeUnderTest == null) ? 0 : codeUnderTest.Id(); 162 BufferedReader in = null; 163 try { 164 in = TestUtil.getDataReader("numberformattestspecification.txt", "UTF-8"); 165 // read first line and remove BOM if present 166 readLine(in); 167 if (fileLine != null && fileLine.charAt(0) == '\uFEFF') { 168 fileLine = fileLine.substring(1); 169 } 170 171 int state = 0; 172 List<String> columnValues; 173 List<String> columnNames = null; 174 while (true) { 175 if (fileLine == null || fileLine.length() == 0) { 176 if (!readLine(in)) { 177 break; 178 } 179 if (fileLine.length() == 0 && state == 2) { 180 state = 0; 181 } 182 continue; 183 } 184 if (fileLine.startsWith("//")) { 185 fileLine = null; 186 continue; 187 } 188 // Initial setup of test. 189 if (state == 0) { 190 if (fileLine.startsWith("test ")) { 191 fileTestName = fileLine; 192 tuple = new DataDrivenNumberFormatTestData(); 193 } else if (fileLine.startsWith("set ")) { 194 if (!setTupleField()) { 195 return; 196 } 197 } else if(fileLine.startsWith("begin")) { 198 state = 1; 199 } else { 200 showError("Unrecognized verb."); 201 return; 202 } 203 // column specification 204 } else if (state == 1) { 205 columnNames = splitBy((char) 0x09); 206 state = 2; 207 // "breaks" column must be last! 208 if (columnNames.contains("breaks") 209 && columnNames.indexOf("breaks") != columnNames.size() - 1) { 210 showError("'breaks' column must be last!"); 211 } 212 // run the tests 213 } else { 214 int columnNamesSize = columnNames.size(); 215 columnValues = splitBy(columnNamesSize, (char) 0x09); 216 int columnValuesSize = columnValues.size(); 217 for (int i = 0; i < columnValuesSize; ++i) { 218 if (!setField(columnNames.get(i), columnValues.get(i))) { 219 return; 220 } 221 } 222 for (int i = columnValuesSize; i < columnNamesSize; ++i) { 223 if (!clearField(columnNames.get(i))) { 224 return; 225 } 226 } 227 if (runMode == RunMode.CHECK_FOR_UNKNOWN_IDS) { 228 String actualIDs = tuple.breaks; 229 if (actualIDs != null) { 230 // Make sure there are no IDs in actualIDs that are not in allowedIDs. 231 // As a bonus, check that actualIDs are in alphabetical order. 232 char prevID = 0; 233 for (int i=0; i<actualIDs.length(); i++) { 234 char currID = actualIDs.charAt(i); 235 if (allowedIDs.indexOf(currID) == -1) { 236 showError("Unknown ID: " + currID); 237 } 238 if (prevID > currID) { 239 showError("IDs not in alphabetical order: " + actualIDs); 240 } 241 prevID = currID; 242 } 243 } 244 } else if (runMode == RunMode.INCLUDE_KNOWN_FAILURES || !breaks(codeUnderTestId)) { 245 String errorMessage; 246 Exception err = null; 247 boolean shouldFail = (tuple.output != null && tuple.output.equals("fail")) 248 ? !breaks(codeUnderTestId) 249 : breaks(codeUnderTestId); 250 try { 251 errorMessage = isPass(tuple); 252 } catch (Exception e) { 253 err = e; 254 errorMessage = "Exception: " + e + ": " + e.getCause(); 255 } 256 if (shouldFail && errorMessage == null) { 257 showError("Expected failure, but passed"); 258 } else if (!shouldFail && errorMessage != null) { 259 if (err != null) { 260 ByteArrayOutputStream os = new ByteArrayOutputStream(); 261 PrintStream ps = new PrintStream(os); 262 err.printStackTrace(ps); 263 String stackTrace = os.toString(); 264 showError(errorMessage + " Stack trace: " + stackTrace.substring(0, 500)); 265 } else { 266 showError(errorMessage); 267 } 268 } 269 } 270 } 271 fileLine = null; 272 } 273 } catch (Exception e) { 274 ByteArrayOutputStream os = new ByteArrayOutputStream(); 275 PrintStream ps = new PrintStream(os); 276 e.printStackTrace(ps); 277 String stackTrace = os.toString(); 278 showError("MAJOR ERROR: " + e.toString() + " Stack trace: " + stackTrace.substring(0,500)); 279 } finally { 280 try { 281 if (in != null) { 282 in.close(); 283 } 284 } catch (IOException e) { 285 e.printStackTrace(); 286 } 287 } 288 } 289 breaks(char code)290 private boolean breaks(char code) { 291 String breaks = tuple.breaks == null ? "" : tuple.breaks; 292 return (breaks.toUpperCase().indexOf(code) != -1); 293 } 294 isSpace(char c)295 private static boolean isSpace(char c) { 296 return (c == 0x09 || c == 0x20 || c == 0x3000); 297 } 298 setTupleField()299 private boolean setTupleField() { 300 List<String> parts = splitBy(3, (char) 0x20); 301 if (parts.size() < 3) { 302 showError("Set expects 2 parameters"); 303 return false; 304 } 305 return setField(parts.get(1), parts.get(2)); 306 } 307 setField(String name, String value)308 private boolean setField(String name, String value) { 309 try { 310 tuple.setField(name, Utility.unescape(value)); 311 return true; 312 } catch (Exception e) { 313 showError("No such field: " + name + ", or bad value: " + value + ": " + e); 314 return false; 315 } 316 } 317 clearField(String name)318 private boolean clearField(String name) { 319 try { 320 tuple.clearField(name); 321 return true; 322 } catch (Exception e) { 323 showError("Field cannot be clared: " + name); 324 return false; 325 } 326 } 327 showError(String message)328 private void showError(String message) { 329 TestFmwk.errln(String.format("line %d: %s\n%s\n%s", fileLineNumber, Utility.escape(message), fileTestName,fileLine)); 330 } 331 splitBy(char delimiter)332 private List<String> splitBy(char delimiter) { 333 return splitBy(Integer.MAX_VALUE, delimiter); 334 } 335 splitBy(int max, char delimiter)336 private List<String> splitBy(int max, char delimiter) { 337 ArrayList<String> result = new ArrayList<String>(); 338 int colIdx = 0; 339 int colStart = 0; 340 int len = fileLine.length(); 341 for (int idx = 0; colIdx < max - 1 && idx < len; ++idx) { 342 char ch = fileLine.charAt(idx); 343 if (ch == delimiter) { 344 result.add( 345 fileLine.substring(colStart, idx)); 346 ++colIdx; 347 colStart = idx + 1; 348 } 349 } 350 result.add(fileLine.substring(colStart, len)); 351 return result; 352 } 353 readLine(BufferedReader in)354 private boolean readLine(BufferedReader in) throws IOException { 355 String line = in.readLine(); 356 if (line == null) { 357 fileLine = null; 358 return false; 359 } 360 ++fileLineNumber; 361 // Strip trailing comments and spaces 362 int idx = line.length(); 363 for (; idx > 0; idx--) { 364 if (!isSpace(line.charAt(idx -1))) { 365 break; 366 } 367 } 368 fileLine = idx == 0 ? "" : line; 369 return true; 370 } 371 isPass(DataDrivenNumberFormatTestData tuple)372 private String isPass(DataDrivenNumberFormatTestData tuple) { 373 StringBuilder result = new StringBuilder(); 374 if (tuple.format != null && tuple.output != null) { 375 String errorMessage = codeUnderTest.format(tuple); 376 if (errorMessage != null) { 377 result.append(errorMessage); 378 } 379 } else if (tuple.toPattern != null || tuple.toLocalizedPattern != null) { 380 String errorMessage = codeUnderTest.toPattern(tuple); 381 if (errorMessage != null) { 382 result.append(errorMessage); 383 } 384 } else if (tuple.parse != null && tuple.output != null && tuple.outputCurrency != null) { 385 String errorMessage = codeUnderTest.parseCurrency(tuple); 386 if (errorMessage != null) { 387 result.append(errorMessage); 388 } 389 } else if (tuple.parse != null && tuple.output != null) { 390 String errorMessage = codeUnderTest.parse(tuple); 391 if (errorMessage != null) { 392 result.append(errorMessage); 393 } 394 } else if (tuple.plural != null) { 395 String errorMessage = codeUnderTest.select(tuple); 396 if (errorMessage != null) { 397 result.append(errorMessage); 398 } 399 } else { 400 result.append("Unrecognized test type."); 401 } 402 if (result.length() > 0) { 403 result.append(": "); 404 result.append(tuple); 405 return result.toString(); 406 } 407 return null; 408 } 409 } 410