ICU-20568 Skeleton for Testing Units

PR: https://github.com/sffc/icu/pull/6
Commit: 9b3ca66937c40044055bf9a8e13db57bcdc75fec
diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj
index 96c8726..8ada6ed 100644
--- a/icu4c/source/i18n/i18n.vcxproj
+++ b/icu4c/source/i18n/i18n.vcxproj
@@ -250,6 +250,7 @@
     <ClCompile Include="ufieldpositer.cpp" />
     <ClCompile Include="ulocdata.cpp" />
     <ClCompile Include="umsg.cpp" />
+    <ClCompile Include="unitconverter.cpp" />
     <ClCompile Include="unum.cpp" />
     <ClCompile Include="unumsys.cpp" />
     <ClCompile Include="upluralrules.cpp" />
@@ -484,6 +485,7 @@
     <ClInclude Include="numparse_utils.h" />
     <ClInclude Include="numrange_impl.h" />
     <ClInclude Include="formattedval_impl.h" />
+    <ClInclude Include="unitconverter.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="i18n.rc" />
diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters
index a4ad63a..778c912 100644
--- a/icu4c/source/i18n/i18n.vcxproj.filters
+++ b/icu4c/source/i18n/i18n.vcxproj.filters
@@ -651,6 +651,9 @@
     <ClCompile Include="erarules.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="unitconverter.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="bocsu.cpp">
@@ -995,6 +998,9 @@
     <ClInclude Include="umsg_imp.h">
       <Filter>formatting</Filter>
     </ClInclude>
+    <ClInclude Include="unitconveter.h">
+      <Filter>formatting</Filter>
+    </ClInclude>
     <ClInclude Include="vzone.h">
       <Filter>formatting</Filter>
     </ClInclude>
diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj
index c33be61..9bc6c3d 100644
--- a/icu4c/source/i18n/i18n_uwp.vcxproj
+++ b/icu4c/source/i18n/i18n_uwp.vcxproj
@@ -481,6 +481,7 @@
     <ClCompile Include="ufieldpositer.cpp" />
     <ClCompile Include="ulocdata.cpp" />
     <ClCompile Include="umsg.cpp" />
+    <ClCompile Include="unitconverter.cpp" />
     <ClCompile Include="unum.cpp" />
     <ClCompile Include="unumsys.cpp" />
     <ClCompile Include="upluralrules.cpp" />
@@ -715,6 +716,7 @@
     <ClInclude Include="numparse_utils.h" />
     <ClInclude Include="numrange_impl.h" />
     <ClInclude Include="formattedval_impl.h" />
+    <ClInclude Include="unitconverter.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="i18n.rc" />
diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt
index faaa14e..b9dd1a8 100644
--- a/icu4c/source/i18n/sources.txt
+++ b/icu4c/source/i18n/sources.txt
@@ -208,6 +208,7 @@
 umsg.cpp
 unesctrn.cpp
 uni2name.cpp
+unitconverter.cpp
 unum.cpp
 unumsys.cpp
 upluralrules.cpp
diff --git a/icu4c/source/i18n/unitconverter.cpp b/icu4c/source/i18n/unitconverter.cpp
new file mode 100644
index 0000000..0e28134
--- /dev/null
+++ b/icu4c/source/i18n/unitconverter.cpp
@@ -0,0 +1,19 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+U_NAMESPACE_BEGIN
+
+
+
+
+
+
+
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
\ No newline at end of file
diff --git a/icu4c/source/i18n/unitconverter.h b/icu4c/source/i18n/unitconverter.h
new file mode 100644
index 0000000..9a112f2
--- /dev/null
+++ b/icu4c/source/i18n/unitconverter.h
@@ -0,0 +1,22 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+#ifndef __UNITCONVERTER_H__
+#define __UNITCONVERTER_H__
+
+
+#include <vector>
+
+U_NAMESPACE_BEGIN
+
+
+
+U_NAMESPACE_END
+
+
+#endif //__UNITCONVERTER_H__
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in
index bcab2c9..594d491 100644
--- a/icu4c/source/test/intltest/Makefile.in
+++ b/icu4c/source/test/intltest/Makefile.in
@@ -68,7 +68,8 @@
 string_segment_test.o \
 numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \
 static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \
-formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o
+formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o \
+unitstest.o
 
 DEPS = $(OBJECTS:.o=.d)
 
diff --git a/icu4c/source/test/intltest/intltest.vcxproj b/icu4c/source/test/intltest/intltest.vcxproj
index 6e956d9..4fdaa23 100644
--- a/icu4c/source/test/intltest/intltest.vcxproj
+++ b/icu4c/source/test/intltest/intltest.vcxproj
@@ -285,6 +285,7 @@
     <ClCompile Include="formattedvaluetest.cpp" />
     <ClCompile Include="localebuildertest.cpp" />
     <ClCompile Include="localematchertest.cpp" />
+    <ClCompile Include="unitstest.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="colldata.h" />
diff --git a/icu4c/source/test/intltest/intltest.vcxproj.filters b/icu4c/source/test/intltest/intltest.vcxproj.filters
index 0bf336b..f068ff8 100644
--- a/icu4c/source/test/intltest/intltest.vcxproj.filters
+++ b/icu4c/source/test/intltest/intltest.vcxproj.filters
@@ -550,6 +550,9 @@
     <ClCompile Include="localematchertest.cpp">
       <Filter>locales &amp; resources</Filter>
     </ClCompile>
+    <ClCompile Include="unitstest.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="itrbbi.h">
diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp
index 870728a..9126d52 100644
--- a/icu4c/source/test/intltest/itformat.cpp
+++ b/icu4c/source/test/intltest/itformat.cpp
@@ -74,6 +74,7 @@
 extern IntlTest *createFormattedValueTest();
 extern IntlTest *createFormattedStringBuilderTest();
 extern IntlTest *createStringSegmentTest();
+extern IntlTest *createUnitsTest();
 
 
 #define TESTCLASS(id, TestClass)          \
@@ -247,6 +248,15 @@
             callTest(*test, par);
           }
           break;
