1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4 * Copyright (c) 1997-2016, International Business Machines
5 * Corporation and others. All Rights Reserved.
6 ********************************************************************/
8 #include "unicode/utypes.h"
13 #include "unicode/unistr.h"
14 #include "unicode/resbund.h"
15 #include "unicode/brkiter.h"
16 #include "unicode/utrace.h"
17 #include "unicode/ucurr.h"
28 //***************************************************************************************
30 void NewResourceBundleTest::runIndexedTest( int32_t index
, UBool exec
, const char* &name
, char* /*par*/ )
32 if (exec
) logln("TestSuite ResourceBundleTest: ");
35 #if !UCONFIG_NO_FILE_IO && !UCONFIG_NO_LEGACY_CONVERSION
36 TESTCASE_AUTO(TestResourceBundles
);
37 TESTCASE_AUTO(TestConstruction
);
38 TESTCASE_AUTO(TestIteration
);
39 TESTCASE_AUTO(TestOtherAPI
);
40 TESTCASE_AUTO(TestNewTypes
);
43 TESTCASE_AUTO(TestGetByFallback
);
44 TESTCASE_AUTO(TestFilter
);
45 TESTCASE_AUTO(TestIntervalAliasFallbacks
);
48 TESTCASE_AUTO(TestTrace
);
54 //***************************************************************************************
56 static const UChar kErrorUChars
[] = { 0x45, 0x52, 0x52, 0x4f, 0x52, 0 };
57 static const int32_t kErrorLength
= 5;
58 static const int32_t kERROR_COUNT
= -1234567;
60 //***************************************************************************************
70 //***************************************************************************************
72 #define CONFIRM_EQ(actual,expected) UPRV_BLOCK_MACRO_BEGIN { \
73 if ((expected)==(actual)) { \
77 errln(action + (UnicodeString)" returned " + (actual) + (UnicodeString)" instead of " + (expected)); \
79 } UPRV_BLOCK_MACRO_END
80 #define CONFIRM_GE(actual,expected) UPRV_BLOCK_MACRO_BEGIN { \
81 if ((actual)>=(expected)) { \
85 errln(action + (UnicodeString)" returned " + (actual) + (UnicodeString)" instead of x >= " + (expected)); \
87 } UPRV_BLOCK_MACRO_END
88 #define CONFIRM_NE(actual,expected) UPRV_BLOCK_MACRO_BEGIN { \
89 if ((expected)!=(actual)) { \
93 errln(action + (UnicodeString)" returned " + (actual) + (UnicodeString)" instead of x != " + (expected)); \
95 } UPRV_BLOCK_MACRO_END
97 #define CONFIRM_UErrorCode(actual,expected) UPRV_BLOCK_MACRO_BEGIN { \
98 if ((expected)==(actual)) { \
102 errln(action + (UnicodeString)" returned " + (UnicodeString)u_errorName(actual) + (UnicodeString)" instead of " + (UnicodeString)u_errorName(expected)); \
104 } UPRV_BLOCK_MACRO_END
106 //***************************************************************************************
109 * Convert an integer, positive or negative, to a character string radix 10.
112 itoa(int32_t i
, char* buf
)
123 // Output digits in reverse order
127 *p
++ = (char)('0' + (i
% 10));
133 // Reverse the string
146 //***************************************************************************************
148 // Array of our test objects
154 UErrorCode expected_constructor_status
;
156 UBool like
[e_Where_count
];
157 UBool inherits
[e_Where_count
];
162 // "IN" means inherits
163 // "NE" or "ne" means "does not exist"
165 { "root", 0, U_ZERO_ERROR
, e_Root
, { TRUE
, FALSE
, FALSE
}, { TRUE
, FALSE
, FALSE
} },
166 { "te", 0, U_ZERO_ERROR
, e_te
, { FALSE
, TRUE
, FALSE
}, { TRUE
, TRUE
, FALSE
} },
167 { "te_IN", 0, U_ZERO_ERROR
, e_te_IN
, { FALSE
, FALSE
, TRUE
}, { TRUE
, TRUE
, TRUE
} },
168 { "te_NE", 0, U_USING_FALLBACK_WARNING
, e_te
, { FALSE
, TRUE
, FALSE
}, { TRUE
, TRUE
, FALSE
} },
169 { "te_IN_NE", 0, U_USING_FALLBACK_WARNING
, e_te_IN
, { FALSE
, FALSE
, TRUE
}, { TRUE
, TRUE
, TRUE
} },
170 { "ne", 0, U_USING_DEFAULT_WARNING
, e_Root
, { TRUE
, FALSE
, FALSE
}, { TRUE
, FALSE
, FALSE
} }
173 static int32_t bundles_count
= UPRV_LENGTHOF(param
);
175 //***************************************************************************************
178 * Return a random unsigned long l where 0N <= l <= ULONG_MAX.
184 static UBool initialized
= FALSE
;
187 srand((unsigned)time(NULL
));
190 // Assume rand has at least 12 bits of precision
192 for (uint32_t i
=0; i
<sizeof(l
); ++i
)
193 ((char*)&l
)[i
] = (char)((rand() & 0x0FF0) >> 4);
198 * Return a random double x where 0.0 <= x < 1.0.
203 return (double)(randul() / ULONG_MAX
);
207 * Return a random integer i where 0 <= i < n.
209 static int32_t randi(int32_t n
)
211 return (int32_t)(randd() * n
);
214 //***************************************************************************************
217 Don't use more than one of these at a time because of the Locale names
219 NewResourceBundleTest::NewResourceBundleTest()
223 if (param
[5].locale
== NULL
) {
224 param
[0].locale
= new Locale("root");
225 param
[1].locale
= new Locale("te");
226 param
[2].locale
= new Locale("te", "IN");
227 param
[3].locale
= new Locale("te", "NE");
228 param
[4].locale
= new Locale("te", "IN", "NE");
229 param
[5].locale
= new Locale("ne");
233 NewResourceBundleTest::~NewResourceBundleTest()
235 if (param
[5].locale
) {
237 for (idx
= 0; idx
< UPRV_LENGTHOF(param
); idx
++) {
238 delete param
[idx
].locale
;
239 param
[idx
].locale
= NULL
;
244 //***************************************************************************************
247 NewResourceBundleTest::TestResourceBundles()
249 UErrorCode status
= U_ZERO_ERROR
;
250 loadTestData(status
);
251 if(U_FAILURE(status
))
253 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(status
)));
257 /* Make sure that users using te_IN for the default locale don't get test failures. */
258 Locale originalDefault
;
259 if (Locale::getDefault() == Locale("te_IN")) {
260 Locale::setDefault(Locale("en_US"), status
);
263 testTag("only_in_Root", TRUE
, FALSE
, FALSE
);
264 testTag("only_in_te", FALSE
, TRUE
, FALSE
);
265 testTag("only_in_te_IN", FALSE
, FALSE
, TRUE
);
266 testTag("in_Root_te", TRUE
, TRUE
, FALSE
);
267 testTag("in_Root_te_te_IN", TRUE
, TRUE
, TRUE
);
268 testTag("in_Root_te_IN", TRUE
, FALSE
, TRUE
);
269 testTag("in_te_te_IN", FALSE
, TRUE
, TRUE
);
270 testTag("nonexistent", FALSE
, FALSE
, FALSE
);
271 logln("Passed: %d\nFailed: %d", pass
, fail
);
273 /* Restore the default locale for the other tests. */
274 Locale::setDefault(originalDefault
, status
);
278 NewResourceBundleTest::TestConstruction()
280 UErrorCode err
= U_ZERO_ERROR
;
281 Locale
locale("te", "IN");
283 const char* testdatapath
;
284 testdatapath
=loadTestData(err
);
287 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(err
)));
291 /* Make sure that users using te_IN for the default locale don't get test failures. */
292 Locale originalDefault
;
293 if (Locale::getDefault() == Locale("te_IN")) {
294 Locale::setDefault(Locale("en_US"), err
);
297 ResourceBundle
test1((UnicodeString
)testdatapath
, err
);
298 ResourceBundle
test2(testdatapath
, locale
, err
);
300 UnicodeString result1
;
301 UnicodeString result2
;
303 result1
= test1
.getStringEx("string_in_Root_te_te_IN", err
);
304 result2
= test2
.getStringEx("string_in_Root_te_te_IN", err
);
305 if (U_FAILURE(err
)) {
306 errln("Something threw an error in TestConstruction()");
310 logln("for string_in_Root_te_te_IN, root.txt had " + result1
);
311 logln("for string_in_Root_te_te_IN, te_IN.txt had " + result2
);
313 if (result1
!= "ROOT" || result2
!= "TE_IN") {
314 errln("Construction test failed; run verbose for more information");
317 const char* version1
;
318 const char* version2
;
320 version1
= test1
.getVersionNumber();
321 version2
= test2
.getVersionNumber();
323 char *versionID1
= new char[1 + strlen(U_ICU_VERSION
) + strlen(version1
)]; // + 1 for zero byte
324 char *versionID2
= new char[1 + strlen(U_ICU_VERSION
) + strlen(version2
)]; // + 1 for zero byte
326 strcpy(versionID1
, "44.0"); // hardcoded, please change if the default.txt file or ResourceBundle::kVersionSeparater is changed.
328 strcpy(versionID2
, "55.0"); // hardcoded, please change if the te_IN.txt file or ResourceBundle::kVersionSeparater is changed.
330 logln(UnicodeString("getVersionNumber on default.txt returned ") + version1
+ UnicodeString(" Expect: " ) + versionID1
);
331 logln(UnicodeString("getVersionNumber on te_IN.txt returned ") + version2
+ UnicodeString(" Expect: " ) + versionID2
);
333 if (strcmp(version1
, versionID1
) != 0) {
334 errln("getVersionNumber(version1) failed. %s != %s", version1
, versionID1
);
336 if (strcmp(version2
, versionID2
) != 0) {
337 errln("getVersionNumber(version2) failed. %s != %s", version2
, versionID2
);
342 /* Restore the default locale for the other tests. */
343 Locale::setDefault(originalDefault
, err
);
347 NewResourceBundleTest::TestIteration()
349 UErrorCode err
= U_ZERO_ERROR
;
350 const char* testdatapath
;
352 "string_in_Root_te_te_IN", "1",
353 "array_in_Root_te_te_IN", "5",
354 "array_2d_in_Root_te_te_IN", "4",
357 Locale
*locale
=new Locale("te_IN");
359 testdatapath
=loadTestData(err
);
362 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(err
)));
366 ResourceBundle
test1(testdatapath
, *locale
, err
);
368 errln("Construction failed");
371 int32_t count
, row
=0, col
=0;
373 UnicodeString expected
;
374 UnicodeString
element("TE_IN");
375 UnicodeString action
;
378 for(i
=0; i
<UPRV_LENGTHOF(data
); i
=i
+2){
384 ResourceBundle bundle
= test1
.get(data
[i
], err
);
387 action
+=".getKey()";
389 CONFIRM_EQ((UnicodeString
)bundle
.getKey(), (UnicodeString
)data
[i
]);
393 while(bundle
.hasNext()){
395 action
+=".getNextString(err)";
397 UnicodeString got
=bundle
.getNextString(err
);
400 if(bundle
.getSize() > 1){
401 CONFIRM_EQ(bundle
.getType(), URES_ARRAY
);
402 expected
+=itoa(row
, buf
);
403 ResourceBundle rowbundle
=bundle
.get(row
, err
);
404 if(!U_FAILURE(err
) && rowbundle
.getSize()>1){
406 while(rowbundle
.hasNext()){
408 got
=rowbundle
.getNextString(err
);
410 expected
+=itoa(row
, buf
);
411 expected
+=itoa(col
, buf
);
413 CONFIRM_EQ(got
, expected
);
416 CONFIRM_EQ(col
, rowbundle
.getSize());
420 CONFIRM_EQ(bundle
.getType(), (int32_t)URES_STRING
);
423 CONFIRM_EQ(got
, expected
);
427 action
+=".getSize()";
428 CONFIRM_EQ(bundle
.getSize(), count
);
429 CONFIRM_EQ(count
, atoi(data
[i
+1]));
430 //after reaching the end
432 ResourceBundle errbundle
=bundle
.getNext(err
);
433 action
= "After reaching the end of the Iterator:- ";
435 action
+=".getNext()";
436 CONFIRM_NE(err
, (int32_t)U_ZERO_ERROR
);
437 CONFIRM_EQ(u_errorName(err
), u_errorName(U_INDEX_OUTOFBOUNDS_ERROR
));
440 bundle
.resetIterator();
441 /* The following code is causing a crash
447 errln("ERROR: getNext() throw an error");
454 // TODO: add operator== and != to ResourceBundle
456 equalRB(ResourceBundle
&a
, ResourceBundle
&b
) {
464 a
.getLocale()==b
.getLocale() &&
465 0==strcmp(a
.getName(), b
.getName()) &&
467 a
.getString(status
)==b
.getString(status
) :
469 a
.getInt(status
)==b
.getInt(status
) :
474 NewResourceBundleTest::TestOtherAPI(){
475 UErrorCode err
= U_ZERO_ERROR
;
476 const char* testdatapath
=loadTestData(err
);
477 UnicodeString tDataPathUS
= UnicodeString(testdatapath
, "");
481 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(err
)));
485 /* Make sure that users using te_IN for the default locale don't get test failures. */
486 Locale originalDefault
;
487 if (Locale::getDefault() == Locale("te_IN")) {
488 Locale::setDefault(Locale("en_US"), err
);
491 Locale
*locale
=new Locale("te_IN");
493 ResourceBundle
test0(tDataPathUS
, *locale
, err
);
495 errln("Construction failed");
499 ResourceBundle
test1(testdatapath
, *locale
, err
);
501 errln("Construction failed");
505 logln("Testing getLocale()\n");
506 if(strcmp(test1
.getLocale().getName(), locale
->getName()) !=0 ){
507 errln("FAIL: ResourceBundle::getLocale() failed\n");
512 logln("Testing ResourceBundle(UErrorCode)\n");
513 ResourceBundle
defaultresource(err
);
514 ResourceBundle
explicitdefaultresource(NULL
, Locale::getDefault(), err
);
516 errcheckln(err
, "Construction of default resourcebundle failed - %s", u_errorName(err
));
519 // You can't compare the default locale to the resolved locale in the
520 // resource bundle due to aliasing, keywords in the default locale
521 // or the chance that the machine running these tests is using a locale
522 // that isn't available in ICU.
523 if(strcmp(defaultresource
.getLocale().getName(), explicitdefaultresource
.getLocale().getName()) != 0){
524 errln("Construction of default resourcebundle didn't take the defaultlocale. Expected %s Got %s err=%s\n",
525 explicitdefaultresource
.getLocale().getName(), defaultresource
.getLocale().getName(), u_errorName(err
));
529 ResourceBundle
copyRes(defaultresource
);
530 if(strcmp(copyRes
.getName(), defaultresource
.getName() ) !=0 ||
531 strcmp(test1
.getName(), defaultresource
.getName() ) ==0 ||
532 strcmp(copyRes
.getLocale().getName(), defaultresource
.getLocale().getName() ) !=0 ||
533 strcmp(test1
.getLocale().getName(), defaultresource
.getLocale().getName() ) ==0 )
535 errln("copy construction failed\n");
538 ResourceBundle defaultSub
= defaultresource
.get((int32_t)0, err
);
539 ResourceBundle
defSubCopy(defaultSub
);
540 if(strcmp(defSubCopy
.getName(), defaultSub
.getName() ) !=0 ||
541 strcmp(defSubCopy
.getLocale().getName(), defaultSub
.getLocale().getName() ) !=0 ){
542 errln("copy construction for subresource failed\n");
547 p
= defaultresource
.clone();
548 if(p
== &defaultresource
|| !equalRB(*p
, defaultresource
)) {
549 errln("ResourceBundle.clone() failed");
553 p
= defaultSub
.clone();
554 if(p
== &defaultSub
|| !equalRB(*p
, defaultSub
)) {
555 errln("2nd ResourceBundle.clone() failed");
560 copyRes
.getVersion(ver
);
562 logln("Version returned: [%d.%d.%d.%d]\n", ver
[0], ver
[1], ver
[2], ver
[3]);
564 logln("Testing C like UnicodeString APIs\n");
566 UResourceBundle
*testCAPI
= NULL
, *bundle
= NULL
, *rowbundle
= NULL
, *temp
= NULL
;
569 "string_in_Root_te_te_IN", "1",
570 "array_in_Root_te_te_IN", "5",
571 "array_2d_in_Root_te_te_IN", "4",
575 testCAPI
= ures_open(testdatapath
, "te_IN", &err
);
582 int32_t count
, row
=0, col
=0;
584 UnicodeString expected
;
585 UnicodeString
element("TE_IN");
586 UnicodeString action
;
589 for(i
=0; i
<UPRV_LENGTHOF(data
); i
=i
+2){
595 bundle
= ures_getByKey(testCAPI
, data
[i
], bundle
, &err
);
597 const char* key
= NULL
;
599 action
+=".getKey()";
601 CONFIRM_EQ((UnicodeString
)ures_getKey(bundle
), (UnicodeString
)data
[i
]);
605 while(ures_hasNext(bundle
)){
607 action
+=".getNextString(err)";
609 UnicodeString got
=ures_getNextUnicodeString(bundle
, &key
, &err
);
612 if(ures_getSize(bundle
) > 1){
613 CONFIRM_EQ(ures_getType(bundle
), URES_ARRAY
);
614 expected
+=itoa(row
, buf
);
615 rowbundle
=ures_getByIndex(bundle
, row
, rowbundle
, &err
);
616 if(!U_FAILURE(err
) && ures_getSize(rowbundle
)>1){
618 while(ures_hasNext(rowbundle
)){
620 got
=ures_getNextUnicodeString(rowbundle
, &key
, &err
);
621 temp
= ures_getByIndex(rowbundle
, col
, temp
, &err
);
622 UnicodeString bla
= ures_getUnicodeString(temp
, &err
);
623 UnicodeString bla2
= ures_getUnicodeStringByIndex(rowbundle
, col
, &err
);
625 expected
+=itoa(row
, buf
);
626 expected
+=itoa(col
, buf
);
628 CONFIRM_EQ(got
, expected
);
629 CONFIRM_EQ(bla
, expected
);
630 CONFIRM_EQ(bla2
, expected
);
633 CONFIRM_EQ(col
, ures_getSize(rowbundle
));
637 CONFIRM_EQ(ures_getType(bundle
), (int32_t)URES_STRING
);
640 CONFIRM_EQ(got
, expected
);
646 // Check that ures_getUnicodeString() & variants return a bogus string if failure.
647 // Same relevant code path whether the failure code is passed in
648 // or comes from a lookup error.
649 UErrorCode failure
= U_INTERNAL_PROGRAM_ERROR
;
650 assertTrue("ures_getUnicodeString(failure).isBogus()",
651 ures_getUnicodeString(testCAPI
, &failure
).isBogus());
652 assertTrue("ures_getNextUnicodeString(failure).isBogus()",
653 ures_getNextUnicodeString(testCAPI
, NULL
, &failure
).isBogus());
654 assertTrue("ures_getUnicodeStringByIndex(failure).isBogus()",
655 ures_getUnicodeStringByIndex(testCAPI
, 999, &failure
).isBogus());
656 assertTrue("ures_getUnicodeStringByKey(failure).isBogus()",
657 ures_getUnicodeStringByKey(testCAPI
, "bogus key", &failure
).isBogus());
660 ures_close(rowbundle
);
662 ures_close(testCAPI
);
664 errln("failed to open a resource bundle\n");
667 /* Restore the default locale for the other tests. */
668 Locale::setDefault(originalDefault
, err
);
674 //***************************************************************************************
677 NewResourceBundleTest::testTag(const char* frag
,
682 int32_t failOrig
= fail
;
684 // Make array from input params
686 UBool is_in
[] = { in_Root
, in_te
, in_te_IN
};
688 const char* NAME
[] = { "ROOT", "TE", "TE_IN" };
690 // Now try to load the desired items
693 UnicodeString action
;
695 int32_t i
,j
,row
,col
, actual_bundle
;
697 const char* testdatapath
;
699 UErrorCode status
= U_ZERO_ERROR
;
700 testdatapath
=loadTestData(status
);
701 if(U_FAILURE(status
))
703 dataerrln("Could not load testdata.dat %s " + UnicodeString(u_errorName(status
)));
707 for (i
=0; i
<bundles_count
; ++i
)
709 action
= "Constructor for ";
710 action
+= param
[i
].name
;
712 status
= U_ZERO_ERROR
;
713 ResourceBundle
theBundle( testdatapath
, *param
[i
].locale
, status
);
714 //ResourceBundle theBundle( "c:\\icu\\icu\\source\\test\\testdata\\testdata", *param[i].locale, status);
715 CONFIRM_UErrorCode(status
,param
[i
].expected_constructor_status
);
718 actual_bundle
= 0; /* ne -> default */
720 actual_bundle
= 1; /* te_NE -> te */
722 actual_bundle
= 2; /* te_IN_NE -> te_IN */
727 UErrorCode expected_resource_status
= U_MISSING_RESOURCE_ERROR
;
728 for (j
=e_te_IN
; j
>=e_Root
; --j
)
730 if (is_in
[j
] && param
[i
].inherits
[j
])
732 if(j
== actual_bundle
) /* it's in the same bundle OR it's a nonexistent=default bundle (5) */
733 expected_resource_status
= U_ZERO_ERROR
;
735 expected_resource_status
= U_USING_DEFAULT_WARNING
;
737 expected_resource_status
= U_USING_FALLBACK_WARNING
;
743 UErrorCode expected_status
;
746 for (j
=param
[i
].where
; j
>=0; --j
)
755 //--------------------------------------------------------------------------
758 uprv_strcpy(tag
, "string_");
759 uprv_strcat(tag
, frag
);
761 action
= param
[i
].name
;
762 action
+= ".getStringEx(";
767 status
= U_ZERO_ERROR
;
768 UnicodeString string
= theBundle
.getStringEx(tag
, status
);
769 if(U_FAILURE(status
)) {
770 string
.setTo(TRUE
, kErrorUChars
, kErrorLength
);
773 CONFIRM_UErrorCode(status
, expected_resource_status
);
775 UnicodeString
expected_string(kErrorUChars
);
776 if (U_SUCCESS(status
)) {
777 expected_string
= base
;
780 CONFIRM_EQ(string
, expected_string
);
782 //--------------------------------------------------------------------------
783 // array ResourceBundle using the key
785 uprv_strcpy(tag
, "array_");
786 uprv_strcat(tag
, frag
);
788 action
= param
[i
].name
;
793 int32_t count
= kERROR_COUNT
;
794 status
= U_ZERO_ERROR
;
795 ResourceBundle array
= theBundle
.get(tag
, status
);
796 CONFIRM_UErrorCode(status
,expected_resource_status
);
799 if (U_SUCCESS(status
))
801 //confirm the resource type is an array
802 UResType bundleType
=array
.getType();
803 CONFIRM_EQ(bundleType
, URES_ARRAY
);
805 count
=array
.getSize();
808 for (j
=0; j
<count
; ++j
)
811 expected_string
= base
;
812 expected_string
+= itoa(j
,buf
);
813 CONFIRM_EQ(array
.getNextString(status
),expected_string
);
819 CONFIRM_EQ(count
,kERROR_COUNT
);
820 // CONFIRM_EQ((int32_t)(unsigned long)array,(int32_t)0);
824 //--------------------------------------------------------------------------
825 // arrayItem ResourceBundle using the index
828 for (j
=0; j
<100; ++j
)
830 index
= count
? (randi(count
* 3) - count
) : (randi(200) - 100);
831 status
= U_ZERO_ERROR
;
832 string
= kErrorUChars
;
833 ResourceBundle array
= theBundle
.get(tag
, status
);
834 if(!U_FAILURE(status
)){
835 UnicodeString t
= array
.getStringEx(index
, status
);
836 if(!U_FAILURE(status
)) {
841 expected_status
= (index
>= 0 && index
< count
) ? expected_resource_status
: U_MISSING_RESOURCE_ERROR
;
842 CONFIRM_UErrorCode(status
,expected_status
);
844 if (U_SUCCESS(status
)){
846 expected_string
= base
;
847 expected_string
+= itoa(index
,buf
);
849 expected_string
= kErrorUChars
;
851 CONFIRM_EQ(string
,expected_string
);
855 //--------------------------------------------------------------------------
858 uprv_strcpy(tag
, "array_2d_");
859 uprv_strcat(tag
, frag
);
861 action
= param
[i
].name
;
867 int32_t row_count
= kERROR_COUNT
, column_count
= kERROR_COUNT
;
868 status
= U_ZERO_ERROR
;
869 ResourceBundle array2d
=theBundle
.get(tag
, status
);
871 //const UnicodeString** array2d = theBundle.get2dArray(tag, row_count, column_count, status);
872 CONFIRM_UErrorCode(status
,expected_resource_status
);
874 if (U_SUCCESS(status
))
876 //confirm the resource type is an 2darray
877 UResType bundleType
=array2d
.getType();
878 CONFIRM_EQ(bundleType
, URES_ARRAY
);
880 row_count
=array2d
.getSize();
881 CONFIRM_GE(row_count
,1);
883 for(row
=0; row
<row_count
; ++row
){
884 ResourceBundle tablerow
=array2d
.get(row
, status
);
885 CONFIRM_UErrorCode(status
, expected_resource_status
);
886 if(U_SUCCESS(status
)){
887 //confirm the resourcetype of each table row is an array
888 UResType rowType
=tablerow
.getType();
889 CONFIRM_EQ(rowType
, URES_ARRAY
);
891 column_count
=tablerow
.getSize();
892 CONFIRM_GE(column_count
,1);
894 for (col
=0; j
<column_count
; ++j
) {
896 expected_string
= base
;
897 expected_string
+= itoa(row
,buf
);
898 expected_string
+= itoa(col
,buf
);
899 CONFIRM_EQ(tablerow
.getNextString(status
),expected_string
);
904 CONFIRM_EQ(row_count
,kERROR_COUNT
);
905 CONFIRM_EQ(column_count
,kERROR_COUNT
);
906 row_count
=column_count
=0;
912 //--------------------------------------------------------------------------
914 for (j
=0; j
<200; ++j
)
916 row
= row_count
? (randi(row_count
* 3) - row_count
) : (randi(200) - 100);
917 col
= column_count
? (randi(column_count
* 3) - column_count
) : (randi(200) - 100);
918 status
= U_ZERO_ERROR
;
919 string
= kErrorUChars
;
920 ResourceBundle array2d
=theBundle
.get(tag
, status
);
921 if(U_SUCCESS(status
)){
922 ResourceBundle tablerow
=array2d
.get(row
, status
);
923 if(U_SUCCESS(status
)) {
924 UnicodeString t
=tablerow
.getStringEx(col
, status
);
925 if(U_SUCCESS(status
)){
930 expected_status
= (row
>= 0 && row
< row_count
&& col
>= 0 && col
< column_count
) ?
931 expected_resource_status
: U_MISSING_RESOURCE_ERROR
;
932 CONFIRM_UErrorCode(status
,expected_status
);
934 if (U_SUCCESS(status
)){
936 expected_string
= base
;
937 expected_string
+= itoa(row
,buf
);
938 expected_string
+= itoa(col
,buf
);
940 expected_string
= kErrorUChars
;
942 CONFIRM_EQ(string
,expected_string
);
946 //--------------------------------------------------------------------------
949 uprv_strcpy(tag
, "tagged_array_");
950 uprv_strcat(tag
, frag
);
952 action
= param
[i
].name
;
958 status
= U_ZERO_ERROR
;
960 ResourceBundle tags
=theBundle
.get(tag
, status
);
961 CONFIRM_UErrorCode(status
, expected_resource_status
);
963 if (U_SUCCESS(status
)) {
964 UResType bundleType
=tags
.getType();
965 CONFIRM_EQ(bundleType
, URES_TABLE
);
967 tag_count
=tags
.getSize();
968 CONFIRM_GE((int32_t)tag_count
, (int32_t)0);
970 for(index
=0; index
<tag_count
; index
++){
971 ResourceBundle tagelement
=tags
.get(index
, status
);
972 UnicodeString key
=tagelement
.getKey();
973 UnicodeString value
=tagelement
.getNextString(status
);
974 logln("tag = " + key
+ ", value = " + value
);
975 if(key
.startsWith("tag") && value
.startsWith(base
)){
983 for(index
=0; index
<tag_count
; index
++){
984 ResourceBundle tagelement
=tags
.get(index
, status
);
985 const char *tkey
=NULL
;
986 UnicodeString value
=tagelement
.getNextString(&tkey
, status
);
987 UnicodeString
key(tkey
);
988 logln("tag = " + key
+ ", value = " + value
);
989 if(value
.startsWith(base
)){
1003 //--------------------------------------------------------------------------
1006 action
= param
[i
].name
;
1012 for (index
=-20; index
<20; ++index
)
1015 status
= U_ZERO_ERROR
;
1016 string
= kErrorUChars
;
1018 uprv_strcpy(item_tag
, "tag");
1019 uprv_strcat(item_tag
, itoa(index
,buf
));
1020 ResourceBundle tags
=theBundle
.get(tag
, status
);
1021 if(U_SUCCESS(status
)){
1022 ResourceBundle tagelement
=tags
.get(item_tag
, status
);
1023 if(!U_FAILURE(status
)){
1024 UResType elementType
=tagelement
.getType();
1025 CONFIRM_EQ(elementType
, (int32_t)URES_STRING
);
1026 const char* key
=tagelement
.getKey();
1027 CONFIRM_EQ((UnicodeString
)key
, (UnicodeString
)item_tag
);
1028 UnicodeString t
=tagelement
.getString(status
);
1029 if(!U_FAILURE(status
)){
1034 CONFIRM_UErrorCode(status
,U_MISSING_RESOURCE_ERROR
);
1037 if (status
!= U_MISSING_RESOURCE_ERROR
) {
1039 expected_string
= base
;
1040 expected_string
+= buf
;
1041 CONFIRM_EQ(string
,expected_string
);
1047 CONFIRM_EQ(count
, tag_count
);
1050 return (UBool
)(failOrig
== fail
);
1054 NewResourceBundleTest::record_pass()
1059 NewResourceBundleTest::record_fail()
1067 NewResourceBundleTest::TestNewTypes() {
1069 const char* testdatapath
;
1070 UErrorCode status
= U_ZERO_ERROR
;
1071 uint8_t *binResult
= NULL
;
1074 int32_t intResult
= 0;
1075 uint32_t uintResult
= 0;
1076 UChar expected
[] = { 'a','b','c','\0','d','e','f' };
1077 const char* expect
="tab:\t cr:\r ff:\f newline:\n backslash:\\\\ quote=\\\' doubleQuote=\\\" singlequoutes=''";
1080 testdatapath
=loadTestData(status
);
1082 if(U_FAILURE(status
))
1084 dataerrln("Could not load testdata.dat %s \n",u_errorName(status
));
1088 ResourceBundle
theBundle(testdatapath
, "testtypes", status
);
1089 ResourceBundle
bundle(testdatapath
, Locale("te_IN"),status
);
1091 UnicodeString emptyStr
= theBundle
.getStringEx("emptystring", status
);
1092 if(emptyStr
.length() != 0) {
1093 logln("Empty string returned invalid value\n");
1096 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1098 /* This test reads the string "abc\u0000def" from the bundle */
1099 /* if everything is working correctly, the size of this string */
1100 /* should be 7. Everything else is a wrong answer, esp. 3 and 6*/
1102 strcpy(action
, "getting and testing of string with embeded zero");
1103 ResourceBundle res
= theBundle
.get("zerotest", status
);
1104 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1105 CONFIRM_EQ(res
.getType(), URES_STRING
);
1106 UnicodeString zeroString
=res
.getString(status
);
1107 len
= zeroString
.length();
1108 if(U_SUCCESS(status
)){
1109 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1114 if(zeroString
[i
]!= expected
[i
]){
1115 logln("Output didnot match Expected: \\u%4X Got: \\u%4X", expected
[i
], zeroString
[i
]);
1119 strcpy(action
, "getting and testing of binary type");
1120 res
= theBundle
.get("binarytest", status
);
1121 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1122 CONFIRM_EQ(res
.getType(), URES_BINARY
);
1123 binResult
=(uint8_t*)res
.getBinary(len
, status
);
1124 if(U_SUCCESS(status
)){
1125 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1126 CONFIRM_EQ(len
, 15);
1127 for(i
= 0; i
<15; i
++) {
1128 CONFIRM_EQ(binResult
[i
], i
);
1132 strcpy(action
, "getting and testing of imported binary type");
1133 res
= theBundle
.get("importtest",status
);
1134 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1135 CONFIRM_EQ(res
.getType(), URES_BINARY
);
1136 binResult
=(uint8_t*)res
.getBinary(len
, status
);
1137 if(U_SUCCESS(status
)){
1138 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1139 CONFIRM_EQ(len
, 15);
1140 for(i
= 0; i
<15; i
++) {
1141 CONFIRM_EQ(binResult
[i
], i
);
1145 strcpy(action
, "getting and testing of integer types");
1146 res
= theBundle
.get("one", status
);
1147 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1148 CONFIRM_EQ(res
.getType(), URES_INT
);
1149 intResult
=res
.getInt(status
);
1150 uintResult
= res
.getUInt(status
);
1151 if(U_SUCCESS(status
)){
1152 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1153 CONFIRM_EQ(uintResult
, (uint32_t)intResult
);
1154 CONFIRM_EQ(intResult
, 1);
1157 strcpy(action
, "getting minusone");
1158 res
= theBundle
.get((const char*)"minusone", status
);
1159 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1160 CONFIRM_EQ(res
.getType(), URES_INT
);
1161 intResult
=res
.getInt(status
);
1162 uintResult
= res
.getUInt(status
);
1163 if(U_SUCCESS(status
)){
1164 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1165 CONFIRM_EQ(uintResult
, 0x0FFFFFFF); /* a 28 bit integer */
1166 CONFIRM_EQ(intResult
, -1);
1167 CONFIRM_NE(uintResult
, (uint32_t)intResult
);
1170 strcpy(action
, "getting plusone");
1171 res
= theBundle
.get("plusone",status
);
1172 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1173 CONFIRM_EQ(res
.getType(), URES_INT
);
1174 intResult
=res
.getInt(status
);
1175 uintResult
= res
.getUInt(status
);
1176 if(U_SUCCESS(status
)){
1177 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1178 CONFIRM_EQ(uintResult
, (uint32_t)intResult
);
1179 CONFIRM_EQ(intResult
, 1);
1182 res
= theBundle
.get("onehundredtwentythree",status
);
1183 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1184 CONFIRM_EQ(res
.getType(), URES_INT
);
1185 intResult
=res
.getInt(status
);
1186 if(U_SUCCESS(status
)){
1187 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1188 CONFIRM_EQ(intResult
, 123);
1191 /* this tests if escapes are preserved or not */
1193 UnicodeString str
= theBundle
.getStringEx("testescape",status
);
1194 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1195 if(U_SUCCESS(status
)){
1196 u_charsToUChars(expect
,uExpect
,(int32_t)uprv_strlen(expect
)+1);
1197 if(str
.compare(uExpect
)!=0){
1198 errln("Did not get the expected string for testescape expected. Expected : "
1199 +UnicodeString(uExpect
)+ " Got: " + str
);
1203 /* test for jitterbug#1435 */
1205 UnicodeString str
= theBundle
.getStringEx("test_underscores",status
);
1206 expect
="test message ....";
1207 CONFIRM_UErrorCode(status
, U_ZERO_ERROR
);
1208 u_charsToUChars(expect
,uExpect
,(int32_t)uprv_strlen(expect
)+1);
1209 if(str
.compare(uExpect
)!=0){
1210 errln("Did not get the expected string for test_underscores.\n");
1218 NewResourceBundleTest::TestGetByFallback() {
1219 UErrorCode status
= U_ZERO_ERROR
;
1221 ResourceBundle
heRes(NULL
, "he", status
);
1223 heRes
.getWithFallback("calendar", status
).getWithFallback("islamic-civil", status
).getWithFallback("DateTime", status
);
1224 if(U_SUCCESS(status
)) {
1225 errln("he locale's Islamic-civil DateTime resource exists. How did it get here?\n");
1227 status
= U_ZERO_ERROR
;
1229 heRes
.getWithFallback("calendar", status
).getWithFallback("islamic-civil", status
).getWithFallback("eras", status
);
1230 if(U_FAILURE(status
)) {
1231 dataerrln("Didn't get Islamic Eras. I know they are there! - %s", u_errorName(status
));
1233 status
= U_ZERO_ERROR
;
1235 ResourceBundle
rootRes(NULL
, "root", status
);
1236 rootRes
.getWithFallback("calendar", status
).getWithFallback("islamic-civil", status
).getWithFallback("DateTime", status
);
1237 if(U_SUCCESS(status
)) {
1238 errln("Root's Islamic-civil's DateTime resource exists. How did it get here?\n");
1240 status
= U_ZERO_ERROR
;
1245 #define REQUIRE_SUCCESS(status) UPRV_BLOCK_MACRO_BEGIN { \
1246 if (status.errIfFailureAndReset("line %d", __LINE__)) { \
1249 } UPRV_BLOCK_MACRO_END
1251 #define REQUIRE_ERROR(expected, status) UPRV_BLOCK_MACRO_BEGIN { \
1252 if (!status.expectErrorAndReset(expected, "line %d", __LINE__)) { \
1255 } UPRV_BLOCK_MACRO_END
1258 * Tests the --filterDir option in genrb.
1260 * Input resource text file: test/testdata/filtertest.txt
1261 * Input filter rule file: test/testdata/filters/filtertest.txt
1263 * The resource bundle should contain no keys matched by the filter
1264 * and should contain all other keys.
1266 void NewResourceBundleTest::TestFilter() {
1267 IcuTestErrorCode
status(*this, "TestFilter");
1269 ResourceBundle
rb(loadTestData(status
), "filtertest", status
);
1270 REQUIRE_SUCCESS(status
);
1271 assertEquals("rb", rb
.getType(), URES_TABLE
);
1273 ResourceBundle alabama
= rb
.get("alabama", status
);
1274 REQUIRE_SUCCESS(status
);
1275 assertEquals("alabama", alabama
.getType(), URES_TABLE
);
1278 ResourceBundle alaska
= alabama
.get("alaska", status
);
1279 REQUIRE_SUCCESS(status
);
1280 assertEquals("alaska", alaska
.getType(), URES_TABLE
);
1283 ResourceBundle arizona
= alaska
.get("arizona", status
);
1284 REQUIRE_SUCCESS(status
);
1285 assertEquals("arizona", arizona
.getType(), URES_STRING
);
1286 assertEquals("arizona", u
"arkansas", arizona
.getString(status
));
1287 REQUIRE_SUCCESS(status
);
1289 // Filter: california should not be included
1290 ResourceBundle california
= alaska
.get("california", status
);
1291 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1294 // Filter: connecticut should not be included
1295 ResourceBundle connecticut
= alabama
.get("connecticut", status
);
1296 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1299 ResourceBundle fornia
= rb
.get("fornia", status
);
1300 REQUIRE_SUCCESS(status
);
1301 assertEquals("fornia", fornia
.getType(), URES_TABLE
);
1304 // Filter: hawaii should not be included based on parent inheritance
1305 ResourceBundle hawaii
= fornia
.get("hawaii", status
);
1306 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1308 // Filter: illinois should not be included based on direct rule
1309 ResourceBundle illinois
= fornia
.get("illinois", status
);
1310 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1313 ResourceBundle mississippi
= rb
.get("mississippi", status
);
1314 REQUIRE_SUCCESS(status
);
1315 assertEquals("mississippi", mississippi
.getType(), URES_TABLE
);
1318 ResourceBundle louisiana
= mississippi
.get("louisiana", status
);
1319 REQUIRE_SUCCESS(status
);
1320 assertEquals("louisiana", louisiana
.getType(), URES_TABLE
);
1323 ResourceBundle maine
= louisiana
.get("maine", status
);
1324 REQUIRE_SUCCESS(status
);
1325 assertEquals("maine", maine
.getType(), URES_STRING
);
1326 assertEquals("maine", u
"maryland", maine
.getString(status
));
1327 REQUIRE_SUCCESS(status
);
1329 ResourceBundle iowa
= louisiana
.get("iowa", status
);
1330 REQUIRE_SUCCESS(status
);
1331 assertEquals("iowa", iowa
.getType(), URES_STRING
);
1332 assertEquals("iowa", u
"kansas", iowa
.getString(status
));
1333 REQUIRE_SUCCESS(status
);
1335 // Filter: missouri should not be included
1336 ResourceBundle missouri
= louisiana
.get("missouri", status
);
1337 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1340 ResourceBundle michigan
= mississippi
.get("michigan", status
);
1341 REQUIRE_SUCCESS(status
);
1342 assertEquals("michigan", michigan
.getType(), URES_TABLE
);
1345 ResourceBundle maine
= michigan
.get("maine", status
);
1346 REQUIRE_SUCCESS(status
);
1347 assertEquals("maine", maine
.getType(), URES_STRING
);
1348 assertEquals("maine", u
"minnesota", maine
.getString(status
));
1349 REQUIRE_SUCCESS(status
);
1351 // Filter: iowa should not be included
1352 ResourceBundle iowa
= michigan
.get("iowa", status
);
1353 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1355 ResourceBundle missouri
= michigan
.get("missouri", status
);
1356 REQUIRE_SUCCESS(status
);
1357 assertEquals("missouri", missouri
.getType(), URES_STRING
);
1358 assertEquals("missouri", u
"nebraska", missouri
.getString(status
));
1359 REQUIRE_SUCCESS(status
);
1362 ResourceBundle nevada
= mississippi
.get("nevada", status
);
1363 REQUIRE_SUCCESS(status
);
1364 assertEquals("nevada", nevada
.getType(), URES_TABLE
);
1367 ResourceBundle maine
= nevada
.get("maine", status
);
1368 REQUIRE_SUCCESS(status
);
1369 assertEquals("maine", maine
.getType(), URES_STRING
);
1370 assertEquals("maine", u
"new-hampshire", maine
.getString(status
));
1371 REQUIRE_SUCCESS(status
);
1373 // Filter: iowa should not be included
1374 ResourceBundle iowa
= nevada
.get("iowa", status
);
1375 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1377 // Filter: missouri should not be included
1378 ResourceBundle missouri
= nevada
.get("missouri", status
);
1379 REQUIRE_ERROR(U_MISSING_RESOURCE_ERROR
, status
);
1383 // Filter: northCarolina should be included based on direct rule,
1384 // and so should its child, northDakota
1385 ResourceBundle northCarolina
= rb
.get("northCarolina", status
);
1386 REQUIRE_SUCCESS(status
);
1387 assertEquals("northCarolina", northCarolina
.getType(), URES_TABLE
);
1390 ResourceBundle northDakota
= northCarolina
.get("northDakota", status
);
1391 REQUIRE_SUCCESS(status
);
1392 assertEquals("northDakota", northDakota
.getType(), URES_STRING
);
1393 assertEquals("northDakota", u
"west-virginia", northDakota
.getString(status
));
1394 REQUIRE_SUCCESS(status
);
1398 void NewResourceBundleTest::TestIntervalAliasFallbacks() {
1399 const char* locales
[] = {
1400 // Thee will not cause infinity loop
1404 // These will cause infinity loop
1417 const char* calendars
[] = {
1418 // These won't cause infinity loop
1422 // These will cause infinity loop
1427 "ethiopic-amete-alem",
1433 for (int lidx
= 0; lidx
< UPRV_LENGTHOF(locales
); lidx
++) {
1434 UErrorCode status
= U_ZERO_ERROR
;
1435 UResourceBundle
*rb
= ures_open(NULL
, locales
[lidx
], &status
);
1436 if (U_FAILURE(status
)) {
1437 errln("Cannot open bundle for locale %s", locales
[lidx
]);
1440 for (int cidx
= 0; cidx
< UPRV_LENGTHOF(calendars
); cidx
++) {
1442 key
.append("calendar/", status
);
1443 key
.append(calendars
[cidx
], status
);
1444 key
.append("/intervalFormats/fallback", status
);
1445 if (! logKnownIssue("20400")) {
1446 int32_t resStrLen
= 0;
1447 ures_getStringByKeyWithFallback(rb
, key
.data(), &resStrLen
, &status
);
1449 if (U_FAILURE(status
)) {
1450 errln("Cannot ures_getStringByKeyWithFallback('%s') on locale %s",
1451 key
.data(), locales
[lidx
]);
1459 #if U_ENABLE_TRACING
1461 static std::vector
<std::string
> gResourcePathsTraced
;
1462 static std::vector
<std::string
> gDataFilesTraced
;
1463 static std::vector
<std::string
> gBundlesTraced
;
1465 static void U_CALLCONV
traceData(
1472 // NOTE: Whether this test is run in isolation affects whether or not
1473 // *.res files are opened. For stability, ignore *.res file opens.
1475 if (fnNumber
== UTRACE_UDATA_RESOURCE
) {
1476 va_arg(args
, const char*); // type
1477 va_arg(args
, const char*); // file
1478 const char* resourcePath
= va_arg(args
, const char*);
1479 gResourcePathsTraced
.push_back(resourcePath
);
1480 } else if (fnNumber
== UTRACE_UDATA_BUNDLE
) {
1481 const char* filePath
= va_arg(args
, const char*);
1482 gBundlesTraced
.push_back(filePath
);
1483 } else if (fnNumber
== UTRACE_UDATA_DATA_FILE
) {
1484 const char* filePath
= va_arg(args
, const char*);
1485 gDataFilesTraced
.push_back(filePath
);
1486 } else if (fnNumber
== UTRACE_UDATA_RES_FILE
) {
1491 void NewResourceBundleTest::TestTrace() {
1492 IcuTestErrorCode
status(*this, "TestTrace");
1494 assertEquals("Start position stability coverage", 0x3000, UTRACE_UDATA_START
);
1496 const void* context
;
1497 utrace_setFunctions(context
, nullptr, nullptr, traceData
);
1498 utrace_setLevel(UTRACE_VERBOSE
);
1501 LocalPointer
<BreakIterator
> brkitr(BreakIterator::createWordInstance("zh-CN", status
));
1503 assertEquals("Should touch expected resource paths",
1504 { "/boundaries", "/boundaries/word", "/boundaries/word" },
1505 gResourcePathsTraced
);
1506 assertEquals("Should touch expected resource bundles",
1507 { U_ICUDATA_NAME
"-brkitr/zh.res" },
1509 assertEquals("Should touch expected data files",
1510 { U_ICUDATA_NAME
"-brkitr/word.brk" },
1512 gResourcePathsTraced
.clear();
1513 gDataFilesTraced
.clear();
1514 gBundlesTraced
.clear();
1518 ucurr_getDefaultFractionDigits(u
"USD", status
);
1520 assertEquals("Should touch expected resource paths",
1521 { "/CurrencyMeta", "/CurrencyMeta/DEFAULT", "/CurrencyMeta/DEFAULT" },
1522 gResourcePathsTraced
);
1523 assertEquals("Should touch expected resource bundles",
1524 { U_ICUDATA_NAME
"-curr/supplementalData.res" },
1526 assertEquals("Should touch expected data files",
1529 gResourcePathsTraced
.clear();
1530 gDataFilesTraced
.clear();
1531 gBundlesTraced
.clear();
1534 utrace_setFunctions(context
, nullptr, nullptr, nullptr);