1 // © 2019 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3
4 #include "unicode/utypes.h"
5
6 #if !UCONFIG_NO_FORMATTING
7
8 #include <fstream>
9 #include <iostream>
10 #include <vector>
11
12 #include "numbertest.h"
13 #include "ucbuf.h"
14 #include "unicode/numberformatter.h"
15
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)16 void NumberPermutationTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) {
17 if (exec) {
18 logln("TestSuite NumberPermutationTest: ");
19 }
20 TESTCASE_AUTO_BEGIN;
21 TESTCASE_AUTO(testPermutations);
22 TESTCASE_AUTO_END;
23 }
24
25 static const char16_t* kSkeletonParts[] = {
26 // Notation
27 u"compact-short",
28 u"scientific/+ee/sign-always",
29 nullptr,
30 // Unit
31 u"percent",
32 u"currency/EUR",
33 u"measure-unit/length-furlong",
34 nullptr,
35 // Unit Width
36 u"unit-width-narrow",
37 u"unit-width-full-name",
38 nullptr,
39 // Precision
40 u"precision-integer",
41 u".000",
42 u".##/@@@+",
43 u"@@",
44 nullptr,
45 // Rounding Mode
46 u"rounding-mode-floor",
47 nullptr,
48 // Integer Width
49 u"integer-width/##00",
50 nullptr,
51 // Scale
52 u"scale/0.5",
53 nullptr,
54 // Grouping
55 u"group-on-aligned",
56 nullptr,
57 // Symbols
58 u"latin",
59 nullptr,
60 // Sign Display
61 u"sign-accounting-except-zero",
62 nullptr,
63 // Decimal Separator Display
64 u"decimal-always",
65 nullptr,
66 };
67
68 static const double kNumbersToTest[]{0, 91827.3645, -0.22222};
69
70 /**
71 * Test permutations of 3 orthogonal skeleton parts from the list above.
72 * Compare the results against the golden data file:
73 * numberpermutationtest.txt
74 * To regenerate that file, run intltest with the -e and -G options.
75 * On Linux, from icu4c/source:
76 * make -j8 tests && (cd test/intltest && LD_LIBRARY_PATH=../../lib:../../tools/ctestfw ./intltest -e -G format/NumberTest/NumberPermutationTest)
77 * After re-generating the file, copy it into icu4j:
78 * cp test/testdata/numberpermutationtest.txt ../../icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberpermutationtest.txt
79 */
testPermutations()80 void NumberPermutationTest::testPermutations() {
81 IcuTestErrorCode status(*this, "testPermutations");
82
83 const struct LocaleData {
84 Locale locale;
85 const char16_t* ustring;
86 } localesToTest[] = {
87 {"es-MX", u"es-MX"},
88 {"zh-TW", u"zh-TW"},
89 {"bn-BD", u"bn-BD"},
90 };
91
92 // Convert kSkeletonParts to a more convenient data structure
93 auto skeletonParts = std::vector<std::vector<const char16_t*>>();
94 auto currentSection = std::vector<const char16_t*>();
95 for (int32_t i = 0; i < UPRV_LENGTHOF(kSkeletonParts); i++) {
96 const char16_t* skeletonPart = kSkeletonParts[i];
97 if (skeletonPart == nullptr) {
98 skeletonParts.push_back(currentSection);
99 currentSection.clear();
100 } else {
101 currentSection.push_back(skeletonPart);
102 }
103 }
104
105 // Build up the golden data string as we evaluate all permutations
106 std::vector<UnicodeString> resultLines;
107 resultLines.push_back(u"# © 2019 and later: Unicode, Inc. and others.");
108 resultLines.push_back(u"# License & terms of use: http://www.unicode.org/copyright.html");
109 resultLines.push_back(UnicodeString());
110
111 // Take combinations of 3 orthogonal options
112 for (size_t i = 0; i < skeletonParts.size() - 2; i++) {
113 const auto& skeletons1 = skeletonParts[i];
114 for (size_t j = i + 1; j < skeletonParts.size() - 1; j++) {
115 const auto& skeletons2 = skeletonParts[j];
116 for (size_t k = j + 1; k < skeletonParts.size(); k++) {
117 const auto& skeletons3 = skeletonParts[k];
118
119 // Evaluate all combinations of skeletons for these options
120 for (const auto& skel1 : skeletons1) {
121 for (const auto& skel2 : skeletons2) {
122 for (const auto& skel3 : skeletons3) {
123 // Compute the skeleton
124 UnicodeString skeleton;
125 skeleton
126 .append(skel1) //
127 .append(u' ') //
128 .append(skel2) //
129 .append(u' ') //
130 .append(skel3);
131 resultLines.push_back(skeleton);
132
133 // Check several locales and several numbers in each locale
134 for (const auto& locData : localesToTest) {
135 auto lnf = NumberFormatter::forSkeleton(skeleton, status)
136 .locale(locData.locale);
137 resultLines.push_back(UnicodeString(u" ").append(locData.ustring));
138 for (const auto& input : kNumbersToTest) {
139 resultLines.push_back(UnicodeString(u" ").append(
140 lnf.formatDouble(input, status).toTempString(status)));
141 }
142 }
143
144 resultLines.push_back(UnicodeString());
145 }
146 }
147 }
148 }
149
150 // Quick mode: test all fields at least once but stop early.
151 if (quick) {
152 infoln(u"Quick mode: stopped after " + Int64ToUnicodeString(resultLines.size()) +
153 u" lines");
154 goto outerEnd;
155 }
156 }
157 }
158 outerEnd:
159 void();
160
161 CharString goldenFilePath(getSourceTestData(status), status);
162 goldenFilePath.appendPathPart("numberpermutationtest.txt", status);
163
164 // Compare it to the golden file
165 const char* codePage = "UTF-8";
166 LocalUCHARBUFPointer f(ucbuf_open(goldenFilePath.data(), &codePage, TRUE, FALSE, status));
167 if (!assertSuccess("Can't open data file", status)) {
168 return;
169 }
170
171 int32_t lineNumber = 1;
172 int32_t lineLength;
173 for (const auto& actualLine : resultLines) {
174 const UChar* lineBuf = ucbuf_readline(f.getAlias(), &lineLength, status);
175 if (lineBuf == nullptr) {
176 errln("More lines generated than are in the data file!");
177 break;
178 }
179 UnicodeString expectedLine(lineBuf, lineLength - 1);
180 assertEquals(u"Line #" + Int64ToUnicodeString(lineNumber) + u" differs", //
181 expectedLine, actualLine);
182 lineNumber++;
183 }
184 // Quick mode: test all fields at least once but stop early.
185 if (!quick && ucbuf_readline(f.getAlias(), &lineLength, status) != nullptr) {
186 errln("Fewer lines generated than are in the data file!");
187 }
188
189 // Overwrite the golden data if requested
190 if (write_golden_data) {
191 std::ofstream outFile;
192 outFile.open(goldenFilePath.data());
193 for (const auto& uniLine : resultLines) {
194 std::string byteLine;
195 uniLine.toUTF8String(byteLine);
196 outFile << byteLine << std::endl;
197 }
198 outFile.close();
199 }
200 }
201
202 #endif /* #if !UCONFIG_NO_FORMATTING */
203