ICU-20138 Implementing ufmtval_nextPosition and additional test infra.

- Adds test infra for multi-category formatted values.
- Adds helper method ConstrainedFieldPosition#matchesField, currently internal.
This commit is contained in:
Shane F. Carr 2019-02-04 14:21:41 -08:00
parent 67461d80dc
commit 9bb910b8d0
11 changed files with 242 additions and 49 deletions

View file

@ -51,6 +51,19 @@ void ConstrainedFieldPosition::setState(
fLimit = limit;
}
UBool ConstrainedFieldPosition::matchesField(UFieldCategory category, int32_t field) {
switch (fConstraint) {
case UCFPOS_CONSTRAINT_NONE:
return TRUE;
case UCFPOS_CONSTRAINT_CATEGORY:
return fCategory == category;
case UCFPOS_CONSTRAINT_FIELD:
return fCategory == category && fField == field;
default:
UPRV_UNREACHABLE;
}
}
FormattedValue::~FormattedValue() = default;
@ -200,6 +213,20 @@ ufmtval_getString(
}
U_DRAFT UBool U_EXPORT2
ufmtval_nextPosition(
const UFormattedValue* ufmtval,
UConstrainedFieldPosition* ucfpos,
UErrorCode* ec) {
const auto* fmtval = number::impl::UFormattedValueApiHelper::validate(ufmtval, *ec);
auto* cfpos = UConstrainedFieldPositionImpl::validate(ucfpos, *ec);
if (U_FAILURE(*ec)) {
return FALSE;
}
return fmtval->fFormattedValue->nextPosition(cfpos->fImpl, *ec);
}
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -221,6 +221,9 @@ class U_I18N_API ConstrainedFieldPosition : public UMemory {
int32_t start,
int32_t limit);
/** @internal */
UBool matchesField(UFieldCategory category, int32_t field);
private:
int64_t fContext = 0LL;
int32_t fField = 0;

View file

@ -54,6 +54,11 @@ typedef enum UFieldCategory {
*/
UFIELD_CATEGORY_LIST,
#ifndef U_HIDE_INTERNAL_API
/** @internal */
UFIELD_CATEGORY_COUNT
#endif
} UFieldCategory;

View file

