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