Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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 | |
| 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 | */ |
| 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"; |
Frank Tang | 1f164ee | 2022-11-08 12:31:27 -0800 | [diff] [blame^] | 166 | LocalUCHARBUFPointer f(ucbuf_open(goldenFilePath.data(), &codePage, true, false, status)); |
Frank Tang | 3e05d9d | 2021-11-08 14:04:04 -0800 | [diff] [blame] | 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 */ |