]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/dcfmtest.cpp
ICU-511.34.tar.gz
[apple/icu.git] / icuSources / test / intltest / dcfmtest.cpp
1 /********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 2002-2012, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
6
7 //
8 // dcfmtest.cpp
9 //
10 // Decimal Formatter tests, data driven.
11 //
12
13 #include "intltest.h"
14
15 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_REGULAR_EXPRESSIONS
16
17 #include "unicode/regex.h"
18 #include "unicode/uchar.h"
19 #include "unicode/ustring.h"
20 #include "unicode/unistr.h"
21 #include "unicode/dcfmtsym.h"
22 #include "unicode/decimfmt.h"
23 #include "unicode/locid.h"
24 #include "cmemory.h"
25 #include "dcfmtest.h"
26 #include "util.h"
27 #include "cstring.h"
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdio.h>
31
32 #include <string>
33 #include <iostream>
34
35 //---------------------------------------------------------------------------
36 //
37 // Test class boilerplate
38 //
39 //---------------------------------------------------------------------------
40 DecimalFormatTest::DecimalFormatTest()
41 {
42 }
43
44
45 DecimalFormatTest::~DecimalFormatTest()
46 {
47 }
48
49
50
51 void DecimalFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
52 {
53 if (exec) logln("TestSuite DecimalFormatTest: ");
54 switch (index) {
55
56 #if !UCONFIG_NO_FILE_IO
57 case 0: name = "DataDrivenTests";
58 if (exec) DataDrivenTests();
59 break;
60 #else
61 case 0: name = "skip";
62 break;
63 #endif
64
65 default: name = "";
66 break; //needed to end loop
67 }
68 }
69
70
71 //---------------------------------------------------------------------------
72 //
73 // Error Checking / Reporting macros used in all of the tests.
74 //
75 //---------------------------------------------------------------------------
76 #define DF_CHECK_STATUS {if (U_FAILURE(status)) \
77 {dataerrln("DecimalFormatTest failure at line %d. status=%s", \
78 __LINE__, u_errorName(status)); return 0;}}
79
80 #define DF_ASSERT(expr) {if ((expr)==FALSE) {errln("DecimalFormatTest failure at line %d.\n", __LINE__);};}
81
82 #define DF_ASSERT_FAIL(expr, errcode) {UErrorCode status=U_ZERO_ERROR; (expr);\
83 if (status!=errcode) {dataerrln("DecimalFormatTest failure at line %d. Expected status=%s, got %s", \
84 __LINE__, u_errorName(errcode), u_errorName(status));};}
85
86 #define DF_CHECK_STATUS_L(line) {if (U_FAILURE(status)) {errln( \
87 "DecimalFormatTest failure at line %d, from %d. status=%d\n",__LINE__, (line), status); }}
88
89 #define DF_ASSERT_L(expr, line) {if ((expr)==FALSE) { \
90 errln("DecimalFormatTest failure at line %d, from %d.", __LINE__, (line)); return;}}
91
92
93
94 //
95 // InvariantStringPiece
96 // Wrap a StringPiece around the extracted invariant data of a UnicodeString.
97 // The data is guaranteed to be nul terminated. (This is not true of StringPiece
98 // in general, but is true of InvariantStringPiece)
99 //
100 class InvariantStringPiece: public StringPiece {
101 public:
102 InvariantStringPiece(const UnicodeString &s);
103 ~InvariantStringPiece() {};
104 private:
105 MaybeStackArray<char, 20> buf;
106 };
107
108 InvariantStringPiece::InvariantStringPiece(const UnicodeString &s) {
109 int32_t len = s.length();
110 if (len+1 > buf.getCapacity()) {
111 buf.resize(len+1);
112 }
113 // Buffer size is len+1 so that s.extract() will nul-terminate the string.
114 s.extract(0, len, buf.getAlias(), len+1, US_INV);
115 this->set(buf.getAlias(), len);
116 }
117
118
119 // UnicodeStringPiece
120 // Wrap a StringPiece around the extracted (to the default charset) data of
121 // a UnicodeString. The extracted data is guaranteed to be nul terminated.
122 // (This is not true of StringPiece in general, but is true of UnicodeStringPiece)
123 //
124 class UnicodeStringPiece: public StringPiece {
125 public:
126 UnicodeStringPiece(const UnicodeString &s);
127 ~UnicodeStringPiece() {};
128 private:
129 MaybeStackArray<char, 20> buf;
130 };
131
132 UnicodeStringPiece::UnicodeStringPiece(const UnicodeString &s) {
133 int32_t len = s.length();
134 int32_t capacity = buf.getCapacity();
135 int32_t requiredCapacity = s.extract(0, len, buf.getAlias(), capacity) + 1;
136 if (capacity < requiredCapacity) {
137 buf.resize(requiredCapacity);
138 capacity = requiredCapacity;
139 s.extract(0, len, buf.getAlias(), capacity);
140 }
141 this->set(buf.getAlias(), requiredCapacity - 1);
142 }
143
144
145
146 //---------------------------------------------------------------------------
147 //
148 // DataDrivenTests
149 // The test cases are in a separate data file,
150 //
151 //---------------------------------------------------------------------------
152
153 // Translate a Formattable::type enum value to a string, for error message formatting.
154 static const char *formattableType(Formattable::Type typ) {
155 static const char *types[] = {"kDate",
156 "kDouble",
157 "kLong",
158 "kString",
159 "kArray",
160 "kInt64",
161 "kObject"
162 };
163 if (typ<0 || typ>Formattable::kObject) {
164 return "Unknown";
165 }
166 return types[typ];
167 }
168
169 const char *
170 DecimalFormatTest::getPath(char *buffer, const char *filename) {
171 UErrorCode status=U_ZERO_ERROR;
172 const char *testDataDirectory = IntlTest::getSourceTestData(status);
173 DF_CHECK_STATUS;
174
175 strcpy(buffer, testDataDirectory);
176 strcat(buffer, filename);
177 return buffer;
178 }
179
180 void DecimalFormatTest::DataDrivenTests() {
181 char tdd[2048];
182 const char *srcPath;
183 UErrorCode status = U_ZERO_ERROR;
184 int32_t lineNum = 0;
185
186 //
187 // Open and read the test data file.
188 //
189 srcPath=getPath(tdd, "dcfmtest.txt");
190 if(srcPath==NULL) {
191 return; /* something went wrong, error already output */
192 }
193
194 int32_t len;
195 UChar *testData = ReadAndConvertFile(srcPath, len, status);
196 if (U_FAILURE(status)) {
197 return; /* something went wrong, error already output */
198 }
199
200 //
201 // Put the test data into a UnicodeString
202 //
203 UnicodeString testString(FALSE, testData, len);
204
205 RegexMatcher parseLineMat(UnicodeString(
206 "(?i)\\s*parse\\s+"
207 "\"([^\"]*)\"\\s+" // Capture group 1: input text
208 "([ild])\\s+" // Capture group 2: expected parsed type
209 "\"([^\"]*)\"\\s+" // Capture group 3: expected parsed decimal
210 "\\s*(?:#.*)?"), // Trailing comment
211 0, status);
212
213 RegexMatcher formatLineMat(UnicodeString(
214 "(?i)\\s*format\\s+"
215 "(\\S+)\\s+" // Capture group 1: pattern
216 "(ceiling|floor|down|up|halfeven|halfdown|halfup|default|unnecessary)\\s+" // Capture group 2: Rounding Mode
217 "\"([^\"]*)\"\\s+" // Capture group 3: input
218 "\"([^\"]*)\"" // Capture group 4: expected output
219 "\\s*(?:#.*)?"), // Trailing comment
220 0, status);
221
222 RegexMatcher commentMat (UNICODE_STRING_SIMPLE("\\s*(#.*)?$"), 0, status);
223 RegexMatcher lineMat(UNICODE_STRING_SIMPLE("(?m)^(.*?)$"), testString, 0, status);
224
225 if (U_FAILURE(status)){
226 dataerrln("Construct RegexMatcher() error.");
227 delete [] testData;
228 return;
229 }
230
231 //
232 // Loop over the test data file, once per line.
233 //
234 while (lineMat.find()) {
235 lineNum++;
236 if (U_FAILURE(status)) {
237 dataerrln("File dcfmtest.txt, line %d: ICU Error \"%s\"", lineNum, u_errorName(status));
238 }
239
240 status = U_ZERO_ERROR;
241 UnicodeString testLine = lineMat.group(1, status);
242 // printf("%s\n", UnicodeStringPiece(testLine).data());
243 if (testLine.length() == 0) {
244 continue;
245 }
246
247 //
248 // Parse the test line. Skip blank and comment only lines.
249 // Separate out the three main fields - pattern, flags, target.
250 //
251
252 commentMat.reset(testLine);
253 if (commentMat.lookingAt(status)) {
254 // This line is a comment, or blank.
255 continue;
256 }
257
258
259 //
260 // Handle "parse" test case line from file
261 //
262 parseLineMat.reset(testLine);
263 if (parseLineMat.lookingAt(status)) {
264 execParseTest(lineNum,
265 parseLineMat.group(1, status), // input
266 parseLineMat.group(2, status), // Expected Type
267 parseLineMat.group(3, status), // Expected Decimal String
268 status
269 );
270 continue;
271 }
272
273 //
274 // Handle "format" test case line
275 //
276 formatLineMat.reset(testLine);
277 if (formatLineMat.lookingAt(status)) {
278 execFormatTest(lineNum,
279 formatLineMat.group(1, status), // Pattern
280 formatLineMat.group(2, status), // rounding mode
281 formatLineMat.group(3, status), // input decimal number
282 formatLineMat.group(4, status), // expected formatted result
283 kFormattable,
284 status);
285
286 execFormatTest(lineNum,
287 formatLineMat.group(1, status), // Pattern
288 formatLineMat.group(2, status), // rounding mode
289 formatLineMat.group(3, status), // input decimal number
290 formatLineMat.group(4, status), // expected formatted result
291 kStringPiece,
292 status);
293 continue;
294 }
295
296 //
297 // Line is not a recognizable test case.
298 //
299 errln("Badly formed test case at line %d.\n%s\n",
300 lineNum, UnicodeStringPiece(testLine).data());
301
302 }
303
304 delete [] testData;
305 }
306
307
308
309 void DecimalFormatTest::execParseTest(int32_t lineNum,
310 const UnicodeString &inputText,
311 const UnicodeString &expectedType,
312 const UnicodeString &expectedDecimal,
313 UErrorCode &status) {
314
315 if (U_FAILURE(status)) {
316 return;
317 }
318
319 DecimalFormatSymbols symbols(Locale::getUS(), status);
320 UnicodeString pattern = UNICODE_STRING_SIMPLE("####");
321 DecimalFormat format(pattern, symbols, status);
322 Formattable result;
323 if (U_FAILURE(status)) {
324 dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.",
325 lineNum, u_errorName(status));
326 return;
327 }
328
329 ParsePosition pos;
330 int32_t expectedParseEndPosition = inputText.length();
331
332 format.parse(inputText, result, pos);
333
334 if (expectedParseEndPosition != pos.getIndex()) {
335 errln("file dcfmtest.txt, line %d: Expected parse position afeter parsing: %d. "
336 "Actual parse position: %d", expectedParseEndPosition, pos.getIndex());
337 return;
338 }
339
340 char expectedTypeC[2];
341 expectedType.extract(0, 1, expectedTypeC, 2, US_INV);
342 Formattable::Type expectType = Formattable::kDate;
343 switch (expectedTypeC[0]) {
344 case 'd': expectType = Formattable::kDouble; break;
345 case 'i': expectType = Formattable::kLong; break;
346 case 'l': expectType = Formattable::kInt64; break;
347 default:
348 errln("file dcfmtest.tx, line %d: unrecongized expected type \"%s\"",
349 lineNum, InvariantStringPiece(expectedType).data());
350 return;
351 }
352 if (result.getType() != expectType) {
353 errln("file dcfmtest.txt, line %d: expectedParseType(%s) != actual parseType(%s)",
354 lineNum, formattableType(expectType), formattableType(result.getType()));
355 return;
356 }
357
358 StringPiece decimalResult = result.getDecimalNumber(status);
359 if (U_FAILURE(status)) {
360 errln("File %s, line %d: error %s. Line in file dcfmtest.txt: %d:",
361 __FILE__, __LINE__, u_errorName(status), lineNum);
362 return;
363 }
364
365 InvariantStringPiece expectedResults(expectedDecimal);
366 if (decimalResult != expectedResults) {
367 errln("file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"",
368 lineNum, expectedResults.data(), decimalResult.data());
369 }
370
371 return;
372 }
373
374
375 void DecimalFormatTest::execFormatTest(int32_t lineNum,
376 const UnicodeString &pattern, // Pattern
377 const UnicodeString &round, // rounding mode
378 const UnicodeString &input, // input decimal number
379 const UnicodeString &expected, // expected formatted result
380 EFormatInputType inType, // input number type
381 UErrorCode &status) {
382 if (U_FAILURE(status)) {
383 return;
384 }
385
386 DecimalFormatSymbols symbols(Locale::getUS(), status);
387 // printf("Pattern = %s\n", UnicodeStringPiece(pattern).data());
388 DecimalFormat fmtr(pattern, symbols, status);
389 if (U_FAILURE(status)) {
390 dataerrln("file dcfmtest.txt, line %d: %s error creating the formatter.",
391 lineNum, u_errorName(status));
392 return;
393 }
394 if (round=="ceiling") {
395 fmtr.setRoundingMode(DecimalFormat::kRoundCeiling);
396 } else if (round=="floor") {
397 fmtr.setRoundingMode(DecimalFormat::kRoundFloor);
398 } else if (round=="down") {
399 fmtr.setRoundingMode(DecimalFormat::kRoundDown);
400 } else if (round=="up") {
401 fmtr.setRoundingMode(DecimalFormat::kRoundUp);
402 } else if (round=="halfeven") {
403 fmtr.setRoundingMode(DecimalFormat::kRoundHalfEven);
404 } else if (round=="halfdown") {
405 fmtr.setRoundingMode(DecimalFormat::kRoundHalfDown);
406 } else if (round=="halfup") {
407 fmtr.setRoundingMode(DecimalFormat::kRoundHalfUp);
408 } else if (round=="default") {
409 // don't set any value.
410 } else if (round=="unnecessary") {
411 fmtr.setRoundingMode(DecimalFormat::kRoundUnnecessary);
412 } else {
413 fmtr.setRoundingMode(DecimalFormat::kRoundFloor);
414 errln("file dcfmtest.txt, line %d: Bad rounding mode \"%s\"",
415 lineNum, UnicodeStringPiece(round).data());
416 }
417
418 const char *typeStr;
419 UnicodeString result;
420 UnicodeStringPiece spInput(input);
421
422 switch (inType) {
423 case kFormattable:
424 {
425 typeStr = "Formattable";
426 Formattable fmtbl;
427 fmtbl.setDecimalNumber(spInput, status);
428 fmtr.format(fmtbl, result, NULL, status);
429 }
430 break;
431 case kStringPiece:
432 typeStr = "StringPiece";
433 fmtr.format(spInput, result, NULL, status);
434 break;
435 }
436
437 if ((status == U_FORMAT_INEXACT_ERROR) && (result == "") && (expected == "Inexact")) {
438 // Test succeeded.
439 status = U_ZERO_ERROR;
440 return;
441 }
442
443 if (U_FAILURE(status)) {
444 errln("[%s] file dcfmtest.txt, line %d: format() returned %s.",
445 typeStr, lineNum, u_errorName(status));
446 status = U_ZERO_ERROR;
447 return;
448 }
449
450 if (result != expected) {
451 errln("[%s] file dcfmtest.txt, line %d: expected \"%s\", got \"%s\"",
452 typeStr, lineNum, UnicodeStringPiece(expected).data(), UnicodeStringPiece(result).data());
453 }
454 }
455
456
457 //-------------------------------------------------------------------------------
458 //
459 // Read a text data file, convert it from UTF-8 to UChars, and return the data
460 // in one big UChar * buffer, which the caller must delete.
461 //
462 // (Lightly modified version of a similar function in regextst.cpp)
463 //
464 //--------------------------------------------------------------------------------
465 UChar *DecimalFormatTest::ReadAndConvertFile(const char *fileName, int32_t &ulen,
466 UErrorCode &status) {
467 UChar *retPtr = NULL;
468 char *fileBuf = NULL;
469 const char *fileBufNoBOM = NULL;
470 FILE *f = NULL;
471
472 ulen = 0;
473 if (U_FAILURE(status)) {
474 return retPtr;
475 }
476
477 //
478 // Open the file.
479 //
480 f = fopen(fileName, "rb");
481 if (f == 0) {
482 dataerrln("Error opening test data file %s\n", fileName);
483 status = U_FILE_ACCESS_ERROR;
484 return NULL;
485 }
486 //
487 // Read it in
488 //
489 int32_t fileSize;
490 int32_t amtRead;
491 int32_t amtReadNoBOM;
492
493 fseek( f, 0, SEEK_END);
494 fileSize = ftell(f);
495 fileBuf = new char[fileSize];
496 fseek(f, 0, SEEK_SET);
497 amtRead = fread(fileBuf, 1, fileSize, f);
498 if (amtRead != fileSize || fileSize <= 0) {
499 errln("Error reading test data file.");
500 goto cleanUpAndReturn;
501 }
502
503 //
504 // Look for a UTF-8 BOM on the data just read.
505 // The test data file is UTF-8.
506 // The BOM needs to be there in the source file to keep the Windows &
507 // EBCDIC machines happy, so force an error if it goes missing.
508 // Many Linux editors will silently strip it.
509 //
510 fileBufNoBOM = fileBuf + 3;
511 amtReadNoBOM = amtRead - 3;
512 if (fileSize<3 || uprv_strncmp(fileBuf, "\xEF\xBB\xBF", 3) != 0) {
513 // TODO: restore this check.
514 errln("Test data file %s is missing its BOM", fileName);
515 fileBufNoBOM = fileBuf;
516 amtReadNoBOM = amtRead;
517 }
518
519 //
520 // Find the length of the input in UTF-16 UChars
521 // (by preflighting the conversion)
522 //
523 u_strFromUTF8(NULL, 0, &ulen, fileBufNoBOM, amtReadNoBOM, &status);
524
525 //
526 // Convert file contents from UTF-8 to UTF-16
527 //
528 if (status == U_BUFFER_OVERFLOW_ERROR) {
529 // Buffer Overflow is expected from the preflight operation.
530 status = U_ZERO_ERROR;
531 retPtr = new UChar[ulen+1];
532 u_strFromUTF8(retPtr, ulen+1, NULL, fileBufNoBOM, amtReadNoBOM, &status);
533 }
534
535 cleanUpAndReturn:
536 fclose(f);
537 delete[] fileBuf;
538 if (U_FAILURE(status)) {
539 errln("ICU Error \"%s\"\n", u_errorName(status));
540 delete retPtr;
541 retPtr = NULL;
542 };
543 return retPtr;
544 }
545
546 #endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */
547