+        case 56:
+          name = "UnitsTest";
+          if (exec) {
+            logln("UnitsTest test---");
+            logln((UnicodeString)"");
+            LocalPointer<IntlTest> test(createUnitsTest());
+            callTest(*test, par);
+          }
+          break;
         default: name = ""; break; //needed to end loop
     }
     if (exec) {
diff --git a/icu4c/source/test/intltest/unitstest.cpp b/icu4c/source/test/intltest/unitstest.cpp
new file mode 100644
index 0000000..05a02f7
--- /dev/null
+++ b/icu4c/source/test/intltest/unitstest.cpp
@@ -0,0 +1,163 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+
+#include "intltest.h"
+#include "unicode/unistr.h"
+
+class UnitsTest : public IntlTest {
+  public:
+    UnitsTest() {}
+
+    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = NULL);
+
+    void testBasic();
+    void testSiPrefixes();
+    void testMass();
+    void testTemperature();
+    void testArea();
+};
+
+extern IntlTest *createUnitsTest() { return new UnitsTest(); }
+
+void UnitsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
+    if (exec) {
+        logln("TestSuite UnitsTest: ");
+    }
+    TESTCASE_AUTO_BEGIN;
+    TESTCASE_AUTO(testBasic);
+    TESTCASE_AUTO(testSiPrefixes);
+    TESTCASE_AUTO(testMass);
+    TESTCASE_AUTO(testTemperature);
+    TESTCASE_AUTO(testArea);
+    TESTCASE_AUTO_END;
+}
+
+// Just for testing quick conversion ability.
+double testConvert(UnicodeString source, UnicodeString target, double input) {
+    if (source == u"meter" && target == u"foot" && input == 1.0)
+        return 3.28084;
+
+    if ( source == u"kilometer" && target == u"foot" && input == 1.0)
+        return 328.084;
+
+    return -1;
+}
+
+void UnitsTest::testBasic() {
+    IcuTestErrorCode status(*this, "Units testBasic");
+
+    // Test Cases
+    struct TestCase {
+        const char16_t *source;
+        const char16_t *target;
+        const double inputValue;
+        const double expectedValue;
+    } testCases[]{{u"meter", u"foot", 1.0, 3.28084}, {u"kilometer", u"foot", 1.0, 328.084}};
+
+    for (const auto &testCase : testCases) {
+        assertEquals("test convert", testConvert(testCase.source, testCase.target, testCase.inputValue),
+                     testCase.expectedValue);
+    }
+}
+
+void UnitsTest::testSiPrefixes() {
+    IcuTestErrorCode status(*this, "Units testSiPrefixes");
+    // Test Cases
+    struct TestCase {
+        const char16_t *source;
+        const char16_t *target;
+        const double inputValue;
+        const double expectedValue;
+    } testCases[]{
+        {u"gram", u"kilogram", 1.0, 0.001},            //
+        {u"milligram", u"kilogram", 1.0, 0.000001},    //
+        {u"microgram", u"kilogram", 1.0, 0.000000001}, //
+        {u"megawatt", u"watt", 1, 1000000},            //
+        {u"megawatt", u"kilowatt", 1.0, 1000},         //
+        {u"gigabyte", u"byte", 1, 1000000000}          //
+    };
+
+    for (const auto &testCase : testCases) {
+        assertEquals("test convert", testConvert(testCase.source, testCase.target, testCase.inputValue),
+                     testCase.expectedValue);
+    }
+}
+
+void UnitsTest::testMass() {
+    IcuTestErrorCode status(*this, "Units testMass");
+
+    // Test Cases
+    struct TestCase {
+        const char16_t *source;
+        const char16_t *target;
+        const double inputValue;
+        const double expectedValue;
+    } testCases[]{
+        {u"gram", u"kilogram", 1.0, 0.001},      //
+        {u"pound", u"kilogram", 1.0, 0.453592},  //
+        {u"pound", u"kilogram", 2.0, 0.907185},  //
+        {u"ounce", u"pound", 16.0, 1.0},         //
+        {u"ounce", u"kilogram", 16.0, 0.453592}, //
+        {u"ton", u"pound", 1.0, 2000},           //
+        {u"stone", u"pound", 1.0, 14},           //
+        {u"stone", u"kilogram", 1.0, 6.35029}    //
+    };
+
+    for (const auto &testCase : testCases) {
+        assertEquals("test convert", testConvert(testCase.source, testCase.target, testCase.inputValue),
+                     testCase.expectedValue);
+    }
+}
+
+void UnitsTest::testTemperature() {
+    IcuTestErrorCode status(*this, "Units testTemperature");
+    // Test Cases
+    struct TestCase {
+        const char16_t *source;
+        const char16_t *target;
+        const double inputValue;
+        const double expectedValue;
+    } testCases[]{
+        {u"celsius", u"fahrenheit", 0.0, 32.0},   //
+        {u"celsius", u"fahrenheit", 10.0, 50.0},  //
+        {u"fahrenheit", u"celsius", 32.0, 0.0},   //
+        {u"fahrenheit", u"celsius", 89.6, 32},    //
+        {u"kelvin", u"fahrenheit", 0.0, -459.67}, //
+        {u"kelvin", u"fahrenheit", 300, 80.33},   //
+        {u"kelvin", u"celsius", 0.0, -273.15},    //
+        {u"kelvin", u"celsius", 300.0, 26.85}     //
+    };
+
+    for (const auto &testCase : testCases) {
+        assertEquals("test convert", testConvert(testCase.source, testCase.target, testCase.inputValue),
+                     testCase.expectedValue);
+    }
+}
+
+void UnitsTest::testArea() {
+    IcuTestErrorCode status(*this, "Units Area");
+
+    // Test Cases
+    struct TestCase {
+        const char16_t *source;
+        const char16_t *target;
+        const double inputValue;
+        const double expectedValue;
+    } testCases[]{
+        {u"square-meter", u"square-yard", 10.0, 11.9599}, //
+        {u"hectare", u"square-yard", 1.0, 11959.9},       //
+        {u"square-mile", u"square-foot", 0.0001, 2787.84} //
+    };
+
+    for (const auto &testCase : testCases) {
+        assertEquals("test convert", testConvert(testCase.source, testCase.target, testCase.inputValue),
+                     testCase.expectedValue);
+    }
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */