1 // © 2020 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3
4 #include "unicode/utypes.h"
5
6 #if !UCONFIG_NO_FORMATTING
7
8 #include "cstring.h"
9 #include "measunit_impl.h"
10 #include "unicode/locid.h"
11 #include "units_data.h"
12
13 #include "intltest.h"
14
15 using namespace ::icu::units;
16
17 // These test are no in ICU4J. TODO: consider porting them to Java?
18 class UnitsDataTest : public IntlTest {
19 public:
UnitsDataTest()20 UnitsDataTest() {}
21
22 void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = NULL) override;
23
24 void testGetUnitCategory();
25 // This is a sanity check that only exists in ICU4C.
26 void testGetAllConversionRates();
27 void testGetPreferencesFor();
28 };
29
createUnitsDataTest()30 extern IntlTest *createUnitsDataTest() { return new UnitsDataTest(); }
31
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)32 void UnitsDataTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
33 if (exec) { logln("TestSuite UnitsDataTest: "); }
34 TESTCASE_AUTO_BEGIN;
35 TESTCASE_AUTO(testGetUnitCategory);
36 TESTCASE_AUTO(testGetAllConversionRates);
37 TESTCASE_AUTO(testGetPreferencesFor);
38 TESTCASE_AUTO_END;
39 }
40
testGetUnitCategory()41 void UnitsDataTest::testGetUnitCategory() {
42 struct TestCase {
43 const char *unit;
44 const char *expectedCategory;
45 } testCases[]{
46 {"kilogram-per-cubic-meter", "mass-density"},
47 {"cubic-meter-per-kilogram", "specific-volume"},
48 {"meter-per-second", "speed"},
49 {"second-per-meter", "speed"},
50 // TODO: add this test cases once the `getUnitCategory` accepts any `MeasureUnit` and not only
51 // base units.
52 // Tests are:
53 // {"liter-per-100-kilometer", "consumption"},
54 // {"mile-per-gallon", "consumption"},
55 {"cubic-meter-per-meter", "consumption"},
56 {"meter-per-cubic-meter", "consumption"},
57 {"kilogram-meter-per-square-meter-square-second", "pressure"},
58 };
59
60 IcuTestErrorCode status(*this, "testGetUnitCategory");
61 for (const auto &t : testCases) {
62 CharString category = getUnitQuantity(MeasureUnitImpl::forIdentifier(t.unit, status), status);
63 if (!status.errIfFailureAndReset("getUnitCategory(%s)", t.unit)) {
64 assertEquals("category", t.expectedCategory, category.data());
65 }
66 }
67 }
68
testGetAllConversionRates()69 void UnitsDataTest::testGetAllConversionRates() {
70 IcuTestErrorCode status(*this, "testGetAllConversionRates");
71 MaybeStackVector<ConversionRateInfo> conversionInfo;
72 getAllConversionRates(conversionInfo, status);
73
74 // Convenience output for debugging
75 for (int i = 0; i < conversionInfo.length(); i++) {
76 ConversionRateInfo *cri = conversionInfo[i];
77 logln("* conversionInfo %d: source=\"%s\", baseUnit=\"%s\", factor=\"%s\", offset=\"%s\"", i,
78 cri->sourceUnit.data(), cri->baseUnit.data(), cri->factor.data(), cri->offset.data());
79 assertTrue("sourceUnit", cri->sourceUnit.length() > 0);
80 assertTrue("baseUnit", cri->baseUnit.length() > 0);
81 assertTrue("factor", cri->factor.length() > 0);
82 }
83 }
84
85 class UnitPreferencesOpenedUp : public UnitPreferences {
86 public:
UnitPreferencesOpenedUp(UErrorCode & status)87 UnitPreferencesOpenedUp(UErrorCode &status) : UnitPreferences(status) {}
getInternalMetadata() const88 const MaybeStackVector<UnitPreferenceMetadata> *getInternalMetadata() const { return &metadata_; }
getInternalUnitPrefs() const89 const MaybeStackVector<UnitPreference> *getInternalUnitPrefs() const { return &unitPrefs_; }
90 };
91
92 /**
93 * This test is dependent upon CLDR Data: when the preferences change, the test
94 * may fail: see the constants for expected Max/Min unit identifiers, for US and
95 * World, and for Roads and default lengths.
96 */
testGetPreferencesFor()97 void UnitsDataTest::testGetPreferencesFor() {
98 const char* USRoadMax = "mile";
99 const char* USRoadMin = "foot";
100 const char* USLenMax = "mile";
101 const char* USLenMin = "inch";
102 const char* WorldRoadMax = "kilometer";
103 const char* WorldRoadMin = "meter";
104 const char* WorldLenMax = "kilometer";
105 const char* WorldLenMin = "centimeter";
106 struct TestCase {
107 const char *name;
108 const char *category;
109 const char *usage;
110 const char *region;
111 const char *expectedBiggest;
112 const char *expectedSmallest;
113 } testCases[]{
114 {"US road", "length", "road", "US", USRoadMax, USRoadMin},
115 {"001 road", "length", "road", "001", WorldRoadMax, WorldRoadMin},
116 {"US lengths", "length", "default", "US", USLenMax, USLenMin},
117 {"001 lengths", "length", "default", "001", WorldLenMax, WorldLenMin},
118 {"XX road falls back to 001", "length", "road", "XX", WorldRoadMax, WorldRoadMin},
119 {"XX default falls back to 001", "length", "default", "XX", WorldLenMax, WorldLenMin},
120 {"Unknown usage US", "length", "foobar", "US", USLenMax, USLenMin},
121 {"Unknown usage 001", "length", "foobar", "XX", WorldLenMax, WorldLenMin},
122 {"Fallback", "length", "person-height-xyzzy", "DE", "centimeter", "centimeter"},
123 {"Fallback twice", "length", "person-height-xyzzy-foo", "DE", "centimeter", "centimeter"},
124 // Confirming results for some unitPreferencesTest.txt test cases
125 {"001 area", "area", "default", "001", "square-kilometer", "square-centimeter"},
126 {"GB area", "area", "default", "GB", "square-mile", "square-inch"},
127 {"001 area geograph", "area", "geograph", "001", "square-kilometer", "square-kilometer"},
128 {"GB area geograph", "area", "geograph", "GB", "square-mile", "square-mile"},
129 {"CA person-height", "length", "person-height", "CA", "foot-and-inch", "inch"},
130 {"AT person-height", "length", "person-height", "AT", "meter-and-centimeter",
131 "meter-and-centimeter"},
132 };
133 IcuTestErrorCode status(*this, "testGetPreferencesFor");
134 UnitPreferencesOpenedUp preferences(status);
135 auto *metadata = preferences.getInternalMetadata();
136 auto *unitPrefs = preferences.getInternalUnitPrefs();
137 assertTrue(UnicodeString("Metadata count: ") + metadata->length() + " > 200",
138 metadata->length() > 200);
139 assertTrue(UnicodeString("Preferences count: ") + unitPrefs->length() + " > 250",
140 unitPrefs->length() > 250);
141
142 for (const auto &t : testCases) {
143 logln(t.name);
144 CharString localeID;
145 localeID.append("und-", status); // append undefined language.
146 localeID.append(t.region, status);
147 Locale locale(localeID.data());
148 auto unitPrefs = preferences.getPreferencesFor(t.category, t.usage, locale, status);
149 if (status.errIfFailureAndReset("getPreferencesFor(\"%s\", \"%s\", \"%s\", ...", t.category,
150 t.usage, t.region)) {
151 continue;
152 }
153 if (unitPrefs.length() > 0) {
154 assertEquals(UnicodeString(t.name) + " - max unit", t.expectedBiggest,
155 unitPrefs[0]->unit.data());
156 assertEquals(UnicodeString(t.name) + " - min unit", t.expectedSmallest,
157 unitPrefs[unitPrefs.length() - 1]->unit.data());
158 } else {
159 errln(UnicodeString(t.name) + ": failed to find preferences");
160 }
161 status.errIfFailureAndReset("testCase '%s'", t.name);
162 }
163 }
164
165 #endif /* #if !UCONFIG_NO_FORMATTING */
166