• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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