Index: source/test/intltest/regextst.cpp |
diff --git a/source/test/intltest/regextst.cpp b/source/test/intltest/regextst.cpp |
index f440c26bf065aa4fcff726bee63cd13b4f7686b9..bf13426041ee166bebc57a3d8ffc7207e22b49a1 100644 |
--- a/source/test/intltest/regextst.cpp |
+++ b/source/test/intltest/regextst.cpp |
@@ -1,6 +1,6 @@ |
/******************************************************************** |
* COPYRIGHT: |
- * Copyright (c) 2002-2014, International Business Machines Corporation and |
+ * Copyright (c) 2002-2015, International Business Machines Corporation and |
* others. All Rights Reserved. |
********************************************************************/ |
@@ -23,6 +23,10 @@ |
#include "intltest.h" |
#if !UCONFIG_NO_REGULAR_EXPRESSIONS |
+#include <stdlib.h> |
+#include <stdio.h> |
+#include <string.h> |
+ |
#include "unicode/localpointer.h" |
#include "unicode/regex.h" |
#include "unicode/uchar.h" |
@@ -31,13 +35,13 @@ |
#include "unicode/uregex.h" |
#include "unicode/usetiter.h" |
#include "unicode/ustring.h" |
+#include "unicode/utext.h" |
+ |
#include "regextst.h" |
#include "regexcmp.h" |
#include "uvector.h" |
#include "util.h" |
-#include <stdlib.h> |
-#include <string.h> |
-#include <stdio.h> |
+#include "cmemory.h" |
#include "cstring.h" |
#include "uinvchar.h" |
@@ -147,6 +151,15 @@ void RegexTest::runIndexedTest( int32_t index, UBool exec, const char* &name, ch |
case 25: name = "TestBug11371"; |
if (exec) TestBug11371(); |
break; |
+ case 26: name = "TestBug11480"; |
+ if (exec) TestBug11480(); |
+ break; |
+ case 27: name = "NamedCapture"; |
+ if (exec) NamedCapture(); |
+ break; |
+ case 28: name = "NamedCaptureLimits"; |
+ if (exec) NamedCaptureLimits(); |
+ break; |
default: name = ""; |
break; //needed to end loop |
} |
@@ -239,7 +252,12 @@ if (status!=errcode) {dataerrln("RegexTest failure at line %d. Expected status= |
#define REGEX_ASSERT_L(expr, line) {if ((expr)==FALSE) { \ |
errln("RegexTest failure at line %d, from %d.", __LINE__, (line)); return;}} |
-#define REGEX_ASSERT_UNISTR(ustr,inv) {if (!(ustr==inv)) {errln("%s:%d: RegexTest failure: REGEX_ASSERT_UNISTR(%s,%s) failed \n", __FILE__, __LINE__, extractToAssertBuf(ustr),inv);};} |
+// expected: const char * , restricted to invariant characters. |
+// actual: const UnicodeString & |
+#define REGEX_ASSERT_UNISTR(expected, actual) { \ |
+ if (UnicodeString(expected, -1, US_INV) != (actual)) { \ |
+ errln("%s:%d: RegexTest failure: REGEX_ASSERT_UNISTR(%s, %s) failed \n", \ |
+ __FILE__, __LINE__, expected, extractToAssertBuf(actual));};} |
static UBool testUTextEqual(UText *uta, UText *utb) { |
@@ -1423,8 +1441,8 @@ void RegexTest::API_Replace() { |
REGEX_ASSERT(dest == "The value of $1 is bc.defg"); |
dest = matcher2->replaceFirst("$ by itself, no group number $$$", status); |
- REGEX_CHECK_STATUS; |
- REGEX_ASSERT(dest == "$ by itself, no group number $$$defg"); |
+ REGEX_ASSERT(U_FAILURE(status)); |
+ status = U_ZERO_ERROR; |
UnicodeString replacement = UNICODE_STRING_SIMPLE("Supplemental Digit 1 $\\U0001D7CF."); |
replacement = replacement.unescape(); |
@@ -2050,47 +2068,72 @@ void RegexTest::API_Match_UTF8() { |
utext_close(&destText); |
utext_openUnicodeString(&destText, &dest, &status); |
- result = matcher->group(0, NULL, status); |
+ int64_t length; |
+ result = matcher->group(0, NULL, length, status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT_UTEXT_UTF8(str_0123456789, result); |
utext_close(result); |
- result = matcher->group(0, &destText, status); |
+ result = matcher->group(0, &destText, length, status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(result == &destText); |
- REGEX_ASSERT_UTEXT_UTF8(str_0123456789, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 0); |
+ REGEX_ASSERT(length == 10); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
- result = matcher->group(1, NULL, status); |
+ // Capture Group 1 == "234567" |
+ result = matcher->group(1, NULL, length, status); |
REGEX_CHECK_STATUS; |
- const char str_234567[] = { 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x00 }; /* 234567 */ |
- REGEX_ASSERT_UTEXT_UTF8(str_234567, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 2); |
+ REGEX_ASSERT(length == 6); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
utext_close(result); |
- result = matcher->group(1, &destText, status); |
+ |
+ result = matcher->group(1, &destText, length, status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(result == &destText); |
- REGEX_ASSERT_UTEXT_UTF8(str_234567, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 2); |
+ REGEX_ASSERT(length == 6); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
+ utext_close(result); |
- result = matcher->group(2, NULL, status); |
+ // Capture Group 2 == "45" |
+ result = matcher->group(2, NULL, length, status); |
REGEX_CHECK_STATUS; |
- const char str_45[] = { 0x34, 0x35, 0x00 }; /* 45 */ |
- REGEX_ASSERT_UTEXT_UTF8(str_45, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 4); |
+ REGEX_ASSERT(length == 2); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
utext_close(result); |
- result = matcher->group(2, &destText, status); |
+ |
+ result = matcher->group(2, &destText, length, status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(result == &destText); |
- REGEX_ASSERT_UTEXT_UTF8(str_45, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 4); |
+ REGEX_ASSERT(length == 2); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
+ utext_close(result); |
- result = matcher->group(3, NULL, status); |
+ // Capture Group 3 == "89" |
+ result = matcher->group(3, NULL, length, status); |
REGEX_CHECK_STATUS; |
- const char str_89[] = { 0x38, 0x39, 0x00 }; /* 89 */ |
- REGEX_ASSERT_UTEXT_UTF8(str_89, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 8); |
+ REGEX_ASSERT(length == 2); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
utext_close(result); |
- result = matcher->group(3, &destText, status); |
+ |
+ result = matcher->group(3, &destText, length, status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(result == &destText); |
- REGEX_ASSERT_UTEXT_UTF8(str_89, result); |
+ REGEX_ASSERT(utext_getNativeIndex(result) == 8); |
+ REGEX_ASSERT(length == 2); |
+ REGEX_ASSERT_UTEXT_INVARIANT("0123456789", result); |
+ utext_close(result); |
+ // Capture Group number out of range. |
+ status = U_ZERO_ERROR; |
REGEX_ASSERT_FAIL(matcher->group(-1, status), U_INDEX_OUTOFBOUNDS_ERROR); |
+ status = U_ZERO_ERROR; |
REGEX_ASSERT_FAIL(matcher->group( 4, status), U_INDEX_OUTOFBOUNDS_ERROR); |
+ status = U_ZERO_ERROR; |
matcher->reset(); |
REGEX_ASSERT_FAIL(matcher->group( 0, status), U_REGEX_INVALID_STATE); |
@@ -2602,7 +2645,9 @@ void RegexTest::API_Replace_UTF8() { |
REGEX_ASSERT(result == &destText); |
REGEX_ASSERT_UTEXT_UTF8(str_Thevalueof1isbcdefg, result); |
- const char str_byitselfnogroupnumber[] = { 0x24, 0x20, 0x62, 0x79, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, 0x66, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x24, 0x24, 0x24, 0x00 }; /* $ by itself, no group number $$$ */ |
+ const char str_byitselfnogroupnumber[] = { 0x5c, 0x24, 0x20, 0x62, 0x79, 0x20, 0x69, 0x74, 0x73, 0x65, 0x6c, |
+ 0x66, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x6e, 0x75, 0x6d, 0x62, |
+ 0x65, 0x72, 0x20, 0x5c, 0x24, 0x5c, 0x24, 0x5c, 0x24, 0x00 }; /* \$ by itself, no group number \$\$\$ */ |
utext_openUTF8(&replText, str_byitselfnogroupnumber, -1, &status); |
result = matcher2->replaceFirst(&replText, NULL, status); |
REGEX_CHECK_STATUS; |
@@ -3069,6 +3114,37 @@ void RegexTest::API_Pattern_UTF8() { |
// |
+ // split of a UText based string, with library allocating output UTexts. |
+ // |
+ { |
+ status = U_ZERO_ERROR; |
+ RegexMatcher matcher(UnicodeString("(:)"), 0, status); |
+ UnicodeString stringToSplit("first:second:third"); |
+ UText *textToSplit = utext_openUnicodeString(NULL, &stringToSplit, &status); |
+ REGEX_CHECK_STATUS; |
+ |
+ UText *splits[10] = {NULL}; |
+ int32_t numFields = matcher.split(textToSplit, splits, UPRV_LENGTHOF(splits), status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(numFields == 5); |
+ REGEX_ASSERT_UTEXT_INVARIANT("first", splits[0]); |
+ REGEX_ASSERT_UTEXT_INVARIANT(":", splits[1]); |
+ REGEX_ASSERT_UTEXT_INVARIANT("second", splits[2]); |
+ REGEX_ASSERT_UTEXT_INVARIANT(":", splits[3]); |
+ REGEX_ASSERT_UTEXT_INVARIANT("third", splits[4]); |
+ REGEX_ASSERT(splits[5] == NULL); |
+ |
+ for (int i=0; i<UPRV_LENGTHOF(splits); i++) { |
+ if (splits[i]) { |
+ utext_close(splits[i]); |
+ splits[i] = NULL; |
+ } |
+ } |
+ utext_close(textToSplit); |
+ } |
+ |
+ |
+ // |
// RegexPattern::pattern() and patternText() |
// |
pat1 = new RegexPattern(); |
@@ -3079,7 +3155,7 @@ void RegexTest::API_Pattern_UTF8() { |
regextst_openUTF8FromInvariant(&re1, helloWorldInvariant, -1, &status); |
pat1 = RegexPattern::compile(&re1, pe, status); |
REGEX_CHECK_STATUS; |
- REGEX_ASSERT_UNISTR(pat1->pattern(),"(Hello, world)*"); |
+ REGEX_ASSERT_UNISTR("(Hello, world)*", pat1->pattern()); |
REGEX_ASSERT_UTEXT_INVARIANT("(Hello, world)*", pat1->patternText(status)); |
delete pat1; |
@@ -3784,7 +3860,7 @@ void RegexTest::Errors() { |
REGEX_ERR("abc{1,,2}",1,7, U_REGEX_BAD_INTERVAL); |
REGEX_ERR("abc{1,2a}",1,8, U_REGEX_BAD_INTERVAL); |
REGEX_ERR("abc{222222222222222222222}",1,14, U_REGEX_NUMBER_TOO_BIG); |
- REGEX_ERR("abc{5,50000000000}", 1, 17, U_REGEX_NUMBER_TOO_BIG); // Overflows int during scan |
+ REGEX_ERR("abc{5,50000000000}", 1, 16, U_REGEX_NUMBER_TOO_BIG); // Overflows int during scan |
REGEX_ERR("abc{5,687865858}", 1, 16, U_REGEX_NUMBER_TOO_BIG); // Overflows regex binary format |
REGEX_ERR("abc{687865858,687865859}", 1, 24, U_REGEX_NUMBER_TOO_BIG); |
@@ -4806,6 +4882,15 @@ void RegexTest::Callbacks() { |
REGEX_ASSERT(matcher.matches(status)==FALSE); |
REGEX_ASSERT(status == U_REGEX_STOPPED_BY_CALLER); |
REGEX_ASSERT(cbInfo.numCalls == 4); |
+ |
+ // A longer running find that the callback function will abort. |
+ status = U_ZERO_ERROR; |
+ cbInfo.reset(4); |
+ s = "aaaaaaaaaaaaaaaaaaaaaaab"; |
+ matcher.reset(s); |
+ REGEX_ASSERT(matcher.find(status)==FALSE); |
+ REGEX_ASSERT(status == U_REGEX_STOPPED_BY_CALLER); |
+ REGEX_ASSERT(cbInfo.numCalls == 4); |
} |
@@ -4995,7 +5080,11 @@ void RegexTest::PreAllocatedUTextCAPI () { |
UChar text1[80]; |
UText *actual; |
UBool result; |
- u_uastrncpy(text1, "noise abc interior def, and this is off the end", sizeof(text1)/2); |
+ int64_t length = 0; |
+ |
+ u_uastrncpy(text1, "noise abc interior def, and this is off the end", UPRV_LENGTHOF(text1)); |
+ // 012345678901234567890123456789012345678901234567 |
+ // 0 1 2 3 4 |
status = U_ZERO_ERROR; |
re = uregex_openC("abc(.*?)def", 0, NULL, &status); |
@@ -5005,26 +5094,29 @@ void RegexTest::PreAllocatedUTextCAPI () { |
result = uregex_find(re, 0, &status); |
REGEX_ASSERT(result==TRUE); |
- /* Capture Group 0, the full match. Should succeed. */ |
+ /* Capture Group 0, the full match. Should succeed. "abc interior def" */ |
status = U_ZERO_ERROR; |
- actual = uregex_groupUTextDeep(re, 0, &bufferText, &status); |
+ actual = uregex_groupUText(re, 0, &bufferText, &length, &status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(actual == &bufferText); |
- REGEX_ASSERT_UTEXT_INVARIANT("abc interior def", actual); |
+ REGEX_ASSERT(utext_getNativeIndex(actual) == 6); |
+ REGEX_ASSERT(length == 16); |
+ REGEX_ASSERT(utext_nativeLength(actual) == 47); |
- /* Capture group #1. Should succeed. */ |
+ /* Capture group #1. Should succeed, matching " interior ". */ |
status = U_ZERO_ERROR; |
- actual = uregex_groupUTextDeep(re, 1, &bufferText, &status); |
+ actual = uregex_groupUText(re, 1, &bufferText, &length, &status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(actual == &bufferText); |
- REGEX_ASSERT_UTEXT_INVARIANT(" interior ", actual); |
+ REGEX_ASSERT(utext_getNativeIndex(actual) == 9); // position of " interior " |
+ REGEX_ASSERT(length == 10); |
+ REGEX_ASSERT(utext_nativeLength(actual) == 47); |
/* Capture group out of range. Error. */ |
status = U_ZERO_ERROR; |
- actual = uregex_groupUTextDeep(re, 2, &bufferText, &status); |
+ actual = uregex_groupUText(re, 2, &bufferText, &length, &status); |
REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); |
REGEX_ASSERT(actual == &bufferText); |
- |
uregex_close(re); |
} |
@@ -5037,10 +5129,12 @@ void RegexTest::PreAllocatedUTextCAPI () { |
UChar text2[80]; |
UText replText = UTEXT_INITIALIZER; |
UText *result; |
+ status = U_ZERO_ERROR; |
+ utext_openUnicodeString(&bufferText, &buffer, &status); |
status = U_ZERO_ERROR; |
- u_uastrncpy(text1, "Replace xaax x1x x...x.", sizeof(text1)/2); |
- u_uastrncpy(text2, "No match here.", sizeof(text2)/2); |
+ u_uastrncpy(text1, "Replace xaax x1x x...x.", UPRV_LENGTHOF(text1)); |
+ u_uastrncpy(text2, "No match here.", UPRV_LENGTHOF(text2)/2); |
regextst_openUTF8FromInvariant(&replText, "<$1>", -1, &status); |
re = uregex_openC("x(.*?)x", 0, NULL, &status); |
@@ -5048,7 +5142,9 @@ void RegexTest::PreAllocatedUTextCAPI () { |
/* Normal case, with match */ |
uregex_setText(re, text1, -1, &status); |
+ REGEX_CHECK_STATUS; |
utext_replace(&bufferText, 0, utext_nativeLength(&bufferText), NULL, 0, &status); |
+ REGEX_CHECK_STATUS; |
result = uregex_replaceFirstUText(re, &replText, &bufferText, &status); |
REGEX_CHECK_STATUS; |
REGEX_ASSERT(result == &bufferText); |
@@ -5064,7 +5160,7 @@ void RegexTest::PreAllocatedUTextCAPI () { |
/* Unicode escapes */ |
uregex_setText(re, text1, -1, &status); |
- regextst_openUTF8FromInvariant(&replText, "\\\\\\u0041$1\\U00000042$\\a", -1, &status); |
+ regextst_openUTF8FromInvariant(&replText, "\\\\\\u0041$1\\U00000042\\$\\a", -1, &status); |
utext_replace(&bufferText, 0, utext_nativeLength(&bufferText), NULL, 0, &status); |
result = uregex_replaceFirstUText(re, &replText, &bufferText, &status); |
REGEX_CHECK_STATUS; |
@@ -5123,6 +5219,276 @@ void RegexTest::PreAllocatedUTextCAPI () { |
utext_close(&patternText); |
} |
+ |
+//-------------------------------------------------------------- |
+// |
+// NamedCapture Check basic named capture group functionality |
+// |
+//-------------------------------------------------------------- |
+void RegexTest::NamedCapture() { |
+ UErrorCode status = U_ZERO_ERROR; |
+ RegexPattern *pat = RegexPattern::compile(UnicodeString( |
+ "abc()()(?<three>xyz)(de)(?<five>hmm)(?<six>oh)f\\k<five>"), 0, status); |
+ REGEX_CHECK_STATUS; |
+ int32_t group = pat->groupNumberFromName("five", -1, status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(5 == group); |
+ group = pat->groupNumberFromName("three", -1, status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(3 == group); |
+ |
+ status = U_ZERO_ERROR; |
+ group = pat->groupNumberFromName(UnicodeString("six"), status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(6 == group); |
+ |
+ status = U_ZERO_ERROR; |
+ group = pat->groupNumberFromName(UnicodeString("nosuch"), status); |
+ U_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ |
+ // After copying a pattern, named capture should still work in the copy. |
+ RegexPattern *copiedPat = new RegexPattern(*pat); |
+ REGEX_ASSERT(*copiedPat == *pat); |
+ delete pat; pat = NULL; // Delete original, copy should have no references back to it. |
+ |
+ group = copiedPat->groupNumberFromName("five", -1, status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(5 == group); |
+ group = copiedPat->groupNumberFromName("three", -1, status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(3 == group); |
+ delete copiedPat; |
+ |
+ // ReplaceAll with named capture group. |
+ status = U_ZERO_ERROR; |
+ UnicodeString text("Substitution of <<quotes>> for <<double brackets>>"); |
+ RegexMatcher *m = new RegexMatcher(UnicodeString("<<(?<mid>.+?)>>"), text, 0, status); |
+ REGEX_CHECK_STATUS; |
+ // m.pattern().dumpPattern(); |
+ UnicodeString replacedText = m->replaceAll("'${mid}'", status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("Substitution of 'quotes' for 'double brackets'") == replacedText); |
+ delete m; |
+ |
+ // ReplaceAll, allowed capture group numbers. |
+ text = UnicodeString("abcmxyz"); |
+ m = new RegexMatcher(UnicodeString("..(?<one>m)(.)(.)"), text, 0, status); |
+ REGEX_CHECK_STATUS; |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$0>"), status); // group 0, full match, is allowed. |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<bcmxy>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$1>"), status); // group 1 by number. |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<m>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<${one}>"), status); // group 1 by name. |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<m>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$2>"), status); // group 2. |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<x>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$3>"), status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<y>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$4>"), status); |
+ REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$04>"), status); // group 0, leading 0, |
+ REGEX_CHECK_STATUS; // trailing out-of-range 4 passes through. |
+ REGEX_ASSERT(UnicodeString("a<bcmxy4>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$000016>"), status); // Consume leading zeroes. Don't consume digits |
+ REGEX_CHECK_STATUS; // that push group num out of range. |
+ REGEX_ASSERT(UnicodeString("a<m6>z") == replacedText); // This is group 1. |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<$3$2$1${one}>"), status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<yxmm>z") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("$3$2$1${one}"), status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("ayxmmz") == replacedText); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<${noSuchName}>"), status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<${invalid-name}>"), status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("<${one"), status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ replacedText = m->replaceAll(UnicodeString("$not a capture group"), status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ delete m; |
+ |
+ // Repeat the above replaceAll() tests using the plain C API, which |
+ // has a separate implementation internally. |
+ // TODO: factor out the test data. |
+ |
+ status = U_ZERO_ERROR; |
+ URegularExpression *re = uregex_openC("..(?<one>m)(.)(.)", 0, NULL, &status); |
+ REGEX_CHECK_STATUS; |
+ text = UnicodeString("abcmxyz"); |
+ uregex_setText(re, text.getBuffer(), text.length(), &status); |
+ REGEX_CHECK_STATUS; |
+ |
+ UChar resultBuf[100]; |
+ int32_t resultLength; |
+ UnicodeString repl; |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$0>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<bcmxy>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$1>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<m>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<${one}>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<m>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$2>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<x>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$3>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<y>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$4>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$04>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<bcmxy4>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$000016>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<m6>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<$3$2$1${one}>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("a<yxmm>z") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("$3$2$1${one}"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(UnicodeString("ayxmmz") == UnicodeString(resultBuf, resultLength)); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<${noSuchName}>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<${invalid-name}>"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("<${one"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ status = U_ZERO_ERROR; |
+ repl = UnicodeString("$not a capture group"); |
+ resultLength = uregex_replaceAll(re, repl.getBuffer(), repl.length(), resultBuf, UPRV_LENGTHOF(resultBuf), &status); |
+ REGEX_ASSERT(status == U_REGEX_INVALID_CAPTURE_GROUP_NAME); |
+ |
+ uregex_close(re); |
+} |
+ |
+//-------------------------------------------------------------- |
+// |
+// NamedCaptureLimits Patterns with huge numbers of named capture groups. |
+// The point is not so much what the exact limit is, |
+// but that a largish number doesn't hit bad non-linear performance, |
+// and that exceeding the limit fails cleanly. |
+// |
+//-------------------------------------------------------------- |
+void RegexTest::NamedCaptureLimits() { |
+ if (quick) { |
+ logln("Skipping test. Runs in exhuastive mode only."); |
+ return; |
+ } |
+ const int32_t goodLimit = 1000000; // Pattern w this many groups builds successfully. |
+ const int32_t failLimit = 10000000; // Pattern exceeds internal limits, fails to compile. |
+ char nnbuf[100]; |
+ UnicodeString pattern; |
+ int32_t nn; |
+ |
+ for (nn=1; nn<goodLimit; nn++) { |
+ sprintf(nnbuf, "(?<nn%d>)", nn); |
+ pattern.append(UnicodeString(nnbuf, -1, US_INV)); |
+ } |
+ UErrorCode status = U_ZERO_ERROR; |
+ RegexPattern *pat = RegexPattern::compile(pattern, 0, status); |
+ REGEX_CHECK_STATUS; |
+ for (nn=1; nn<goodLimit; nn++) { |
+ sprintf(nnbuf, "nn%d", nn); |
+ int32_t groupNum = pat->groupNumberFromName(nnbuf, -1, status); |
+ REGEX_ASSERT(nn == groupNum); |
+ if (nn != groupNum) { |
+ break; |
+ } |
+ } |
+ delete pat; |
+ |
+ pattern.remove(); |
+ for (nn=1; nn<failLimit; nn++) { |
+ sprintf(nnbuf, "(?<nn%d>)", nn); |
+ pattern.append(UnicodeString(nnbuf, -1, US_INV)); |
+ } |
+ status = U_ZERO_ERROR; |
+ pat = RegexPattern::compile(pattern, 0, status); |
+ REGEX_ASSERT(status == U_REGEX_PATTERN_TOO_BIG); |
+ delete pat; |
+} |
+ |
+ |
//-------------------------------------------------------------- |
// |
// Bug7651 Regex pattern that exceeds default operator stack depth in matcher. |
@@ -5414,5 +5780,49 @@ void RegexTest::TestBug11371() { |
} |
} |
-#endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */ |
+void RegexTest::TestBug11480() { |
+ // C API, get capture group of a group that does not participate in the match. |
+ // (Returns a zero length string, with nul termination, |
+ // indistinguishable from a group with a zero length match.) |
+ |
+ UErrorCode status = U_ZERO_ERROR; |
+ URegularExpression *re = uregex_openC("(A)|(B)", 0, NULL, &status); |
+ REGEX_CHECK_STATUS; |
+ UnicodeString text = UNICODE_STRING_SIMPLE("A"); |
+ uregex_setText(re, text.getBuffer(), text.length(), &status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(uregex_lookingAt(re, 0, &status)); |
+ UChar buf[10] = {(UChar)13, (UChar)13, (UChar)13, (UChar)13}; |
+ int32_t length = uregex_group(re, 2, buf+1, UPRV_LENGTHOF(buf)-1, &status); |
+ REGEX_ASSERT(length == 0); |
+ REGEX_ASSERT(buf[0] == 13); |
+ REGEX_ASSERT(buf[1] == 0); |
+ REGEX_ASSERT(buf[2] == 13); |
+ uregex_close(re); |
+ |
+ // UText C++ API, length of match is 0 for non-participating matches. |
+ UText ut = UTEXT_INITIALIZER; |
+ utext_openUnicodeString(&ut, &text, &status); |
+ RegexMatcher matcher(UnicodeString("(A)|(B)"), 0, status); |
+ REGEX_CHECK_STATUS; |
+ matcher.reset(&ut); |
+ REGEX_ASSERT(matcher.lookingAt(0, status)); |
+ |
+ // UText C++ API, Capture group 1 matches "A", position 0, length 1. |
+ int64_t groupLen = -666; |
+ UText group = UTEXT_INITIALIZER; |
+ matcher.group(1, &group, groupLen, status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(groupLen == 1); |
+ REGEX_ASSERT(utext_getNativeIndex(&group) == 0); |
+ // Capture group 2, the (B), does not participate in the match. |
+ matcher.group(2, &group, groupLen, status); |
+ REGEX_CHECK_STATUS; |
+ REGEX_ASSERT(groupLen == 0); |
+ REGEX_ASSERT(matcher.start(2, status) == -1); |
+ REGEX_CHECK_STATUS; |
+} |
+ |
+ |
+#endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */ |