• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4  * COPYRIGHT:
5  * Copyright (c) 2015, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  ********************************************************************/
8 
9 #include "datadrivennumberformattestsuite.h"
10 
11 #if !UCONFIG_NO_FORMATTING
12 
13 #include "charstr.h"
14 #include "ucbuf.h"
15 #include "unicode/localpointer.h"
16 #include "ustrfmt.h"
17 
isCROrLF(char16_t c)18 static UBool isCROrLF(char16_t c) { return c == 0xa || c == 0xd; }
isSpace(char16_t c)19 static UBool isSpace(char16_t c) { return c == 9 || c == 0x20 || c == 0x3000; }
20 
run(const char * fileName,UBool runAllTests)21 void DataDrivenNumberFormatTestSuite::run(const char *fileName, UBool runAllTests) {
22     fFileLineNumber = 0;
23     fFormatTestNumber = 0;
24     UErrorCode status = U_ZERO_ERROR;
25     for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) {
26         delete fPreviousFormatters[i];
27         fPreviousFormatters[i] = newFormatter(status);
28     }
29     if (!assertSuccess("Can't create previous formatters", status)) {
30         return;
31     }
32     CharString path(getSourceTestData(status), status);
33     path.appendPathPart(fileName, status);
34     const char *codePage = "UTF-8";
35     LocalUCHARBUFPointer f(ucbuf_open(path.data(), &codePage, true, false, &status));
36     if (!assertSuccess("Can't open data file", status)) {
37         return;
38     }
39     UnicodeString columnValues[kNumberFormatTestTupleFieldCount];
40     ENumberFormatTestTupleField columnTypes[kNumberFormatTestTupleFieldCount];
41     int32_t columnCount = 0;
42     int32_t state = 0;
43     while(U_SUCCESS(status)) {
44         // Read a new line if necessary.
45         if(fFileLine.isEmpty()) {
46             if(!readLine(f.getAlias(), status)) { break; }
47             if (fFileLine.isEmpty() && state == 2) {
48                 state = 0;
49             }
50             continue;
51         }
52         if (fFileLine.startsWith("//")) {
53             fFileLine.remove();
54             continue;
55         }
56         // Initial setup of test.
57         if (state == 0) {
58             if (fFileLine.startsWith(UNICODE_STRING("test ", 5))) {
59                 fFileTestName = fFileLine;
60                 fTuple.clear();
61             } else if(fFileLine.startsWith(UNICODE_STRING("set ", 4))) {
62                 setTupleField(status);
63             } else if(fFileLine.startsWith(UNICODE_STRING("begin", 5))) {
64                 state = 1;
65             } else {
66                 showError("Unrecognized verb.");
67                 return;
68             }
69         // column specification
70         } else if (state == 1) {
71             columnCount = splitBy(columnValues, UPRV_LENGTHOF(columnValues), 0x9);
72             for (int32_t i = 0; i < columnCount; ++i) {
73                 columnTypes[i] = NumberFormatTestTuple::getFieldByName(
74                     columnValues[i]);
75                 if (columnTypes[i] == kNumberFormatTestTupleFieldCount) {
76                     showError("Unrecognized field name.");
77                     return;
78                 }
79             }
80             state = 2;
81         // run the tests
82         } else {
83             int32_t columnsInThisRow = splitBy(columnValues, columnCount, 0x9);
84             for (int32_t i = 0; i < columnsInThisRow; ++i) {
85                 fTuple.setField(
86                         columnTypes[i], columnValues[i].unescape(), status);
87             }
88             for (int32_t i = columnsInThisRow; i < columnCount; ++i) {
89                 fTuple.clearField(columnTypes[i], status);
90             }
91             if (U_FAILURE(status)) {
92                 showError("Invalid column values");
93                 return;
94             }
95             if (runAllTests || !breaksC()) {
96                 UnicodeString errorMessage;
97                 UBool shouldFail = (NFTT_GET_FIELD(fTuple, output, "") == "fail")
98                         ? !breaksC()
99                         : breaksC();
100                 UBool actualSuccess = isPass(fTuple, errorMessage, status);
101                 if (shouldFail && actualSuccess) {
102                     showFailure("Expected failure, but passed: " + errorMessage);
103                     break;
104                 } else if (!shouldFail && !actualSuccess) {
105                     showFailure(errorMessage);
106                     break;
107                 }
108                 status = U_ZERO_ERROR;
109             }
110         }
111         fFileLine.remove();
112     }
113 }
114 
~DataDrivenNumberFormatTestSuite()115 DataDrivenNumberFormatTestSuite::~DataDrivenNumberFormatTestSuite() {
116     for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) {
117         delete fPreviousFormatters[i];
118     }
119 }
120 
breaksC()121 UBool DataDrivenNumberFormatTestSuite::breaksC() {
122     return (NFTT_GET_FIELD(fTuple, breaks, "").toUpper().indexOf(static_cast<char16_t>(0x43)) != -1);
123 }
124 
setTupleField(UErrorCode & status)125 void DataDrivenNumberFormatTestSuite::setTupleField(UErrorCode &status) {
126     if (U_FAILURE(status)) {
127         return;
128     }
129     UnicodeString parts[3];
130     int32_t partCount = splitBy(parts, UPRV_LENGTHOF(parts), 0x20);
131     if (partCount < 3) {
132         showError("Set expects 2 parameters");
133         status = U_PARSE_ERROR;
134         return;
135     }
136     if (!fTuple.setField(
137             NumberFormatTestTuple::getFieldByName(parts[1]),
138             parts[2].unescape(),
139             status)) {
140         showError("Invalid field value");
141     }
142 }
143 
144 
145 int32_t
splitBy(UnicodeString * columnValues,int32_t columnValuesCount,char16_t delimiter)146 DataDrivenNumberFormatTestSuite::splitBy(
147         UnicodeString *columnValues,
148         int32_t columnValuesCount,
149         char16_t delimiter) {
150     int32_t colIdx = 0;
151     int32_t colStart = 0;
152     int32_t len = fFileLine.length();
153     for (int32_t idx = 0; colIdx < columnValuesCount - 1 && idx < len; ++idx) {
154         char16_t ch = fFileLine.charAt(idx);
155         if (ch == delimiter) {
156             columnValues[colIdx++] =
157                     fFileLine.tempSubString(colStart, idx - colStart);
158             colStart = idx + 1;
159         }
160     }
161     columnValues[colIdx++] =
162             fFileLine.tempSubString(colStart, len - colStart);
163     return colIdx;
164 }
165 
showLineInfo()166 void DataDrivenNumberFormatTestSuite::showLineInfo() {
167     UnicodeString indent("    ");
168     infoln(indent + fFileTestName);
169     infoln(indent + fFileLine);
170 }
171 
showError(const char * message)172 void DataDrivenNumberFormatTestSuite::showError(const char *message) {
173     errln("line %d: %s", static_cast<int>(fFileLineNumber), message);
174     showLineInfo();
175 }
176 
showFailure(const UnicodeString & message)177 void DataDrivenNumberFormatTestSuite::showFailure(const UnicodeString &message) {
178     char16_t lineStr[20];
179     uprv_itou(lineStr, UPRV_LENGTHOF(lineStr), static_cast<uint32_t>(fFileLineNumber), 10, 1);
180     UnicodeString fullMessage("line ");
181     dataerrln(fullMessage.append(lineStr).append(": ")
182             .append(prettify(message)));
183     showLineInfo();
184 }
185 
readLine(UCHARBUF * f,UErrorCode & status)186 UBool DataDrivenNumberFormatTestSuite::readLine(
187         UCHARBUF *f, UErrorCode &status) {
188     int32_t lineLength;
189     const char16_t *line = ucbuf_readline(f, &lineLength, &status);
190     if(line == nullptr || U_FAILURE(status)) {
191         if (U_FAILURE(status)) {
192             errln("Error reading line from file.");
193         }
194         fFileLine.remove();
195         return false;
196     }
197     ++fFileLineNumber;
198     // Strip trailing CR/LF, comments, and spaces.
199     while(lineLength > 0 && isCROrLF(line[lineLength - 1])) { --lineLength; }
200     fFileLine.setTo(false, line, lineLength);
201     while(lineLength > 0 && isSpace(line[lineLength - 1])) { --lineLength; }
202     if (lineLength == 0) {
203         fFileLine.remove();
204     }
205     return true;
206 }
207 
isPass(const NumberFormatTestTuple & tuple,UnicodeString & appendErrorMessage,UErrorCode & status)208 UBool DataDrivenNumberFormatTestSuite::isPass(
209         const NumberFormatTestTuple &tuple,
210         UnicodeString &appendErrorMessage,
211         UErrorCode &status) {
212     if (U_FAILURE(status)) {
213         return false;
214     }
215     UBool result = false;
216     if (tuple.formatFlag && tuple.outputFlag) {
217         ++fFormatTestNumber;
218         result = isFormatPass(
219                 tuple,
220                 fPreviousFormatters[
221                         fFormatTestNumber % UPRV_LENGTHOF(fPreviousFormatters)],
222                 appendErrorMessage,
223                 status);
224     }
225     else if (tuple.toPatternFlag || tuple.toLocalizedPatternFlag) {
226         result = isToPatternPass(tuple, appendErrorMessage, status);
227     } else if (tuple.parseFlag && tuple.outputFlag && tuple.outputCurrencyFlag) {
228         result = isParseCurrencyPass(tuple, appendErrorMessage, status);
229 
230     } else if (tuple.parseFlag && tuple.outputFlag) {
231         result = isParsePass(tuple, appendErrorMessage, status);
232     } else if (tuple.pluralFlag) {
233         result = isSelectPass(tuple, appendErrorMessage, status);
234     } else {
235         appendErrorMessage.append("Unrecognized test type.");
236         status = U_ILLEGAL_ARGUMENT_ERROR;
237     }
238     if (!result) {
239         if (appendErrorMessage.length() > 0) {
240             appendErrorMessage.append(": ");
241         }
242         if (U_FAILURE(status)) {
243             appendErrorMessage.append(u_errorName(status));
244             appendErrorMessage.append(": ");
245         }
246         tuple.toString(appendErrorMessage);
247     }
248     return result;
249 }
250 
isFormatPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)251 UBool DataDrivenNumberFormatTestSuite::isFormatPass(
252         const NumberFormatTestTuple & /* tuple */,
253         UnicodeString & /*appendErrorMessage*/,
254         UErrorCode &status) {
255     if (U_FAILURE(status)) {
256         return false;
257     }
258     return true;
259 }
260 
isFormatPass(const NumberFormatTestTuple & tuple,UObject *,UnicodeString & appendErrorMessage,UErrorCode & status)261 UBool DataDrivenNumberFormatTestSuite::isFormatPass(
262         const NumberFormatTestTuple &tuple,
263         UObject * /* somePreviousFormatter */,
264         UnicodeString &appendErrorMessage,
265         UErrorCode &status) {
266     return isFormatPass(tuple, appendErrorMessage, status);
267 }
268 
newFormatter(UErrorCode &)269 UObject *DataDrivenNumberFormatTestSuite::newFormatter(
270         UErrorCode & /*status*/) {
271     return nullptr;
272 }
273 
isToPatternPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)274 UBool DataDrivenNumberFormatTestSuite::isToPatternPass(
275         const NumberFormatTestTuple & /* tuple */,
276         UnicodeString & /*appendErrorMessage*/,
277         UErrorCode &status) {
278     if (U_FAILURE(status)) {
279         return false;
280     }
281     return true;
282 }
283 
isParsePass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)284 UBool DataDrivenNumberFormatTestSuite::isParsePass(
285         const NumberFormatTestTuple & /* tuple */,
286         UnicodeString & /*appendErrorMessage*/,
287         UErrorCode &status) {
288     if (U_FAILURE(status)) {
289         return false;
290     }
291     return true;
292 }
293 
isParseCurrencyPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)294 UBool DataDrivenNumberFormatTestSuite::isParseCurrencyPass(
295         const NumberFormatTestTuple & /* tuple */,
296         UnicodeString & /*appendErrorMessage*/,
297         UErrorCode &status) {
298     if (U_FAILURE(status)) {
299         return false;
300     }
301     return true;
302 }
303 
isSelectPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)304 UBool DataDrivenNumberFormatTestSuite::isSelectPass(
305         const NumberFormatTestTuple & /* tuple */,
306         UnicodeString & /*appendErrorMessage*/,
307         UErrorCode &status) {
308     if (U_FAILURE(status)) {
309         return false;
310     }
311     return true;
312 }
313 #endif /* !UCONFIG_NO_FORMATTING */
314