1 // Copyright (C) 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(UChar c)18 static UBool isCROrLF(UChar c) { return c == 0xa || c == 0xd; }
isSpace(UChar c)19 static UBool isSpace(UChar 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;
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 (!breaksC() || runAllTests) {
96 UnicodeString errorMessage;
97 if (!isPass(fTuple, errorMessage, status)) {
98 showFailure(errorMessage);
99 }
100 }
101 }
102 fFileLine.remove();
103 }
104 }
105
~DataDrivenNumberFormatTestSuite()106 DataDrivenNumberFormatTestSuite::~DataDrivenNumberFormatTestSuite() {
107 for (int32_t i = 0; i < UPRV_LENGTHOF(fPreviousFormatters); ++i) {
108 delete fPreviousFormatters[i];
109 }
110 }
111
breaksC()112 UBool DataDrivenNumberFormatTestSuite::breaksC() {
113 return (NFTT_GET_FIELD(fTuple, breaks, "").toUpper().indexOf((UChar)0x43) != -1);
114 }
115
setTupleField(UErrorCode & status)116 void DataDrivenNumberFormatTestSuite::setTupleField(UErrorCode &status) {
117 if (U_FAILURE(status)) {
118 return;
119 }
120 UnicodeString parts[3];
121 int32_t partCount = splitBy(parts, UPRV_LENGTHOF(parts), 0x20);
122 if (partCount < 3) {
123 showError("Set expects 2 parameters");
124 status = U_PARSE_ERROR;
125 return;
126 }
127 if (!fTuple.setField(
128 NumberFormatTestTuple::getFieldByName(parts[1]),
129 parts[2].unescape(),
130 status)) {
131 showError("Invalid field value");
132 }
133 }
134
135
136 int32_t
splitBy(UnicodeString * columnValues,int32_t columnValuesCount,UChar delimiter)137 DataDrivenNumberFormatTestSuite::splitBy(
138 UnicodeString *columnValues,
139 int32_t columnValuesCount,
140 UChar delimiter) {
141 int32_t colIdx = 0;
142 int32_t colStart = 0;
143 int32_t len = fFileLine.length();
144 for (int32_t idx = 0; colIdx < columnValuesCount - 1 && idx < len; ++idx) {
145 UChar ch = fFileLine.charAt(idx);
146 if (ch == delimiter) {
147 columnValues[colIdx++] =
148 fFileLine.tempSubString(colStart, idx - colStart);
149 colStart = idx + 1;
150 }
151 }
152 columnValues[colIdx++] =
153 fFileLine.tempSubString(colStart, len - colStart);
154 return colIdx;
155 }
156
showLineInfo()157 void DataDrivenNumberFormatTestSuite::showLineInfo() {
158 UnicodeString indent(" ");
159 infoln(indent + fFileTestName);
160 infoln(indent + fFileLine);
161 }
162
showError(const char * message)163 void DataDrivenNumberFormatTestSuite::showError(const char *message) {
164 errln("line %d: %s", (int) fFileLineNumber, message);
165 showLineInfo();
166 }
167
showFailure(const UnicodeString & message)168 void DataDrivenNumberFormatTestSuite::showFailure(const UnicodeString &message) {
169 UChar lineStr[20];
170 uprv_itou(
171 lineStr, UPRV_LENGTHOF(lineStr), (uint32_t) fFileLineNumber, 10, 1);
172 UnicodeString fullMessage("line ");
173 dataerrln(fullMessage.append(lineStr).append(": ")
174 .append(prettify(message)));
175 showLineInfo();
176 }
177
readLine(UCHARBUF * f,UErrorCode & status)178 UBool DataDrivenNumberFormatTestSuite::readLine(
179 UCHARBUF *f, UErrorCode &status) {
180 int32_t lineLength;
181 const UChar *line = ucbuf_readline(f, &lineLength, &status);
182 if(line == NULL || U_FAILURE(status)) {
183 if (U_FAILURE(status)) {
184 errln("Error reading line from file.");
185 }
186 fFileLine.remove();
187 return FALSE;
188 }
189 ++fFileLineNumber;
190 // Strip trailing CR/LF, comments, and spaces.
191 while(lineLength > 0 && isCROrLF(line[lineLength - 1])) { --lineLength; }
192 fFileLine.setTo(FALSE, line, lineLength);
193 while(lineLength > 0 && isSpace(line[lineLength - 1])) { --lineLength; }
194 if (lineLength == 0) {
195 fFileLine.remove();
196 }
197 return TRUE;
198 }
199
isPass(const NumberFormatTestTuple & tuple,UnicodeString & appendErrorMessage,UErrorCode & status)200 UBool DataDrivenNumberFormatTestSuite::isPass(
201 const NumberFormatTestTuple &tuple,
202 UnicodeString &appendErrorMessage,
203 UErrorCode &status) {
204 if (U_FAILURE(status)) {
205 return FALSE;
206 }
207 UBool result = FALSE;
208 if (tuple.formatFlag && tuple.outputFlag) {
209 ++fFormatTestNumber;
210 result = isFormatPass(
211 tuple,
212 fPreviousFormatters[
213 fFormatTestNumber % UPRV_LENGTHOF(fPreviousFormatters)],
214 appendErrorMessage,
215 status);
216 }
217 else if (tuple.toPatternFlag || tuple.toLocalizedPatternFlag) {
218 result = isToPatternPass(tuple, appendErrorMessage, status);
219 } else if (tuple.parseFlag && tuple.outputFlag && tuple.outputCurrencyFlag) {
220 result = isParseCurrencyPass(tuple, appendErrorMessage, status);
221
222 } else if (tuple.parseFlag && tuple.outputFlag) {
223 result = isParsePass(tuple, appendErrorMessage, status);
224 } else if (tuple.pluralFlag) {
225 result = isSelectPass(tuple, appendErrorMessage, status);
226 } else {
227 appendErrorMessage.append("Unrecognized test type.");
228 status = U_ILLEGAL_ARGUMENT_ERROR;
229 }
230 if (!result) {
231 if (appendErrorMessage.length() > 0) {
232 appendErrorMessage.append(": ");
233 }
234 if (U_FAILURE(status)) {
235 appendErrorMessage.append(u_errorName(status));
236 appendErrorMessage.append(": ");
237 }
238 tuple.toString(appendErrorMessage);
239 }
240 return result;
241 }
242
isFormatPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)243 UBool DataDrivenNumberFormatTestSuite::isFormatPass(
244 const NumberFormatTestTuple & /* tuple */,
245 UnicodeString & /*appendErrorMessage*/,
246 UErrorCode &status) {
247 if (U_FAILURE(status)) {
248 return FALSE;
249 }
250 return TRUE;
251 }
252
isFormatPass(const NumberFormatTestTuple & tuple,UObject *,UnicodeString & appendErrorMessage,UErrorCode & status)253 UBool DataDrivenNumberFormatTestSuite::isFormatPass(
254 const NumberFormatTestTuple &tuple,
255 UObject * /* somePreviousFormatter */,
256 UnicodeString &appendErrorMessage,
257 UErrorCode &status) {
258 return isFormatPass(tuple, appendErrorMessage, status);
259 }
260
newFormatter(UErrorCode &)261 UObject *DataDrivenNumberFormatTestSuite::newFormatter(
262 UErrorCode & /*status*/) {
263 return NULL;
264 }
265
isToPatternPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)266 UBool DataDrivenNumberFormatTestSuite::isToPatternPass(
267 const NumberFormatTestTuple & /* tuple */,
268 UnicodeString & /*appendErrorMessage*/,
269 UErrorCode &status) {
270 if (U_FAILURE(status)) {
271 return FALSE;
272 }
273 return TRUE;
274 }
275
isParsePass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)276 UBool DataDrivenNumberFormatTestSuite::isParsePass(
277 const NumberFormatTestTuple & /* tuple */,
278 UnicodeString & /*appendErrorMessage*/,
279 UErrorCode &status) {
280 if (U_FAILURE(status)) {
281 return FALSE;
282 }
283 return TRUE;
284 }
285
isParseCurrencyPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)286 UBool DataDrivenNumberFormatTestSuite::isParseCurrencyPass(
287 const NumberFormatTestTuple & /* tuple */,
288 UnicodeString & /*appendErrorMessage*/,
289 UErrorCode &status) {
290 if (U_FAILURE(status)) {
291 return FALSE;
292 }
293 return TRUE;
294 }
295
isSelectPass(const NumberFormatTestTuple &,UnicodeString &,UErrorCode & status)296 UBool DataDrivenNumberFormatTestSuite::isSelectPass(
297 const NumberFormatTestTuple & /* tuple */,
298 UnicodeString & /*appendErrorMessage*/,
299 UErrorCode &status) {
300 if (U_FAILURE(status)) {
301 return FALSE;
302 }
303 return TRUE;
304 }
305 #endif /* !UCONFIG_NO_FORMATTING */
306