From c1e3b6853505b4f17c1c84fa3ca277cc2d54c74d Mon Sep 17 00:00:00 2001 From: "Steven R. Loomis" Date: Fri, 10 Aug 2007 02:09:24 +0000 Subject: [PATCH] ICU-4567 data driven format test X-SVN-Rev: 22335 --- .gitattributes | 5 + icu4c/source/test/intltest/Makefile.in | 2 +- icu4c/source/test/intltest/dadrcal.cpp | 225 +------------ icu4c/source/test/intltest/dadrcal.h | 1 + icu4c/source/test/intltest/dadrfmt.cpp | 312 +++++++++++++++++ icu4c/source/test/intltest/dadrfmt.h | 52 +++ icu4c/source/test/intltest/fldset.cpp | 315 ++++++++++++++++++ icu4c/source/test/intltest/fldset.h | 184 ++++++++++ icu4c/source/test/intltest/itformat.cpp | 4 +- icu4c/source/test/testdata/format.txt | 37 ++ icu4c/source/test/testdata/tstfiles.mk | 3 +- icu4c/source/tools/ctestfw/udbgutil.cpp | 24 ++ icu4c/source/tools/ctestfw/unicode/udbgutil.h | 1 + 13 files changed, 943 insertions(+), 222 deletions(-) create mode 100644 icu4c/source/test/intltest/dadrfmt.cpp create mode 100644 icu4c/source/test/intltest/dadrfmt.h create mode 100644 icu4c/source/test/intltest/fldset.cpp create mode 100644 icu4c/source/test/intltest/fldset.h create mode 100644 icu4c/source/test/testdata/format.txt diff --git a/.gitattributes b/.gitattributes index 877c05f748e..d25b2fb67e1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -143,6 +143,10 @@ icu4c/source/test/compat/tzdate.c -text icu4c/source/test/compat/tzone.pl -text icu4c/source/test/intltest/dadrcal.cpp -text icu4c/source/test/intltest/dadrcal.h -text +icu4c/source/test/intltest/dadrfmt.cpp -text +icu4c/source/test/intltest/dadrfmt.h -text +icu4c/source/test/intltest/fldset.cpp -text +icu4c/source/test/intltest/fldset.h -text icu4c/source/test/intltest/tzrulets.h -text icu4c/source/test/perf/charperf/CharPerf_r.pl -text icu4c/source/test/perf/convperf/ConvPerf_r.pl -text @@ -173,6 +177,7 @@ icu4c/source/test/perf/ubrkperf/UBrkPerf_r.pl -text icu4c/source/test/perf/ustrperf/StringPerf_r.pl -text icu4c/source/test/testdata/TestFont1.otf -text icu4c/source/test/testdata/calendar.txt -text +icu4c/source/test/testdata/format.txt -text icu4c/source/test/testdata/icu26_testtypes.res -text icu4c/source/test/testdata/icu26e_testtypes.res -text icu4c/source/test/testdata/importtest.bin -text diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index 9e66e55162b..0e6964e78b8 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -41,7 +41,7 @@ LIBS = $(LIBCTESTFW) $(LIBICUI18N) $(LIBICUUC) $(LIBICUTOOLUTIL) $(DEFAULT_LIBS) OBJECTS = aliastst.o allcoll.o apicoll.o astrotst.o callimts.o calregts.o caltest.o \ caltztst.o canittst.o citrtest.o cntabcol.o convtest.o currcoll.o \ -dadrcal.o dadrcoll.o dcfmapts.o decoll.o dtfmapts.o dtfmrgts.o dtfmtrtts.o dtfmttst.o \ +fldset.o dadrfmt.o dadrcal.o dadrcoll.o dcfmapts.o decoll.o dtfmapts.o dtfmrgts.o dtfmtrtts.o dtfmttst.o \ dtptngts.o encoll.o escoll.o ficoll.o frcoll.o g7coll.o intltest.o \ itercoll.o itformat.o itmajor.o itutil.o jacoll.o lcukocol.o \ loctest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \ diff --git a/icu4c/source/test/intltest/dadrcal.cpp b/icu4c/source/test/intltest/dadrcal.cpp index cc3e269cd85..da2e567abcf 100644 --- a/icu4c/source/test/intltest/dadrcal.cpp +++ b/icu4c/source/test/intltest/dadrcal.cpp @@ -27,219 +27,6 @@ #include -/** ------- Calendar Fields Set -------- **/ -class CalendarFieldsSet { - public: - CalendarFieldsSet(); - ~CalendarFieldsSet(); - - void clear(); - void clear(UCalendarDateFields field); - void set(UCalendarDateFields field, int32_t amount); - - UBool isSet(UCalendarDateFields field) const; - int32_t get(UCalendarDateFields field) const; - - /** - * @param diffSet fillin to hold any fields different. Will have the calendar's value set on them. - * @return true if the calendar matches in these fields. - */ - UBool matches(Calendar *cal, CalendarFieldsSet &diffSet, - UErrorCode& status) const; - - /** - * set the specified fields on this calendar. Doesn't clear first. Returns any errors the cale - */ - void setOnCalendar(Calendar *cal, UErrorCode& status) const; - - /** - * @param "expected" set to match against - * @return a formatted string listing which fields are set in - * this, with the comparison made agaainst those fields in other. - */ - UnicodeString diffFrom(const CalendarFieldsSet& other) const; - /** - * parse from a string. - * @param str the string to parse - * @param inherit the CalendarFieldsSet to inherit any inherited items from, or NULL - * @param status error code - * @return the number of items successfully parsed - */ - int32_t parseFrom(const UnicodeString& str, - const CalendarFieldsSet* inheritFrom, UErrorCode& status); - int32_t parseFrom(const UnicodeString& str, UErrorCode& status); - - private: - int32_t fValue[UDAT_FIELD_COUNT]; /** field values **/ - UBool fIsSet[UDAT_FIELD_COUNT]; /** Is this field set? **/ -}; - -CalendarFieldsSet::CalendarFieldsSet() { - clear(); -} - -CalendarFieldsSet::~CalendarFieldsSet() { - // -} - -void CalendarFieldsSet::clear() { - for (int i=0; i=UCAL_FIELD_COUNT) { - return; - } - fValue[field] = -1; - fIsSet[field] = FALSE; -} -void CalendarFieldsSet::set(UCalendarDateFields field, int32_t amount) { - if (field<0|| field>=UCAL_FIELD_COUNT) { - return; - } - fValue[field] = amount; - fIsSet[field] = TRUE; -} - -UBool CalendarFieldsSet::isSet(UCalendarDateFields field) const { - if (field<0|| field>=UCAL_FIELD_COUNT) { - return FALSE; - } - return fIsSet[field]; -} -int32_t CalendarFieldsSet::get(UCalendarDateFields field) const { - if (field<0|| field>=UCAL_FIELD_COUNT) { - return -1; - } - return fValue[field]; -} - -/** - * set the specified fields on this calendar. Doesn't clear first. Returns any errors the caller - */ -void CalendarFieldsSet::setOnCalendar(Calendar *cal, UErrorCode& /*status*/) const { - for (int i=0; iset((UCalendarDateFields)i, value); - } - } -} - -/** - * return true if the calendar matches in these fields - */ -UBool CalendarFieldsSet::matches(Calendar *cal, CalendarFieldsSet &diffSet, - UErrorCode& status) const { - UBool match = TRUE; - if (U_FAILURE(status)) - return FALSE; - for (int i=0; iget((UCalendarDateFields)i, status); - if (U_FAILURE(status)) - return FALSE; - if (calVal != get((UCalendarDateFields)i)) { - match = FALSE; - diffSet.set((UCalendarDateFields)i, calVal); - //fprintf(stderr, "match failed: %s#%d=%d != %d\n",udbg_enumName(UDBG_UCalendarDateFields,i),i,cal->get((UCalendarDateFields)i,status), get((UCalendarDateFields)i));; - } - } - } - return match; -} - -UnicodeString CalendarFieldsSet::diffFrom(const CalendarFieldsSet& other) const { - UnicodeString str; - for (int i=0; iisSet((UCalendarDateFields)field)) { - // couldn't inherit from field - fprintf(stderr,"Parse Failed: Couldn't inherit field %d [%s]\n", field, udbg_enumName(UDBG_UCalendarDateFields, field)); - status = U_ILLEGAL_ARGUMENT_ERROR; - return -1; - } - value = inheritFrom->get((UCalendarDateFields)field); - } else { - if(field==UCAL_MONTH) { - value = udbg_enumByString(UDBG_UCalendarMonths, kv[1]); - } // else, other types.. - if(value==-1) { - // parse as decimal - value = udbg_stoi(kv[1]); - } - } - //fprintf(stderr, "Parsed: %s#%d=%d\n",udbg_enumName(UDBG_UCalendarDateFields,field),field,value); - set((UCalendarDateFields)field,value); - } - - return -1; -} - DataDrivenCalendarTest::DataDrivenCalendarTest() { UErrorCode status = U_ZERO_ERROR; driver = TestDataModule::getTestDataModule("calendar", *this, status); @@ -275,7 +62,7 @@ void DataDrivenCalendarTest::runIndexedTest(int32_t index, UBool exec, name = ""; } } else { - errln("format/DataDrivenTest data (calendar.res) not initialized!"); + errln("format/DataDriven*Test data (calendar.res) not initialized!"); name = ""; } @@ -413,7 +200,7 @@ void DataDrivenCalendarTest::testOps(TestData *testData, diffSet.clear(); // Is the calendar sane after being set? if (!fromSet.matches(fromCalendar, diffSet, status)) { - UnicodeString diffs = diffSet.diffFrom(fromSet); + UnicodeString diffs = diffSet.diffFrom(fromSet, status); errln((UnicodeString)"FAIL: "+caseString +", SET SOURCE calendar was not set: Differences: "+ diffs +"', status: "+ u_errorName(status)); @@ -453,7 +240,7 @@ void DataDrivenCalendarTest::testOps(TestData *testData, diffSet.clear(); if (!toSet.matches(toCalendar, diffSet, status)) { - UnicodeString diffs = diffSet.diffFrom(toSet); + UnicodeString diffs = diffSet.diffFrom(toSet, status); errln((UnicodeString)"FAIL: "+caseString+" - , "+caseContentsString +" Differences: "+ diffs +"', status: " + u_errorName(status)); @@ -489,7 +276,7 @@ void DataDrivenCalendarTest::testConvert(int32_t n, diffSet.clear(); // Is the calendar sane at the first? if (!fromSet.matches(fromCalendar, diffSet, status)) { - UnicodeString diffs = diffSet.diffFrom(fromSet); + UnicodeString diffs = diffSet.diffFrom(fromSet, status); errln((UnicodeString)"FAIL: "+thisString +", SOURCE calendar was not set: Differences: "+ diffs +"', status: "+ u_errorName(status)); @@ -511,7 +298,7 @@ void DataDrivenCalendarTest::testConvert(int32_t n, diffSet.clear(); // Is the calendar sane after being set? if (!fromSet.matches(fromCalendar, diffSet, status)) { - UnicodeString diffs = diffSet.diffFrom(fromSet); + UnicodeString diffs = diffSet.diffFrom(fromSet, status); errln((UnicodeString)"FAIL: "+thisString +", SET SOURCE calendar was not set: Differences: "+ diffs +"', status: "+ u_errorName(status)); @@ -531,7 +318,7 @@ void DataDrivenCalendarTest::testConvert(int32_t n, diffSet.clear(); if (!toSet.matches(toCalendar, diffSet, status)) { - UnicodeString diffs = diffSet.diffFrom(toSet); + UnicodeString diffs = diffSet.diffFrom(toSet, status); errln((UnicodeString)"FAIL: "+thisString+", Differences: "+ diffs +"', status: "+ u_errorName(status)); SimpleDateFormat fmt(UnicodeString("EEE MMM dd yyyy G"), status); diff --git a/icu4c/source/test/intltest/dadrcal.h b/icu4c/source/test/intltest/dadrcal.h index 785b3e89384..3d59494ccc7 100644 --- a/icu4c/source/test/intltest/dadrcal.h +++ b/icu4c/source/test/intltest/dadrcal.h @@ -20,6 +20,7 @@ #include "tsdate.h" #include "uvector.h" #include "unicode/calendar.h" +#include "fldset.h" class TestDataModule; class TestData; diff --git a/icu4c/source/test/intltest/dadrfmt.cpp b/icu4c/source/test/intltest/dadrfmt.cpp new file mode 100644 index 00000000000..b51f037c164 --- /dev/null +++ b/icu4c/source/test/intltest/dadrfmt.cpp @@ -0,0 +1,312 @@ +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 1997-2007, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/*********************************************************************** + * Modification history + * Date Name Description + * 07/09/2007 srl Copied from dadrcoll.cpp + ***********************************************************************/ + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "unicode/tstdtmod.h" +#include "tsdate.h" +#include "dadrfmt.h" +#include "unicode/calendar.h" +#include "intltest.h" +#include +#include "unicode/schriter.h" +#include "unicode/regex.h" +#include "unicode/smpdtfmt.h" +#include "unicode/dbgutil.h" +#include "fldset.h" + + +#include + +DataDrivenFormatTest::DataDrivenFormatTest() { + UErrorCode status = U_ZERO_ERROR; + driver = TestDataModule::getTestDataModule("format", *this, status); +} + +DataDrivenFormatTest::~DataDrivenFormatTest() { + delete driver; +} + +void DataDrivenFormatTest::runIndexedTest(int32_t index, UBool exec, + const char* &name, char* /*par */) { + if (driver != NULL) { + if (exec) { + // logln("Begin "); + } + const DataMap *info= NULL; + UErrorCode status= U_ZERO_ERROR; + TestData *testData = driver->createTestData(index, status); + if (U_SUCCESS(status)) { + name = testData->getName(); + if (testData->getInfo(info, status)) { + log(info->getString("Description", status)); + } + if (exec) { + log(name); + logln("---"); + logln(""); + + processTest(testData); + } + delete testData; + } else { + name = ""; + } + } else { + errln("format/DataDriven*Test data (format.res) not initialized!"); + name = ""; + } + +} + + + +/* + * Headers { "locale","spec", "date", "str"} + // locale: locale including calendar type + // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' + // date: either an unsigned long (millis), or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale + // str: the expected unicode string + Cases { + { + "en_US@calendar=gregorian", + "DATE=SHORT,TIME=SHORT", + "ERA=1,YEAR=2007,MONTH=AUGUST,DATE=8,HOUR=18,MINUTE=54,SECOND=12", + "8/8/2007 6:54pm" + }, + * */ + + +void DataDrivenFormatTest::testConvertDate(TestData *testData, + const DataMap */* settings */, UBool fmt) { + UnicodeString kPATTERN("PATTERN="); // TODO: static + UnicodeString kMILLIS("MILLIS="); // TODO: static + + UErrorCode status = U_ZERO_ERROR; + SimpleDateFormat basicFmt(UnicodeString("EEE MMM dd yyyy / YYYY'-W'ww-ee"), + status); + if (U_FAILURE(status)) { + errln("FAIL: Couldn't create basic SimpleDateFormat: %s\n", + u_errorName(status)); + return; + } + + const DataMap *currentCase= NULL; + // Start the processing + int n = 0; + while (testData->nextCase(currentCase, status)) { + char calLoc[256] = ""; + DateTimeStyleSet styleSet; + UnicodeString pattern; + UBool usePattern = FALSE; + CalendarFieldsSet fromSet; + UDate fromDate; + UBool useDate = FALSE; + + + + ++n; + + char theCase[200]; + sprintf(theCase, "case %d:", n); + UnicodeString caseString(theCase, ""); + + // load params + UnicodeString locale = currentCase->getString("locale", status); + if (U_FAILURE(status)) { + errln("case %d: No 'locale' line.", n); + continue; + } + UnicodeString spec = currentCase->getString("spec", status); + if(U_FAILURE(status)) { + errln("case %d: No 'spec' line.", n); + continue; + } + UnicodeString date = currentCase->getString("date", status); + if(U_FAILURE(status)) { + errln("case %d: No 'date' line.", n); + continue; + } + UnicodeString expectStr= currentCase->getString("str", status); + if(U_FAILURE(status)) { + errln("case %d: No 'str' line.", n); + continue; + } + + DateFormat *format = NULL; + + // Process: 'locale' + locale.extract(0, locale.length(), calLoc, (const char*)0); // default codepage. Invariant codepage doesn't have '@'! + Locale loc(calLoc); + if(spec.startsWith(kPATTERN)) { + pattern = UnicodeString(spec,kPATTERN.length()); + usePattern = TRUE; + format = new SimpleDateFormat(pattern, loc, status); + if(U_FAILURE(status)) { + errln("case %d: could not create SimpleDateFormat from pattern: %s", n, u_errorName(status)); + continue; + } + } else { + if(styleSet.parseFrom(spec, status)<0 || U_FAILURE(status)) { + errln("case %d: could not parse spec as style fields: %s", n, u_errorName(status)); + continue; + } + format = DateFormat::createDateTimeInstance((DateFormat::EStyle)styleSet.getDateStyle(), (DateFormat::EStyle)styleSet.getTimeStyle(), loc); + if(format == NULL ) { + errln("case %d: could not create SimpleDateFormat from styles.", n); + continue; + } + } + + // parse 'date' + if(date.startsWith(kMILLIS)) { + UnicodeString millis = UnicodeString(date, kMILLIS.length()); + useDate = TRUE; + fromDate = udbg_stoi(millis); + } else { + if(fromSet.parseFrom(date, status)<0 || U_FAILURE(status)) { + errln("case %d: could not parse date as calendar fields: %s", n, u_errorName(status)); + continue; + } + } + + Calendar *cal = Calendar::createInstance(loc, status); + if(U_FAILURE(status)) { + errln("case %d: could not create calendar from %s", n, calLoc); + } + // now, do it. + if (fmt) { + FieldPosition pos; +// logln((UnicodeString)"#"+n+" "+locale+"/"+from+" >>> "+toCalLoc+"/" +// +to); + cal->clear(); + UnicodeString output; + + if(useDate) { +// cal->setTime(fromDate, status); +// if(U_FAILURE(status)) { +// errln("case %d: could not set time on calendar: %s", n, u_errorName(status)); +// continue; +// } + format->format(fromDate, output, pos, status); + } else { + fromSet.setOnCalendar(cal, status); + if(U_FAILURE(status)) { + errln("case %d: could not set fields on calendar: %s", n, u_errorName(status)); + continue; + } + format->format(*cal, output, pos); + } + + // check erro result from 'format' + if(U_FAILURE(status)) { + errln("case %d: could not format(): %s", n, u_errorName(status)); // TODO: use 'pos' + } +// if(pos.getBeginIndex()==0 && pos.getEndIndex()==0) { // TODO: more precise error? +// errln("WARNING: case %d: format's pos returned (0,0) - error ??", n); +// } + + if(output == expectStr) { + logln(caseString+": format: SUCCESS! "+UnicodeString("expect=output=")+output); + } else { + errln(caseString+": format: output!=expectStr, got " + output + " expected " + expectStr); + } + } else { + cal->clear(); + ParsePosition pos; + format->parse(expectStr,*cal,pos); + if(useDate) { + UDate gotDate = cal->getTime(status); + if(U_FAILURE(status)) { + errln(caseString+": parse: could not get time on calendar: "+UnicodeString(u_errorName(status))); + continue; + } + if(gotDate == fromDate) { + logln(caseString+": parse: SUCCESS! "+UnicodeString("gotDate=parseDate=")+expectStr); + } else { + UnicodeString expectDateStr, gotDateStr; + basicFmt.format(fromDate,expectDateStr); + basicFmt.format(gotDate,gotDateStr); + errln(caseString+": parse: FAIL. parsed '"+expectStr+"' and got "+gotDateStr+", expected " + expectDateStr); + } + } else { +// Calendar *cal2 = cal->clone(); +// cal2->clear(); +// fromSet.setOnCalendar(cal2, status); + if(U_FAILURE(status)) { + errln("case %d: parse: could not set fields on calendar: %s", n, u_errorName(status)); + continue; + } + + CalendarFieldsSet diffSet; +// diffSet.clear(); + if (!fromSet.matches(cal, diffSet, status)) { + UnicodeString diffs = diffSet.diffFrom(fromSet, status); + errln((UnicodeString)"FAIL: "+caseString + +", Differences: '"+ diffs + +"', status: "+ u_errorName(status)); + } else if (U_FAILURE(status)) { + errln("FAIL: "+caseString+" parse SET SOURCE calendar Failed to match: " + +u_errorName(status)); + } else { + logln("PASS: "+caseString+" parse."); + } + + + + } + } + delete cal; + delete format; + + } +// delete basicFmt; +} + +void DataDrivenFormatTest::processTest(TestData *testData) { + //Format *cal= NULL; + //const UChar *arguments= NULL; + //int32_t argLen = 0; + char testType[256]; + const DataMap *settings= NULL; + //const UChar *type= NULL; + UErrorCode status = U_ZERO_ERROR; + UnicodeString testSetting; + int n = 0; + while (testData->nextSettings(settings, status)) { + status = U_ZERO_ERROR; + // try to get a locale + testSetting = settings->getString("Type", status); + if (U_SUCCESS(status)) { + if ((++n)>0) { + logln("---"); + } + logln(testSetting + "---"); + testSetting.extract(0, testSetting.length(), testType, ""); + } else { + errln("Unable to extract 'Type'. Skipping.."); + continue; + } + + if (!strcmp(testType, "date_format")) { + testConvertDate(testData, settings, true); + } else if (!strcmp(testType, "date_parse")) { + testConvertDate(testData, settings, false); + } else { + errln("Unknown type: %s", testType); + } + } +} + +#endif diff --git a/icu4c/source/test/intltest/dadrfmt.h b/icu4c/source/test/intltest/dadrfmt.h new file mode 100644 index 00000000000..77f098bbe56 --- /dev/null +++ b/icu4c/source/test/intltest/dadrfmt.h @@ -0,0 +1,52 @@ +/******************************************************************** + * COPYRIGHT: + * Copyright (c) 2007, International Business Machines Corporation and + * others. All Rights Reserved. + ********************************************************************/ + +/** + * DataDrivenFormatTest is a test class that uses data stored in resource + * bundles to perform testing. For more details on data structure, see + * source/test/testdata/calendar.txt + */ + +#ifndef _INTLTESTDATADRIVENFORMAT +#define _INTLTESTDATADRIVENFORMAT + +#include "unicode/utypes.h" + +#if !UCONFIG_NO_FORMATTING + +#include "tsdate.h" +#include "uvector.h" +#include "unicode/format.h" +//#include "fldset.h" + +class TestDataModule; +class TestData; +class DataMap; +//class DateTimeStyle; + +class DataDrivenFormatTest : public IntlTest { + void runIndexedTest(int32_t index, UBool exec, const char* &name, + char* par = NULL); +public: + DataDrivenFormatTest(); + virtual ~DataDrivenFormatTest(); +protected: + + void DataDrivenTest(char *par); + void processTest(TestData *testData); +private: + void testConvertDate(TestData *testData, const DataMap *settings, UBool fmt); +// void testOps(TestData *testData, const DataMap *settings); +// void testConvert(int32_t n, const FormatFieldsSet &fromSet, +// Format *fromCal, const FormatFieldsSet &toSet, Format *toCal, +// UBool fwd); +private: + TestDataModule *driver; +}; + +#endif /* #if !UCONFIG_NO_COLLATION */ + +#endif diff --git a/icu4c/source/test/intltest/fldset.cpp b/icu4c/source/test/intltest/fldset.cpp new file mode 100644 index 00000000000..b758232afd6 --- /dev/null +++ b/icu4c/source/test/intltest/fldset.cpp @@ -0,0 +1,315 @@ +/* +************************************************************************ +* Copyright (c) 2007, International Business Machines +* Corporation and others. All Rights Reserved. +************************************************************************ +*/ + +#include "fldset.h" +#include +#include "unicode/regex.h" + + +FieldsSet::FieldsSet() { + // NOTREACHED +} + +FieldsSet::FieldsSet(int32_t fieldCount) { + construct((UDebugEnumType)-1, fieldCount); +} + +FieldsSet::FieldsSet(UDebugEnumType field) { + construct(field, udbg_enumCount(field)); +} + +FieldsSet::~FieldsSet() { + +} + +int32_t FieldsSet::fieldCount() const { + return fFieldCount; +} + +void FieldsSet::construct(UDebugEnumType field, int32_t fieldCount) { + fEnum = field; + if(fieldCount > U_FIELDS_SET_MAX) { + fieldCount = U_FIELDS_SET_MAX; + } + fFieldCount = fieldCount; + clear(); +} + +UnicodeString FieldsSet::diffFrom(const FieldsSet& other, UErrorCode& status) const { + UnicodeString str; + if(!isSameType(other)) { + status = U_ILLEGAL_ARGUMENT_ERROR; + return UnicodeString("U_ILLEGAL_ARGUMENT_ERROR: FieldsSet of a different type!"); + } + for (int i=0; i=fieldCount()) { + return; + } + fValue[field] = -1; + fIsSet[field] = FALSE; +} +void FieldsSet::set(int32_t field, int32_t amount) { + if (field<0|| field>=fieldCount()) { + return; + } + fValue[field] = amount; + fIsSet[field] = TRUE; +} + +UBool FieldsSet::isSet(int32_t field) const { + if (field<0|| field>=fieldCount()) { + return FALSE; + } + return fIsSet[field]; +} +int32_t FieldsSet::get(int32_t field) const { + if (field<0|| field>=fieldCount()) { + return -1; + } + return fValue[field]; +} + + +int32_t FieldsSet::handleParseName(const FieldsSet* /* inheritFrom */, const UnicodeString& name, const UnicodeString& /* substr*/ , UErrorCode& status) { + if(fEnum > -1) { + return udbg_enumByString(fEnum, name); + } else { + status = U_UNSUPPORTED_ERROR; + return -1; + } +} + +void FieldsSet::parseValueDefault(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status) { + int32_t value = -1; + if(substr.length()==0) { // inherit requested + // inherit + if((inheritFrom == NULL) || !inheritFrom->isSet((UCalendarDateFields)field)) { + // couldn't inherit from field + fprintf(stderr,"Parse Failed: Couldn't inherit field %d [%s]\n", field, udbg_enumName(fEnum, field)); + status = U_ILLEGAL_ARGUMENT_ERROR; + return; + } + value = inheritFrom->get((UCalendarDateFields)field); + } else { + value = udbg_stoi(substr); + } + set(field, value); +} + +void FieldsSet::parseValueEnum(UDebugEnumType type, const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status) { + int32_t value = udbg_enumByString(type, substr); + if(value>=0) { + set(field, value); + } else { + // fallback + parseValueDefault(inheritFrom,field,substr,status); + } +} + +void FieldsSet::handleParseValue(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status) { + parseValueDefault(inheritFrom, field, substr, status); +} + +/// CAL FIELDS + + +CalendarFieldsSet::CalendarFieldsSet() : +FieldsSet(UDBG_UCalendarDateFields) { + // base class will call clear. +} + +CalendarFieldsSet::~CalendarFieldsSet() { +} + +void CalendarFieldsSet::handleParseValue(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status) { + if(field==UCAL_MONTH) { + parseValueEnum(UDBG_UCalendarMonths, inheritFrom, field, substr, status); + // will fallback to default. + } else { + parseValueDefault(inheritFrom, field, substr, status); + } +} + +/** + * set the specified fields on this calendar. Doesn't clear first. Returns any errors the caller + */ +void CalendarFieldsSet::setOnCalendar(Calendar *cal, UErrorCode& /*status*/) const { + for (int i=0; iset((UCalendarDateFields)i, value); + } + } +} + +/** + * return true if the calendar matches in these fields + */ +UBool CalendarFieldsSet::matches(Calendar *cal, CalendarFieldsSet &diffSet, + UErrorCode& status) const { + UBool match = TRUE; + if (U_FAILURE(status)) + return FALSE; + for (int i=0; iget((UCalendarDateFields)i, status); + if (U_FAILURE(status)) + return FALSE; + if (calVal != get((UCalendarDateFields)i)) { + match = FALSE; + diffSet.set((UCalendarDateFields)i, calVal); + //fprintf(stderr, "match failed: %s#%d=%d != %d\n",udbg_enumName(UDBG_UCalendarDateFields,i),i,cal->get((UCalendarDateFields)i,status), get((UCalendarDateFields)i));; + } + } + } + return match; +} + + +enum { + DTS_DATE = 0, + DTS_TIME, + DTS_COUNT +}; + +/** + * DateTimeSet + * */ +DateTimeStyleSet::DateTimeStyleSet() : + FieldsSet(DTS_COUNT) { + +} + +DateTimeStyleSet::~DateTimeStyleSet() { + +} + +UDateFormatStyle DateTimeStyleSet::getDateStyle() const { + if(!isSet(DTS_DATE)) { + return UDAT_NONE; + } else { + return (UDateFormatStyle)get(DTS_DATE); + } +} + + +UDateFormatStyle DateTimeStyleSet::getTimeStyle() const { + if(!isSet(DTS_TIME)) { + return UDAT_NONE; + } else { + return (UDateFormatStyle)get(DTS_TIME); + } +} + +void DateTimeStyleSet::handleParseValue(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status) { +// int32_t value = udbg_enumByString(UDBG_UDateFormatStyle, substr); +// fprintf(stderr, " HPV: %d -> %d\n", field, value); + parseValueEnum(UDBG_UDateFormatStyle, inheritFrom, field, substr, status); +} + +int32_t DateTimeStyleSet::handleParseName(const FieldsSet* /* inheritFrom */, const UnicodeString& name, const UnicodeString& /* substr */, UErrorCode& status) { + UnicodeString kDATE("DATE"); // TODO: static + UnicodeString kTIME("TIME"); // TODO: static + if(name == kDATE ) { + return DTS_DATE; + } else if(name == kTIME) { + return DTS_TIME; + } else { + status = U_ILLEGAL_ARGUMENT_ERROR; + return -1; + } +} + diff --git a/icu4c/source/test/intltest/fldset.h b/icu4c/source/test/intltest/fldset.h new file mode 100644 index 00000000000..c652f3bcc59 --- /dev/null +++ b/icu4c/source/test/intltest/fldset.h @@ -0,0 +1,184 @@ +/* +************************************************************************ +* Copyright (c) 2007, International Business Machines +* Corporation and others. All Rights Reserved. +************************************************************************ +*/ +#ifndef FLDSET_H_ +#define FLDSET_H_ + +#include "unicode/utypes.h" +#include "unicode/calendar.h" +#include "unicode/ucal.h" +#include "unicode/udat.h" +#include "unicode/udbgutil.h" +#include "unicode/dbgutil.h" +#include "unicode/unistr.h" + +#define U_FIELDS_SET_MAX 64 + +class FieldsSet { + protected: + /** + * subclass interface + * @param whichEnum which enumaration value goes with this set. Will be used to calculate str values and also enum size. + */ + FieldsSet(UDebugEnumType whichEnum); + + /** + * subclass interface - no enum tie-in + * @param fieldCount how many fields this can hold. + */ + FieldsSet(int32_t fieldsCount); + + public: + /** + * @param other "expected" set to match against + * @param status - will return invalid argument if sets are not the same size + * @return a formatted string listing which fields are set in + * this, with the comparison made agaainst those fields in other. + */ + UnicodeString diffFrom(const FieldsSet& other, UErrorCode &status) const; + + public: + /** + * @param str string to parse + * @param status formatted string for status + */ + int32_t parseFrom(const UnicodeString& str, UErrorCode& status) { return parseFrom(str,NULL,status); } + + public: + int32_t parseFrom(const UnicodeString& str, const FieldsSet& inheritFrom, UErrorCode& status) { return parseFrom(str, &inheritFrom, status); } + + int32_t parseFrom(const UnicodeString& str, const + FieldsSet* inheritFrom, UErrorCode& status); + + protected: + /** + * Callback interface for subclass. + * This function is called when parsing a field name, such as "MONTH" in "MONTH=4". + * Base implementation is to lookup the enum value using udbg_* utilities, or else as an integer if + * enum is not available. + * + * If there is a special directive, the implementer can catch it here and return -1 after special processing completes. + * + * @param inheritFrom the set inheriting from - may be null. + * @param name the field name (key side) + * @param substr the string in question (value side) + * @param status error status - set to error for failure. + * @return field number, or negative if field should be skipped. + */ + virtual int32_t handleParseName(const FieldsSet* inheritFrom, const UnicodeString& name, const UnicodeString& substr, UErrorCode& status); + + /** + * Callback interface for subclass. + * Base implementation is to call parseValueDefault(...) + * @param inheritFrom the set inheriting from - may be null. + * @param field which field is being parsed + * @param substr the string in question (value side) + * @param status error status - set to error for failure. + * @see parseValueDefault + */ + virtual void handleParseValue(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status); + + /** + * the default implementation for handleParseValue. + * Base implementation is to parse a decimal integer value, or inherit from inheritFrom if the string is 0-length. + * Implementations of this function should call set(field,...) on successful parse. + * @see handleParseValue + */ + void parseValueDefault(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status); + + + /** + * convenience implementation for handleParseValue + * attempt to load a value from an enum value using udbg_enumByString() + * if fails, will call parseValueDefault() + * @see handleParseValue + */ + void parseValueEnum(UDebugEnumType type, const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status); + + private: + FieldsSet(); + + void construct(UDebugEnumType whichEnum, int32_t fieldCount); + + public: + virtual ~FieldsSet(); + + void clear(); + void clear(int32_t field); + void set(int32_t field, int32_t amount); + UBool isSet(int field) const; + int32_t get(int field) const; + + UBool isSameType(const FieldsSet& other) const; + int32_t fieldCount() const; + + + protected: + int32_t fValue[U_FIELDS_SET_MAX]; + UBool fIsSet[U_FIELDS_SET_MAX]; + protected: + int32_t fFieldCount; + UDebugEnumType fEnum; +}; + +/** ------- Calendar Fields Set -------- **/ +class CalendarFieldsSet : public FieldsSet { + public: + CalendarFieldsSet(); + virtual ~CalendarFieldsSet(); + +// void clear(UCalendarDateFields field) { clear((int32_t)field); } +// void set(UCalendarDateFields field, int32_t amount) { set ((int32_t)field, amount); } + +// UBool isSet(UCalendarDateFields field) const { return isSet((int32_t)field); } +// int32_t get(UCalendarDateFields field) const { return get((int32_t)field); } + + /** + * @param matches fillin to hold any fields different. Will have the calendar's value set on them. + * @return true if the calendar matches in these fields. + */ + UBool matches(Calendar *cal, CalendarFieldsSet &diffSet, + UErrorCode& status) const; + + /** + * set the specified fields on this calendar. Doesn't clear first. Returns any errors the cale + */ + void setOnCalendar(Calendar *cal, UErrorCode& status) const; + + + protected: + void handleParseValue(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status); +}; + +/** + * This class simply implements a set of date and time styles + * such as DATE=SHORT or TIME=SHORT,DATE=LONG + */ +class DateTimeStyleSet : public FieldsSet { + public: + DateTimeStyleSet(); + virtual ~DateTimeStyleSet(); + + + /** + * @return the date style, or UDAT_NONE if not set + */ + UDateFormatStyle getDateStyle() const; + + /** + * @return the time style, or UDAT_NONE if not set + */ + UDateFormatStyle getTimeStyle() const; + + + protected: + void handleParseValue(const FieldsSet* inheritFrom, int32_t field, const UnicodeString& substr, UErrorCode& status); + int32_t handleParseName(const FieldsSet* inheritFrom, const UnicodeString& name, const UnicodeString& substr, UErrorCode& status); +}; + + + +#endif /*FLDSET_H_*/ diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp index 96fe0dc2e77..23bf1c11081 100644 --- a/icu4c/source/test/intltest/itformat.cpp +++ b/icu4c/source/test/intltest/itformat.cpp @@ -44,6 +44,7 @@ #include "calcasts.h" // CalendarCaseTest #include "tzrulets.h" // TimeZoneRuleTest #include "dadrcal.h" // DataDrivenCalendarTest +#include "dadrfmt.h" // DataDrivenFormatTest #include "dtptngts.h" // IntlTestDateTimePatternGeneratorAPI @@ -107,7 +108,8 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam TESTCLASS(28,CalendarCaseTest); TESTCLASS(29,TimeZoneRuleTest); TESTCLASS(30,DataDrivenCalendarTest); - TESTCLASS(31,IntlTestDateTimePatternGeneratorAPI); + TESTCLASS(31,DataDrivenFormatTest); + TESTCLASS(32,IntlTestDateTimePatternGeneratorAPI); default: name = ""; break; //needed to end loop diff --git a/icu4c/source/test/testdata/format.txt b/icu4c/source/test/testdata/format.txt new file mode 100644 index 00000000000..e56be95ec2d --- /dev/null +++ b/icu4c/source/test/testdata/format.txt @@ -0,0 +1,37 @@ +// Copyright (c) 2007 International Business Machines +// Corporation and others. All Rights Reserved. +format:table(nofallback) { + Info { + Description { "These are the data driven format tests" } + LongDescription { "Data for data driven format tests." } + } + TestData { + TestDateFormatBasic { + Info { + Description { "Test data for format and parse :" } + } + Settings { + // options: 'format', or 'parse'. One can be omitted if this is a 1 way test. + { + Type { "date_format" } + }, + { + Type { "date_parse" } + }, + } + Headers { "locale","spec", "date", "str"} + // locale: locale including calendar type + // spec: either 'PATTERN=y mm h' etc, or 'DATE=SHORT,TIME=LONG' + // date: either 'MILLIS=####' where #### is an unsigned long (millis), or a calendar spec ERA=0,YEAR=1, etc.. applied to the calendar type specified by the locale + // str: the expected unicode string + Cases { + { + "en_US@calendar=gregorian", + "DATE=SHORT,TIME=SHORT", + "ERA=1,YEAR=2007,MONTH=AUGUST,DATE=8,HOUR_OF_DAY=18,MINUTE=54,SECOND=0", + "8/8/07 6:54 PM" + }, + } + } + } +} \ No newline at end of file diff --git a/icu4c/source/test/testdata/tstfiles.mk b/icu4c/source/test/testdata/tstfiles.mk index cd9dc6ff936..8ba8d45593c 100644 --- a/icu4c/source/test/testdata/tstfiles.mk +++ b/icu4c/source/test/testdata/tstfiles.mk @@ -6,4 +6,5 @@ # Not everything has been moved to this file yet, as this is a new facility. # -TEST_RES_SOURCE=DataDrivenCollationTest.txt calendar.txt +TEST_RES_SOURCE=DataDrivenCollationTest.txt calendar.txt format.txt + diff --git a/icu4c/source/tools/ctestfw/udbgutil.cpp b/icu4c/source/tools/ctestfw/udbgutil.cpp index de632454893..0c81fa5dc3c 100644 --- a/icu4c/source/tools/ctestfw/udbgutil.cpp +++ b/icu4c/source/tools/ctestfw/udbgutil.cpp @@ -78,6 +78,27 @@ static const Field names_UCalendarMonths[] = FIELD_NAME_STR( LEN_UCAL, UCAL_UNDECIMBER) }; +#include "unicode/udat.h" + +#define LEN_UDAT 5 /* "UDAT_" */ + +static const int32_t count_UDateFormatStyle = UDAT_SHORT+1; + +static const Field names_UDateFormatStyle[] = +{ + FIELD_NAME_STR( LEN_UDAT, UDAT_FULL ), + FIELD_NAME_STR( LEN_UDAT, UDAT_LONG ), + FIELD_NAME_STR( LEN_UDAT, UDAT_MEDIUM ), + FIELD_NAME_STR( LEN_UDAT, UDAT_SHORT ), + /* end regular */ + /* + * negative enums.. leave out for now. + FIELD_NAME_STR( LEN_UDAT, UDAT_NONE ), + FIELD_NAME_STR( LEN_UDAT, UDAT_IGNORE ), + */ +}; + + #endif @@ -90,6 +111,7 @@ static const Field names_UDebugEnumType[] = FIELD_NAME_STR( LEN_UDBG, UDBG_UDebugEnumType ), FIELD_NAME_STR( LEN_UDBG, UDBG_UCalendarDateFields ), FIELD_NAME_STR( LEN_UDBG, UDBG_UCalendarMonths ), + FIELD_NAME_STR( LEN_UDBG, UDBG_UDateFormatStyle ), }; @@ -110,6 +132,7 @@ static int32_t _udbg_enumCount(UDebugEnumType type, UBool actual) { COUNT_CASE(UDebugEnumType) COUNT_CASE(UCalendarDateFields) COUNT_CASE(UCalendarMonths) + COUNT_CASE(UDateFormatStyle) // COUNT_FAIL_CASE(UNonExistentEnum) default: return -1; @@ -121,6 +144,7 @@ static const Field* _udbg_enumFields(UDebugEnumType type) { FIELD_CASE(UDebugEnumType) FIELD_CASE(UCalendarDateFields) FIELD_CASE(UCalendarMonths) + FIELD_CASE(UDateFormatStyle) // FIELD_FAIL_CASE(UNonExistentEnum) default: return NULL; diff --git a/icu4c/source/tools/ctestfw/unicode/udbgutil.h b/icu4c/source/tools/ctestfw/unicode/udbgutil.h index 8e7ec86ec28..e1decd4d026 100644 --- a/icu4c/source/tools/ctestfw/unicode/udbgutil.h +++ b/icu4c/source/tools/ctestfw/unicode/udbgutil.h @@ -18,6 +18,7 @@ enum UDebugEnumType { UDBG_UDebugEnumType = 0, /* Self-referential, strings for UDebugEnumType. Count=ENUM_COUNT. */ UDBG_UCalendarDateFields, /* UCalendarDateFields. Count=UCAL_FIELD_COUNT. Unsupported if UCONFIG_NO_FORMATTING. */ UDBG_UCalendarMonths, /* UCalendarMonths. Count= (UCAL_UNDECIMBER+1) */ + UDBG_UDateFormatStyle, /* Count = UDAT_SHORT=1 */ UDBG_ENUM_COUNT };