• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //---------------------------------------------------------------------------
DecimalFormatTest()40 DecimalFormatTest::DecimalFormatTest()
41 {
42 }
43 
44 
~DecimalFormatTest()45 DecimalFormatTest::~DecimalFormatTest()
46 {
47 }
48 
49 
50 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)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);
~InvariantStringPiece()103     ~InvariantStringPiece() {};
104   private:
105     MaybeStackArray<char, 20>  buf;
106 };
107 
InvariantStringPiece(const UnicodeString & s)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);
~UnicodeStringPiece()127     ~UnicodeStringPiece() {};
128   private:
129     MaybeStackArray<char, 20>  buf;
130 };
131 
UnicodeStringPiece(const UnicodeString & s)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.
formattableType(Formattable::Type typ)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 *
getPath(char * buffer,const char * filename)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 
DataDrivenTests()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 
execParseTest(int32_t lineNum,const UnicodeString & inputText,const UnicodeString & expectedType,const UnicodeString & expectedDecimal,UErrorCode & status)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 
execFormatTest(int32_t lineNum,const UnicodeString & pattern,const UnicodeString & round,const UnicodeString & input,const UnicodeString & expected,EFormatInputType inType,UErrorCode & status)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 //--------------------------------------------------------------------------------
ReadAndConvertFile(const char * fileName,int32_t & ulen,UErrorCode & status)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 
548