@ -32,8 +32,14 @@
UChar* myDateFormat(UDateFormat *dat, UDate d);
// The following is implemented in uformattedvaluetest.c
// TODO: When needed, add overload with a different category for each position
typedef struct UFieldPositionWithCategory {
UFieldCategory category;
int32_t field;
int32_t beginIndex;
int32_t endIndex;
} UFieldPositionWithCategory;
// The following are implemented in uformattedvaluetest.c
void checkFormattedValue(
const char* message,
const UFormattedValue* fv,
@ -42,6 +48,13 @@ void checkFormattedValue(
const UFieldPosition* expectedFieldPositions,
int32_t expectedFieldPositionsLength);
void checkMixedFormattedValue(
const char* message,
const UFormattedValue* fv,
const UChar* expectedString,
const UFieldPositionWithCategory* expectedFieldPositions,
int32_t length);
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -170,6 +170,20 @@ static void AssertAllPartsEqual(
}
static void checkFormattedValueString(
const char* message,
const UFormattedValue* fv,
const UChar* expectedString,
UErrorCode* ec) {
int32_t length;
const UChar* actualString = ufmtval_getString(fv, &length, ec);
assertSuccess(message, ec);
// The string is guaranteed to be NUL-terminated.
int32_t actualLength = u_strlen(actualString);
assertIntEquals(message, actualLength, length);
assertUEquals(message, expectedString, actualString);
}
// Declared in cformtst.h
void checkFormattedValue(
const char* message,
@ -178,14 +192,60 @@ void checkFormattedValue(
UFieldCategory expectedCategory,
const UFieldPosition* expectedFieldPositions,
int32_t expectedFieldPositionsLength) {
UErrorCode status = U_ZERO_ERROR;
int32_t length;
const UChar* actualString = ufmtval_getString(fv, &length, &status);
assertSuccess(message, &status);
// The string is guaranteed to be NUL-terminated.
int32_t actualLength = u_strlen(actualString);
assertIntEquals(message, actualLength, length);
assertUEquals(message, expectedString, actualString);
UErrorCode ec = U_ZERO_ERROR;
checkFormattedValueString(message, fv, expectedString, &ec);
if (U_FAILURE(ec)) { return; }
// Basic loop over the fields (more rigorous testing in C++)
UConstrainedFieldPosition* ucfpos = ucfpos_open(&ec);
int32_t i = 0;
while (ufmtval_nextPosition(fv, ucfpos, &ec)) {
assertIntEquals("category",
expectedCategory, ucfpos_getCategory(ucfpos, &ec));
assertIntEquals("field",
expectedFieldPositions[i].field, ucfpos_getField(ucfpos, &ec));
int32_t start, limit;
ucfpos_getIndexes(ucfpos, &start, &limit, &ec);
assertIntEquals("start",
expectedFieldPositions[i].beginIndex, start);
assertIntEquals("limit",
expectedFieldPositions[i].endIndex, limit);
i++;
}
assertTrue("After loop", !ufmtval_nextPosition(fv, ucfpos, &ec));
assertSuccess("After loop", &ec);
ucfpos_close(ucfpos);
}
void checkMixedFormattedValue(
const char* message,
const UFormattedValue* fv,
const UChar* expectedString,
const UFieldPositionWithCategory* expectedFieldPositions,
int32_t length) {
UErrorCode ec = U_ZERO_ERROR;
checkFormattedValueString(message, fv, expectedString, &ec);
if (U_FAILURE(ec)) { return; }
// Basic loop over the fields (more rigorous testing in C++)
UConstrainedFieldPosition* ucfpos = ucfpos_open(&ec);
int32_t i = 0;
while (ufmtval_nextPosition(fv, ucfpos, &ec)) {
assertIntEquals("category",
expectedFieldPositions[i].category, ucfpos_getCategory(ucfpos, &ec));
assertIntEquals("field",
expectedFieldPositions[i].field, ucfpos_getField(ucfpos, &ec));
int32_t start, limit;
ucfpos_getIndexes(ucfpos, &start, &limit, &ec);
assertIntEquals("start",
expectedFieldPositions[i].beginIndex, start);
assertIntEquals("limit",
expectedFieldPositions[i].endIndex, limit);
i++;
}
assertTrue("After loop", !ufmtval_nextPosition(fv, ucfpos, &ec));
assertSuccess("After loop", &ec);
ucfpos_close(ucfpos);
}

View file

@ -211,12 +211,10 @@ static void TestFormattedValue() {
assertSuccess("Should convert without error", &ec);
static const UFieldPosition expectedFieldPositions[] = {
// field, begin index, end index
{UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
{UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
{UNUM_INTEGER_FIELD, 0, 10},
{UNUM_GROUPING_SEPARATOR_FIELD, 13, 14},
{UNUM_GROUPING_SEPARATOR_FIELD, 17, 18},
{UNUM_INTEGER_FIELD, 11, 21}};
{UNUM_INTEGER_FIELD, 0, 2},
{UNUM_DECIMAL_SEPARATOR_FIELD, 2, 3},
{UNUM_FRACTION_FIELD, 3, 5},
{UNUM_COMPACT_FIELD, 5, 6}};
checkFormattedValue(
"FormattedNumber as FormattedValue",
fv,

View file

@ -159,7 +159,39 @@ void IntlTestWithFieldPosition::checkFormattedValue(
UFieldCategory expectedCategory,
const UFieldPosition* expectedFieldPositions,
int32_t length) {
IcuTestErrorCode status(*this, "checkFormattedValue");
LocalArray<UFieldPositionWithCategory> converted(new UFieldPositionWithCategory[length]);
for (int32_t i=0; i<length; i++) {
converted[i].category = expectedCategory;
converted[i].field = expectedFieldPositions[i].field;
converted[i].beginIndex = expectedFieldPositions[i].beginIndex;
converted[i].endIndex = expectedFieldPositions[i].endIndex;
}
checkMixedFormattedValue(message, fv, expectedString, converted.getAlias(), length);
}
UnicodeString CFPosToUnicodeString(const ConstrainedFieldPosition& cfpos) {
UnicodeString sb;
sb.append(u"CFPos[");
sb.append(Int64ToUnicodeString(cfpos.getStart()));
sb.append(u'-');
sb.append(Int64ToUnicodeString(cfpos.getLimit()));
sb.append(u' ');
sb.append(Int64ToUnicodeString(cfpos.getCategory()));
sb.append(u':');
sb.append(Int64ToUnicodeString(cfpos.getField()));
sb.append(u']');
return sb;
}
void IntlTestWithFieldPosition::checkMixedFormattedValue(
const char16_t* message,
const FormattedValue& fv,
UnicodeString expectedString,
const UFieldPositionWithCategory* expectedFieldPositions,
int32_t length) {
IcuTestErrorCode status(*this, "checkMixedFormattedValue");
UnicodeString baseMessage = UnicodeString(message) + u": " + fv.toString(status) + u": ";
// Check string values
@ -173,49 +205,81 @@ void IntlTestWithFieldPosition::checkFormattedValue(
// Check nextPosition over all fields
ConstrainedFieldPosition cfpos;
cfpos.constrainCategory(expectedCategory);
for (int32_t i = 0; i < length; i++) {
assertTrue(baseMessage + i, fv.nextPosition(cfpos, status));
int32_t expectedCategory = expectedFieldPositions[i].category;
int32_t expectedField = expectedFieldPositions[i].field;
int32_t expectedStart = expectedFieldPositions[i].beginIndex;
int32_t expectedLimit = expectedFieldPositions[i].endIndex;
assertEquals(baseMessage + u"category " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"A category " + Int64ToUnicodeString(i),
expectedCategory, cfpos.getCategory());
assertEquals(baseMessage + u"field " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"A field " + Int64ToUnicodeString(i),
expectedField, cfpos.getField());
assertEquals(baseMessage + u"start " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"A start " + Int64ToUnicodeString(i),
expectedStart, cfpos.getStart());
assertEquals(baseMessage + u"limit " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"A limit " + Int64ToUnicodeString(i),
expectedLimit, cfpos.getLimit());
}
assertFalse(baseMessage + u"after loop", fv.nextPosition(cfpos, status));
UBool afterLoopResult = fv.nextPosition(cfpos, status);
assertFalse(baseMessage + u"A after loop: " + CFPosToUnicodeString(cfpos), afterLoopResult);
// Check nextPosition constrained over each field one at a time
std::set<int32_t> uniqueFields;
for (int32_t i = 0; i < length; i++) {
uniqueFields.insert(expectedFieldPositions[i].field);
}
for (int32_t field : uniqueFields) {
// Check nextPosition constrained over each category one at a time
for (int32_t category=0; category<UFIELD_CATEGORY_COUNT; category++) {
cfpos.reset();
cfpos.constrainField(expectedCategory, field);
cfpos.constrainCategory(static_cast<UFieldCategory>(category));
for (int32_t i = 0; i < length; i++) {
if (expectedFieldPositions[i].field != field) {
if (expectedFieldPositions[i].category != category) {
continue;
}
assertTrue(baseMessage + i, fv.nextPosition(cfpos, status));
int32_t expectedCategory = expectedFieldPositions[i].category;
int32_t expectedField = expectedFieldPositions[i].field;
int32_t expectedStart = expectedFieldPositions[i].beginIndex;
int32_t expectedLimit = expectedFieldPositions[i].endIndex;
assertEquals(baseMessage + u"category " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"B category " + Int64ToUnicodeString(i),
expectedCategory, cfpos.getCategory());
assertEquals(baseMessage + u"field " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"B field " + Int64ToUnicodeString(i),
expectedField, cfpos.getField());
assertEquals(baseMessage + u"start " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"B start " + Int64ToUnicodeString(i),
expectedStart, cfpos.getStart());
assertEquals(baseMessage + u"limit " + Int64ToUnicodeString(i),
assertEquals(baseMessage + u"B limit " + Int64ToUnicodeString(i),
expectedLimit, cfpos.getLimit());
}
assertFalse(baseMessage + u"after loop", fv.nextPosition(cfpos, status));
UBool afterLoopResult = fv.nextPosition(cfpos, status);
assertFalse(baseMessage + u"B after loop: " + CFPosToUnicodeString(cfpos), afterLoopResult);
}
// Check nextPosition constrained over each field one at a time
std::set<std::pair<UFieldCategory, int32_t>> uniqueFields;
for (int32_t i = 0; i < length; i++) {
uniqueFields.insert({expectedFieldPositions[i].category, expectedFieldPositions[i].field});
}
for (std::pair<UFieldCategory, int32_t> categoryAndField : uniqueFields) {
cfpos.reset();
cfpos.constrainField(categoryAndField.first, categoryAndField.second);
for (int32_t i = 0; i < length; i++) {
if (expectedFieldPositions[i].category != categoryAndField.first) {
continue;
}
if (expectedFieldPositions[i].field != categoryAndField.second) {
continue;
}
assertTrue(baseMessage + i, fv.nextPosition(cfpos, status));
int32_t expectedCategory = expectedFieldPositions[i].category;
int32_t expectedField = expectedFieldPositions[i].field;
int32_t expectedStart = expectedFieldPositions[i].beginIndex;
int32_t expectedLimit = expectedFieldPositions[i].endIndex;
assertEquals(baseMessage + u"C category " + Int64ToUnicodeString(i),
expectedCategory, cfpos.getCategory());
assertEquals(baseMessage + u"C field " + Int64ToUnicodeString(i),
expectedField, cfpos.getField());
assertEquals(baseMessage + u"C start " + Int64ToUnicodeString(i),
expectedStart, cfpos.getStart());
assertEquals(baseMessage + u"C limit " + Int64ToUnicodeString(i),
expectedLimit, cfpos.getLimit());
}
UBool afterLoopResult = fv.nextPosition(cfpos, status);
assertFalse(baseMessage + u"C after loop: " + CFPosToUnicodeString(cfpos), afterLoopResult);
}
}

View file

@ -26,9 +26,15 @@ class IntlTestFormat: public IntlTest {
};
typedef struct UFieldPositionWithCategory {
UFieldCategory category;
int32_t field;
int32_t beginIndex;
int32_t endIndex;
} UFieldPositionWithCategory;
class IntlTestWithFieldPosition : public IntlTest {
public:
// TODO: When needed, add overload with a different category for each position
void checkFormattedValue(
const char16_t* message,
const FormattedValue& fv,
@ -36,6 +42,13 @@ public:
UFieldCategory expectedCategory,
const UFieldPosition* expectedFieldPositions,
int32_t length);
void checkMixedFormattedValue(
const char16_t* message,
const FormattedValue& fv,
UnicodeString expectedString,
const UFieldPositionWithCategory* expectedFieldPositions,
int32_t length);
};

View file

@ -2980,15 +2980,6 @@ void NumberFormatterApiTest::assertNumberFieldPositions(
expectedFieldPositions,
length);
// Check no field positions in an unrelated category
checkFormattedValue(
message,
static_cast<const FormattedValue&>(formattedNumber),
formattedNumber.toString(status),
UFIELD_CATEGORY_DATE,
nullptr,
0);
// Check FormattedNumber-specific functions
UnicodeString baseMessage = UnicodeString(message) + u": " + formattedNumber.toString(status) + u": ";
FieldPositionIterator fpi;

View file

@ -305,6 +305,22 @@ public class ConstrainedFieldPosition {
fLimit = limit;
}
/** @internal */
public boolean matchesField(Field field) {
// If this method ever becomes public, change assert to throw IllegalArgumentException
assert field != null;
switch (fConstraint) {
case NONE:
return true;
case CLASS:
return fClassConstraint.isAssignableFrom(field.getClass());
case FIELD:
return fField == field;
default:
throw new AssertionError();
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View file

@ -176,7 +176,8 @@ public class FormattedValueTest {
assertEquals(baseMessage + "limit " + i, expectedLimit, cfpos.getLimit());
i++;
}
assertFalse(baseMessage + "after loop", fv.nextPosition(cfpos));
boolean afterLoopResult = fv.nextPosition(cfpos);
assertFalse(baseMessage + "after loop: " + cfpos, afterLoopResult);
// Check nextPosition constrained over each class one at a time
for (Class<?> classConstraint : uniqueFieldClasses) {
@ -196,7 +197,8 @@ public class FormattedValueTest {
assertEquals(baseMessage + "limit " + i, expectedLimit, cfpos.getLimit());
i++;
}
assertFalse(baseMessage + "after loop", fv.nextPosition(cfpos));
afterLoopResult = fv.nextPosition(cfpos);
assertFalse(baseMessage + "after loop: " + cfpos, afterLoopResult);
}
// Check nextPosition constrained over an unrelated class
@ -222,7 +224,8 @@ public class FormattedValueTest {
assertEquals(baseMessage + "limit " + i, expectedLimit, cfpos.getLimit());
i++;
}
assertFalse(baseMessage + "after loop", fv.nextPosition(cfpos));
afterLoopResult = fv.nextPosition(cfpos);
assertFalse(baseMessage + "after loop: " + cfpos, afterLoopResult);
}
}