text: // © 2018 and later: Unicode, Inc. and others.
  // License & terms of use: http://www.unicode.org/copyright.html
  
  #include "unicode/utypes.h"
  
  #if !UCONFIG_NO_FORMATTING
  
  // Allow implicit conversion from char16_t* to UnicodeString for this file:
  // Helpful in toString methods and elsewhere.
  #define UNISTR_FROM_STRING_EXPLICIT
  
  #include <stdio.h>
  #include "unicode/unumberformatter.h"
  #include "unicode/unumberrangeformatter.h"
  #include "unicode/umisc.h"
  #include "unicode/unum.h"
  #include "unicode/ustring.h"
  #include "cformtst.h"
  #include "cintltst.h"
  #include "cmemory.h"
  
  static void TestExampleCode(void);
  
  static void TestFormattedValue(void);
  
  static void TestSkeletonParseError(void);
  
  static void TestGetDecimalNumbers(void);
  
  void addUNumberRangeFormatterTest(TestNode** root);
  
  #define TESTCASE(x) addTest(root, &x, "tsformat/unumberrangeformatter/" #x)
  
  void addUNumberRangeFormatterTest(TestNode** root) {
      TESTCASE(TestExampleCode);
      TESTCASE(TestFormattedValue);
      TESTCASE(TestSkeletonParseError);
      TESTCASE(TestGetDecimalNumbers);
  }
  
  
  #define CAPACITY 30
  
  
  static void TestExampleCode() {
      // This is the example code given in unumberrangeformatter.h.
  
      // Setup:
      UErrorCode ec = U_ZERO_ERROR;
      UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
          u"currency/USD precision-integer",
          -1,
          UNUM_RANGE_COLLAPSE_AUTO,
          UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
          "en-US",
          NULL,
          &ec);
      UFormattedNumberRange* uresult = unumrf_openResult(&ec);
      assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
  
      // Format a double range:
      unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec);
      assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
  
      // Get the result string:
      int32_t len;
      const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), &len, &ec);
      assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
      assertUEquals("Should produce expected string result", u"$3 – $5", str);
      int32_t resultLength = str != NULL ? u_strlen(str) : 0;
      assertIntEquals("Length should be as expected", resultLength, len);
  
      // Cleanup:
      unumrf_close(uformatter);
      unumrf_closeResult(uresult);
  }
  
  
  static void TestFormattedValue() {
      UErrorCode ec = U_ZERO_ERROR;
      UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
          u"K",
          -1,
          UNUM_RANGE_COLLAPSE_AUTO,
          UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
          "en-US",
          NULL,
          &ec);
      assertSuccessCheck("Should create without error", &ec, TRUE);
      UFormattedNumberRange* uresult = unumrf_openResult(&ec);
      assertSuccess("Should create result without error", &ec);
  
      // Test the decimal number code path, too
      unumrf_formatDecimalRange(uformatter, "5.5e4", -1, "1.5e5", -1, uresult, &ec);
  
      if (assertSuccessCheck("Should format without error", &ec, TRUE)) {
          const UFormattedValue* fv = unumrf_resultAsValue(uresult, &ec);
          assertSuccess("Should convert without error", &ec);
          static const UFieldPosition expectedFieldPositions[] = {
              // field, begin index, end index
              {UNUM_INTEGER_FIELD, 0, 2},
              {UNUM_COMPACT_FIELD, 2, 3},
              {UNUM_INTEGER_FIELD, 6, 9},
              {UNUM_COMPACT_FIELD, 9, 10}};
          checkFormattedValue(
              "FormattedNumber as FormattedValue",
              fv,
              u"55K – 150K",
              UFIELD_CATEGORY_NUMBER,
              expectedFieldPositions,
              UPRV_LENGTHOF(expectedFieldPositions));
      }
  
      assertIntEquals("Identity result should match",
          UNUM_IDENTITY_RESULT_NOT_EQUAL,
          unumrf_resultGetIdentityResult(uresult, &ec));
  
      // cleanup:
      unumrf_closeResult(uresult);
      unumrf_close(uformatter);
  }
  
  
  static void TestSkeletonParseError() {
      UErrorCode ec = U_ZERO_ERROR;
      UNumberRangeFormatter* uformatter;
      UParseError perror;
  
      // The UParseError can be null. The following should not segfault.
      uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
          u".00 measure-unit/typo",
          -1, 
          UNUM_RANGE_COLLAPSE_AUTO,
          UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
          "en",
          NULL,
          &ec);
      unumrf_close(uformatter);
  
      // Now test the behavior.
      ec = U_ZERO_ERROR;
      uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
          u".00 measure-unit/typo",
          -1, 
          UNUM_RANGE_COLLAPSE_AUTO,
          UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
          "en",
          &perror,
          &ec);
  
      assertIntEquals("Should have set error code", U_NUMBER_SKELETON_SYNTAX_ERROR, ec);
      assertIntEquals("Should have correct skeleton error offset", 17, perror.offset);
      assertUEquals("Should have correct pre context", u"0 measure-unit/", perror.preContext);
      assertUEquals("Should have correct post context", u"typo", perror.postContext);
  
      // cleanup:
      unumrf_close(uformatter);
  }
  
  
  static void TestGetDecimalNumbers() {
      UErrorCode ec = U_ZERO_ERROR;
      UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
          u"currency/USD",
          -1,
          UNUM_RANGE_COLLAPSE_AUTO,
          UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
          "en-US",
          NULL,
          &ec);
      assertSuccessCheck("Should create without error", &ec, TRUE);
      UFormattedNumberRange* uresult = unumrf_openResult(&ec);
      assertSuccess("Should create result without error", &ec);
  
      unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec);
      const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), NULL, &ec);
      assertSuccessCheck("Formatting should succeed", &ec, TRUE);
      assertUEquals("Should produce expected string result", u"$3.00 \u2013 $5.00", str);
  
      char buffer[CAPACITY];
  
      int32_t len = unumrf_resultGetFirstDecimalNumber(uresult, buffer, CAPACITY, &ec);
      assertIntEquals("First len should be as expected", strlen(buffer), len);
      assertEquals("First decimal should be as expected", "3", buffer);
  
      len = unumrf_resultGetSecondDecimalNumber(uresult, buffer, CAPACITY, &ec);
      assertIntEquals("Second len should be as expected", strlen(buffer), len);
      assertEquals("Second decimal should be as expected", "5", buffer);
  
      // cleanup:
      unumrf_closeResult(uresult);
      unumrf_close(uformatter);
  }
  
  #endif /* #if !UCONFIG_NO_FORMATTING */
  