ICU-13267 Refactoring field position testing logic into helper function.

Makes future field position logic easier to test.
This commit is contained in:
Shane Carr 2018-11-06 17:07:49 -08:00 committed by Shane F. Carr
parent ba21ff79c4
commit 11a559f319
3 changed files with 154 additions and 95 deletions

View file

@ -70,7 +70,7 @@ class NumberFormatterApiTest : public IntlTest {
void scale();
void locale();
void formatTypes();
void fieldPosition();
void fieldPositionLogic();
void toFormat();
void errors();
void validRanges();
@ -111,11 +111,15 @@ class NumberFormatterApiTest : public IntlTest {
void assertFormatDescendingBig(const char16_t* message, const char16_t* skeleton,
const UnlocalizedNumberFormatter& f, Locale locale, ...);
void assertFormatSingle(const char16_t* message, const char16_t* skeleton,
const UnlocalizedNumberFormatter& f, Locale locale, double input,
const UnicodeString& expected);
FormattedNumber
assertFormatSingle(const char16_t* message, const char16_t* skeleton,
const UnlocalizedNumberFormatter& f, Locale locale, double input,
const UnicodeString& expected);
void assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f);
void assertFieldPositions(const char16_t* message, const FormattedNumber& formattedNumber,
const UFieldPosition* expectedFieldPositions, int32_t length);
};
class DecimalQuantityTest : public IntlTest {

View file

@ -84,7 +84,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
TESTCASE_AUTO(scale);
TESTCASE_AUTO(locale);
TESTCASE_AUTO(formatTypes);
TESTCASE_AUTO(fieldPosition);
TESTCASE_AUTO(fieldPositionLogic);
TESTCASE_AUTO(toFormat);
TESTCASE_AUTO(errors);
TESTCASE_AUTO(validRanges);
@ -2164,10 +2164,18 @@ void NumberFormatterApiTest::formatTypes() {
assertEquals("Format decNumber to 40 digits", str, actual);
}
void NumberFormatterApiTest::fieldPosition() {
IcuTestErrorCode status(*this, "fieldPosition");
FormattedNumber fmtd = NumberFormatter::withLocale("en").formatDouble(-9876543210.12, status);
assertEquals("Should have expected format output", u"-9,876,543,210.12", fmtd.toString(status));
void NumberFormatterApiTest::fieldPositionLogic() {
IcuTestErrorCode status(*this, "fieldPositionLogic");
const char16_t* message = u"Field position logic test";
FormattedNumber fmtd = assertFormatSingle(
message,
u"",
NumberFormatter::with(),
Locale::getEnglish(),
-9876543210.12,
u"-9,876,543,210.12");
static const UFieldPosition expectedFieldPositions[] = {
// field, begin index, end index
@ -2179,56 +2187,15 @@ void NumberFormatterApiTest::fieldPosition() {
{UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
{UNUM_FRACTION_FIELD, 15, 17}};
FieldPositionIterator fpi;
fmtd.getAllFieldPositions(fpi, status);
int32_t i = 0;
FieldPosition actual;
while (fpi.next(actual)) {
UFieldPosition expected = expectedFieldPositions[i++];
assertEquals(
UnicodeString(u"Field, case #") + Int64ToUnicodeString(i),
expected.field,
actual.getField());
assertEquals(
UnicodeString(u"Iterator, begin index, case #") + Int64ToUnicodeString(i),
expected.beginIndex,
actual.getBeginIndex());
assertEquals(
UnicodeString(u"Iterator, end index, case #") + Int64ToUnicodeString(i),
expected.endIndex,
actual.getEndIndex());
// Check for the first location of the field
if (expected.field != UNUM_GROUPING_SEPARATOR_FIELD) {
FieldPosition actual2(expected.field);
UBool found = fmtd.nextFieldPosition(actual2, status);
assertEquals(
UnicodeString(u"Next, found first time, case #") + Int64ToUnicodeString(i),
(UBool) TRUE,
found);
assertEquals(
UnicodeString(u"Next, begin index, case #") + Int64ToUnicodeString(i),
expected.beginIndex,
actual2.getBeginIndex());
assertEquals(
UnicodeString(u"Next, end index, case #") + Int64ToUnicodeString(i),
expected.endIndex,
actual2.getEndIndex());
found = fmtd.nextFieldPosition(actual2, status);
assertEquals(
UnicodeString(u"Next, found second time, case #") + Int64ToUnicodeString(i),
(UBool) FALSE,
found);
}
}
assertEquals(
"Should have seen every field position",
sizeof(expectedFieldPositions) / sizeof(*expectedFieldPositions),
i);
assertFieldPositions(
message,
fmtd,
expectedFieldPositions,
sizeof(expectedFieldPositions)/sizeof(*expectedFieldPositions));
// Test the iteration functionality of nextFieldPosition
actual = {UNUM_GROUPING_SEPARATOR_FIELD};
i = 1;
FieldPosition actual = {UNUM_GROUPING_SEPARATOR_FIELD};
int32_t i = 1;
while (fmtd.nextFieldPosition(actual, status)) {
UFieldPosition expected = expectedFieldPositions[i++];
assertEquals(
@ -2614,15 +2581,17 @@ void NumberFormatterApiTest::assertFormatDescendingBig(const char16_t* umessage,
}
}
void NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage, const char16_t* uskeleton,
const UnlocalizedNumberFormatter& f, Locale locale,
double input, const UnicodeString& expected) {
FormattedNumber
NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage, const char16_t* uskeleton,
const UnlocalizedNumberFormatter& f, Locale locale,
double input, const UnicodeString& expected) {
UnicodeString message(TRUE, umessage, -1);
const LocalizedNumberFormatter l1 = f.threshold(0).locale(locale); // no self-regulation
const LocalizedNumberFormatter l2 = f.threshold(1).locale(locale); // all self-regulation
IcuTestErrorCode status(*this, "assertFormatSingle");
status.setScope(message);
UnicodeString actual1 = l1.formatDouble(input, status).toString();
FormattedNumber result1 = l1.formatDouble(input, status);
UnicodeString actual1 = result1.toString();
assertSuccess(message + u": Unsafe Path", status);
assertEquals(message + u": Unsafe Path", expected, actual1);
UnicodeString actual2 = l2.formatDouble(input, status).toString();
@ -2640,6 +2609,7 @@ void NumberFormatterApiTest::assertFormatSingle(const char16_t* umessage, const
} else {
assertUndefinedSkeleton(f);
}
return result1;
}
void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberFormatter& f) {
@ -2651,4 +2621,67 @@ void NumberFormatterApiTest::assertUndefinedSkeleton(const UnlocalizedNumberForm
status);
}
void NumberFormatterApiTest::assertFieldPositions(
const char16_t* message, const FormattedNumber& formattedNumber,
const UFieldPosition* expectedFieldPositions, int32_t length) {
IcuTestErrorCode status(*this, "assertFieldPositions");
UnicodeString baseMessage = UnicodeString(message) + u": " + formattedNumber.toString(status) + u": ";
FieldPositionIterator fpi;
formattedNumber.getAllFieldPositions(fpi, status);
int32_t i = 0;
FieldPosition actual;
while (fpi.next(actual)) {
UFieldPosition expected = expectedFieldPositions[i++];
assertEquals(
baseMessage + UnicodeString(u"Field, case #") + Int64ToUnicodeString(i),
expected.field,
actual.getField());
assertEquals(
baseMessage + UnicodeString(u"Iterator, begin, case #") + Int64ToUnicodeString(i),
expected.beginIndex,
actual.getBeginIndex());
assertEquals(
baseMessage + UnicodeString(u"Iterator, end, case #") + Int64ToUnicodeString(i),
expected.endIndex,
actual.getEndIndex());
// Check for the first location of the field
FieldPosition actual2(expected.field);
// Fast-forward the field to skip previous occurrences of the field:
actual2.setBeginIndex(expected.beginIndex);
actual2.setEndIndex(expected.beginIndex);
UBool found = formattedNumber.nextFieldPosition(actual2, status);
assertEquals(
baseMessage + UnicodeString(u"Next, found first, case #") + Int64ToUnicodeString(i),
(UBool) TRUE,
found);
assertEquals(
baseMessage + UnicodeString(u"Next, begin, case #") + Int64ToUnicodeString(i),
expected.beginIndex,
actual2.getBeginIndex());
assertEquals(
baseMessage + UnicodeString(u"Next, end, case #") + Int64ToUnicodeString(i),
expected.endIndex,
actual2.getEndIndex());
// The next position should be empty unless the field occurs again
UBool occursAgain = false;
for (int32_t j=i; j<length; j++) {
if (expectedFieldPositions[j].field == expected.field) {
occursAgain = true;
break;
}
}
if (!occursAgain) {
found = formattedNumber.nextFieldPosition(actual2, status);
assertEquals(
baseMessage + UnicodeString(u"Next, found second, case #") + Int64ToUnicodeString(i),
(UBool) FALSE,
found);
}
}
assertEquals(baseMessage + u"Should have seen every field position", length, i);
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -2144,9 +2144,16 @@ public class NumberFormatterApiTest {
}
@Test
public void fieldPosition() {
FormattedNumber fmtd = NumberFormatter.withLocale(ULocale.ENGLISH).format(-9876543210.12);
assertEquals("Should have expected format output", "-9,876,543,210.12", fmtd.toString());
public void fieldPositionLogic() {
String message = "Field position logic test";
FormattedNumber fmtd = assertFormatSingle(
message,
"",
NumberFormatter.with(),
ULocale.ENGLISH,
-9876543210.12,
"-9,876,543,210.12");
Object[][] expectedFieldPositions = new Object[][]{
{NumberFormat.Field.SIGN, 0, 1},
@ -2157,38 +2164,11 @@ public class NumberFormatterApiTest {
{NumberFormat.Field.DECIMAL_SEPARATOR, 14, 15},
{NumberFormat.Field.FRACTION, 15, 17}};
AttributedCharacterIterator fpi = fmtd.getFieldIterator();
Set<AttributedCharacterIterator.Attribute> allAttributes = fpi.getAllAttributeKeys();
assertEquals("All known fields should be in the iterator", 5, allAttributes.size());
assertEquals("Iterator should have length of string output", 17, fpi.getEndIndex());
int i = 0;
for (char c = fpi.first(); c != AttributedCharacterIterator.DONE; c = fpi.next(), i++) {
Set<AttributedCharacterIterator.Attribute> currentAttributes = fpi.getAttributes().keySet();
int attributesRemaining = currentAttributes.size();
for (Object[] cas : expectedFieldPositions) {
NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
int expectedBeginIndex = (Integer) cas[1];
int expectedEndIndex = (Integer) cas[2];
if (expectedBeginIndex > i || expectedEndIndex <= i) {
// Field position does not overlap with the current character
continue;
}
assertTrue("Current character should have expected field", currentAttributes.contains(expectedField));
assertTrue("Field should be a known attribute", allAttributes.contains(expectedField));
int actualBeginIndex = fpi.getRunStart(expectedField);
int actualEndIndex = fpi.getRunLimit(expectedField);
assertEquals(expectedField + " begin index @" + i, expectedBeginIndex, actualBeginIndex);
assertEquals(expectedField + " end index @" + i, expectedEndIndex, actualEndIndex);
attributesRemaining--;
}
assertEquals("Should have looked at every field", 0, attributesRemaining);
}
assertEquals("Should have looked at every character", 17, i);
assertFieldPositions(message, fmtd, expectedFieldPositions);
// Test the iteration functionality of nextFieldPosition
FieldPosition actual = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
i = 1;
int i = 1;
while (fmtd.nextFieldPosition(actual)) {
Object[] cas = expectedFieldPositions[i++];
NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
@ -2444,7 +2424,7 @@ public class NumberFormatterApiTest {
}
}
static void assertFormatSingle(
static FormattedNumber assertFormatSingle(
String message,
String skeleton,
UnlocalizedNumberFormatter f,
@ -2453,7 +2433,8 @@ public class NumberFormatterApiTest {
String expected) {
LocalizedNumberFormatter l1 = f.threshold(0L).locale(locale); // no self-regulation
LocalizedNumberFormatter l2 = f.threshold(1L).locale(locale); // all self-regulation
String actual1 = l1.format(input).toString();
FormattedNumber result1 = l1.format(input);
String actual1 = result1.toString();
assertEquals(message + ": Unsafe Path: " + input, expected, actual1);
String actual2 = l2.format(input).toString();
assertEquals(message + ": Safe Path: " + input, expected, actual2);
@ -2468,6 +2449,7 @@ public class NumberFormatterApiTest {
} else {
assertUndefinedSkeleton(f);
}
return result1;
}
static void assertFormatSingleMeasure(
@ -2502,4 +2484,44 @@ public class NumberFormatterApiTest {
fail("Expected toSkeleton to fail, but it passed, producing: " + skeleton);
} catch (UnsupportedOperationException expected) {}
}
static void assertFieldPositions(String message, FormattedNumber formattedNumber, Object[][] expectedFieldPositions) {
// Calculate some initial expected values
int stringLength = formattedNumber.toString().length();
HashSet<Format.Field> uniqueFields = new HashSet<>();
for (int i=0; i<expectedFieldPositions.length; i++) {
uniqueFields.add((Format.Field) expectedFieldPositions[i][0]);
}
String baseMessage = message + ": " + formattedNumber.toString() + ": ";
// Check the AttributedCharacterIterator
AttributedCharacterIterator fpi = formattedNumber.getFieldIterator();
Set<AttributedCharacterIterator.Attribute> allAttributes = fpi.getAllAttributeKeys();
assertEquals(baseMessage + "All known fields should be in the iterator", uniqueFields.size(), allAttributes.size());
assertEquals(baseMessage + "Iterator should have length of string output", stringLength, fpi.getEndIndex());
int i = 0;
for (char c = fpi.first(); c != AttributedCharacterIterator.DONE; c = fpi.next(), i++) {
Set<AttributedCharacterIterator.Attribute> currentAttributes = fpi.getAttributes().keySet();
int attributesRemaining = currentAttributes.size();
for (Object[] cas : expectedFieldPositions) {
NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
int expectedBeginIndex = (Integer) cas[1];
int expectedEndIndex = (Integer) cas[2];
if (expectedBeginIndex > i || expectedEndIndex <= i) {
// Field position does not overlap with the current character
continue;
}
assertTrue(baseMessage + "Current character should have expected field", currentAttributes.contains(expectedField));
assertTrue(baseMessage + "Field should be a known attribute", allAttributes.contains(expectedField));
int actualBeginIndex = fpi.getRunStart(expectedField);
int actualEndIndex = fpi.getRunLimit(expectedField);
assertEquals(baseMessage + expectedField + " begin @" + i, expectedBeginIndex, actualBeginIndex);
assertEquals(baseMessage + expectedField + " end @" + i, expectedEndIndex, actualEndIndex);
attributesRemaining--;
}
assertEquals(baseMessage + "Should have looked at every field", 0, attributesRemaining);
}
assertEquals(baseMessage + "Should have looked at every character", stringLength, i);
}
}