diff options
Diffstat (limited to 'Build/source/libs/icu-xetex/test/intltest/regextst.cpp')
-rw-r--r-- | Build/source/libs/icu-xetex/test/intltest/regextst.cpp | 2127 |
1 files changed, 0 insertions, 2127 deletions
diff --git a/Build/source/libs/icu-xetex/test/intltest/regextst.cpp b/Build/source/libs/icu-xetex/test/intltest/regextst.cpp deleted file mode 100644 index 04b437a4a94..00000000000 --- a/Build/source/libs/icu-xetex/test/intltest/regextst.cpp +++ /dev/null @@ -1,2127 +0,0 @@ -/******************************************************************** - * COPYRIGHT: - * Copyright (c) 2002-2007, International Business Machines Corporation and - * others. All Rights Reserved. - ********************************************************************/ - -// -// regextst.cpp -// -// ICU Regular Expressions test, part of intltest. -// - -#include "intltest.h" -#if !UCONFIG_NO_REGULAR_EXPRESSIONS - -#include "unicode/regex.h" -#include "unicode/uchar.h" -#include "unicode/ucnv.h" -#include "regextst.h" -#include "uvector.h" -#include "util.h" -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - - -//--------------------------------------------------------------------------- -// -// Test class boilerplate -// -//--------------------------------------------------------------------------- -RegexTest::RegexTest() -{ -} - - -RegexTest::~RegexTest() -{ -} - - - -void RegexTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) -{ - if (exec) logln("TestSuite RegexTest: "); - switch (index) { - - case 0: name = "Basic"; - if (exec) Basic(); - break; - case 1: name = "API_Match"; - if (exec) API_Match(); - break; - case 2: name = "API_Replace"; - if (exec) API_Replace(); - break; - case 3: name = "API_Pattern"; - if (exec) API_Pattern(); - break; - case 4: name = "Extended"; - if (exec) Extended(); - break; - case 5: name = "Errors"; - if (exec) Errors(); - break; - case 6: name = "PerlTests"; - if (exec) PerlTests(); - break; - - - default: name = ""; - break; //needed to end loop - } -} - - -//--------------------------------------------------------------------------- -// -// Error Checking / Reporting macros used in all of the tests. -// -//--------------------------------------------------------------------------- -#define REGEX_CHECK_STATUS {if (U_FAILURE(status)) {errln("RegexTest failure at line %d. status=%s\n", \ -__LINE__, u_errorName(status)); return;}} - -#define REGEX_ASSERT(expr) {if ((expr)==FALSE) {errln("RegexTest failure at line %d.\n", __LINE__);};} - -#define REGEX_ASSERT_FAIL(expr, errcode) {UErrorCode status=U_ZERO_ERROR; (expr);\ -if (status!=errcode) {errln("RegexTest failure at line %d. Expected status=%s, got %s\n", \ - __LINE__, u_errorName(errcode), u_errorName(status));};} - -#define REGEX_CHECK_STATUS_L(line) {if (U_FAILURE(status)) {errln( \ - "RegexTest failure at line %d, from %d. status=%d\n",__LINE__, (line), status); }} - -#define REGEX_ASSERT_L(expr, line) {if ((expr)==FALSE) { \ - errln("RegexTest failure at line %d, from %d.", __LINE__, (line)); return;}} - - - -//--------------------------------------------------------------------------- -// -// REGEX_TESTLM Macro + invocation function to simplify writing quick tests -// for the LookingAt() and Match() functions. -// -// usage: -// REGEX_TESTLM("pattern", "input text", lookingAt expected, matches expected); -// -// The expected results are UBool - TRUE or FALSE. -// The input text is unescaped. The pattern is not. -// -// -//--------------------------------------------------------------------------- - -#define REGEX_TESTLM(pat, text, looking, match) doRegexLMTest(pat, text, looking, match, __LINE__); - -UBool RegexTest::doRegexLMTest(const char *pat, const char *text, UBool looking, UBool match, int32_t line) { - const UnicodeString pattern(pat); - const UnicodeString inputText(text); - UErrorCode status = U_ZERO_ERROR; - UParseError pe; - RegexPattern *REPattern = NULL; - RegexMatcher *REMatcher = NULL; - UBool retVal = TRUE; - - UnicodeString patString(pat); - REPattern = RegexPattern::compile(patString, 0, pe, status); - if (U_FAILURE(status)) { - errln("RegexTest failure in RegexPattern::compile() at line %d. Status = %s\n", - line, u_errorName(status)); - return FALSE; - } - if (line==376) { RegexPatternDump(REPattern);} - - UnicodeString inputString(inputText); - UnicodeString unEscapedInput = inputString.unescape(); - REMatcher = REPattern->matcher(unEscapedInput, status); - if (U_FAILURE(status)) { - errln("RegexTest failure in REPattern::matcher() at line %d. Status = %s\n", - line, u_errorName(status)); - return FALSE; - } - - UBool actualmatch; - actualmatch = REMatcher->lookingAt(status); - if (U_FAILURE(status)) { - errln("RegexTest failure in lookingAt() at line %d. Status = %s\n", - line, u_errorName(status)); - retVal = FALSE; - } - if (actualmatch != looking) { - errln("RegexTest: wrong return from lookingAt() at line %d.\n", line); - retVal = FALSE; - } - - status = U_ZERO_ERROR; - actualmatch = REMatcher->matches(status); - if (U_FAILURE(status)) { - errln("RegexTest failure in matches() at line %d. Status = %s\n", - line, u_errorName(status)); - retVal = FALSE; - } - if (actualmatch != match) { - errln("RegexTest: wrong return from matches() at line %d.\n", line); - retVal = FALSE; - } - - if (retVal == FALSE) { - RegexPatternDump(REPattern); - } - - delete REPattern; - delete REMatcher; - return retVal; -} - - - - -//--------------------------------------------------------------------------- -// -// regex_find(pattern, inputString, lineNumber) -// -// function to simplify writing tests regex tests. -// -// The input text is unescaped. The pattern is not. -// The input text is marked with the expected match positions -// <0>text <1> more text </1> </0> -// The <n> </n> tags are removed before trying the match. -// The tags mark the start and end of the match and of any capture groups. -// -// -//--------------------------------------------------------------------------- - - -// Set a value into a UVector at position specified by a decimal number in -// a UnicodeString. This is a utility function needed by the actual test function, -// which follows. -static void set(UVector &vec, int32_t val, UnicodeString index) { - UErrorCode status=U_ZERO_ERROR; - int32_t idx = 0; - for (int32_t i=0; i<index.length(); i++) { - int32_t d=u_charDigitValue(index.charAt(i)); - if (d<0) {return;} - idx = idx*10 + d; - } - while (vec.size()<idx+1) {vec.addElement(-1, status);} - vec.setElementAt(val, idx); -} - -void RegexTest::regex_find(const UnicodeString &pattern, - const UnicodeString &flags, - const UnicodeString &inputString, - int32_t line) { - UnicodeString unEscapedInput; - UnicodeString deTaggedInput; - - UErrorCode status = U_ZERO_ERROR; - UParseError pe; - RegexPattern *parsePat = NULL; - RegexMatcher *parseMatcher = NULL; - RegexPattern *callerPattern = NULL; - RegexMatcher *matcher = NULL; - UVector groupStarts(status); - UVector groupEnds(status); - UBool isMatch = FALSE; - UBool failed = FALSE; - int32_t numFinds; - int32_t i; - - // - // Compile the caller's pattern - // - uint32_t bflags = 0; - if (flags.indexOf((UChar)0x69) >= 0) { // 'i' flag - bflags |= UREGEX_CASE_INSENSITIVE; - } - if (flags.indexOf((UChar)0x78) >= 0) { // 'x' flag - bflags |= UREGEX_COMMENTS; - } - if (flags.indexOf((UChar)0x73) >= 0) { // 's' flag - bflags |= UREGEX_DOTALL; - } - if (flags.indexOf((UChar)0x6d) >= 0) { // 'm' flag - bflags |= UREGEX_MULTILINE; - } - - - callerPattern = RegexPattern::compile(pattern, bflags, pe, status); - if (status != U_ZERO_ERROR) { - #if UCONFIG_NO_BREAK_ITERATION==1 - // 'v' test flag means that the test pattern should not compile if ICU was configured - // to not include break iteration. RBBI is needed for Unicode word boundaries. - if (flags.indexOf((UChar)0x76) >= 0 /*'v'*/ && status == U_UNSUPPORTED_ERROR) { - goto cleanupAndReturn; - } - #endif - errln("Line %d: error %s compiling pattern.", line, u_errorName(status)); - goto cleanupAndReturn; - } - - if (flags.indexOf((UChar)'d') >= 0) { - RegexPatternDump(callerPattern); - } - - // - // Number of times find() should be called on the test string, default to 1 - // - numFinds = 1; - for (i=2; i<=9; i++) { - if (flags.indexOf((UChar)(0x30 + i)) >= 0) { // digit flag - if (numFinds != 1) { - errln("Line %d: more than one digit flag. Scanning %d.", line, i); - goto cleanupAndReturn; - } - numFinds = i; - } - } - - // - // Find the tags in the input data, remove them, and record the group boundary - // positions. - // - parsePat = RegexPattern::compile("<(/?)([0-9]+)>", 0, pe, status); - REGEX_CHECK_STATUS_L(line); - - unEscapedInput = inputString.unescape(); - parseMatcher = parsePat->matcher(unEscapedInput, status); - REGEX_CHECK_STATUS_L(line); - while(parseMatcher->find()) { - parseMatcher->appendReplacement(deTaggedInput, "", status); - REGEX_CHECK_STATUS; - UnicodeString groupNum = parseMatcher->group(2, status); - if (parseMatcher->group(1, status) == "/") { - // close tag - set(groupEnds, deTaggedInput.length(), groupNum); - } else { - set(groupStarts, deTaggedInput.length(), groupNum); - } - } - parseMatcher->appendTail(deTaggedInput); - REGEX_ASSERT_L(groupStarts.size() == groupEnds.size(), line); - - - // - // Do a find on the de-tagged input using the caller's pattern - // - matcher = callerPattern->matcher(deTaggedInput, status); - REGEX_CHECK_STATUS_L(line); - if (flags.indexOf((UChar)'t') >= 0) { - matcher->setTrace(TRUE); - } - - for (i=0; i<numFinds; i++) { - isMatch = matcher->find(); - } - matcher->setTrace(FALSE); - - // - // Match up the groups from the find() with the groups from the tags - // - - // number of tags should match number of groups from find operation. - // matcher->groupCount does not include group 0, the entire match, hence the +1. - // G option in test means that capture group data is not available in the - // expected results, so the check needs to be suppressed. - if (isMatch == FALSE && groupStarts.size() != 0) { - errln("Error at line %d: Match expected, but none found.\n", line); - failed = TRUE; - goto cleanupAndReturn; - } - - if (flags.indexOf((UChar)0x47 /*G*/) >= 0) { - // Only check for match / no match. Don't check capture groups. - if (isMatch && groupStarts.size() == 0) { - errln("Error at line %d: No match expected, but one found.\n", line); - failed = TRUE; - } - goto cleanupAndReturn; - } - - for (i=0; i<=matcher->groupCount(); i++) { - int32_t expectedStart = (i >= groupStarts.size()? -1 : groupStarts.elementAti(i)); - if (matcher->start(i, status) != expectedStart) { - errln("Error at line %d: incorrect start position for group %d. Expected %d, got %d", - line, i, expectedStart, matcher->start(i, status)); - failed = TRUE; - goto cleanupAndReturn; // Good chance of subsequent bogus errors. Stop now. - } - int32_t expectedEnd = (i >= groupEnds.size()? -1 : groupEnds.elementAti(i)); - if (matcher->end(i, status) != expectedEnd) { - errln("Error at line %d: incorrect end position for group %d. Expected %d, got %d", - line, i, expectedEnd, matcher->end(i, status)); - failed = TRUE; - // Error on end position; keep going; real error is probably yet to come as group - // end positions work from end of the input data towards the front. - } - } - if ( matcher->groupCount()+1 < groupStarts.size()) { - errln("Error at line %d: Expected %d capture groups, found %d.", - line, groupStarts.size()-1, matcher->groupCount()); - failed = TRUE; - } - -cleanupAndReturn: - if (failed) { - errln((UnicodeString)"\""+pattern+(UnicodeString)"\" " - +flags+(UnicodeString)" \""+inputString+(UnicodeString)"\""); - // callerPattern->dump(); - } - delete parseMatcher; - delete parsePat; - delete matcher; - delete callerPattern; -} - - - - - - - - -//--------------------------------------------------------------------------- -// -// REGEX_ERR Macro + invocation function to simplify writing tests -// regex tests for incorrect patterns -// -// usage: -// REGEX_ERR("pattern", expected error line, column, expected status); -// -//--------------------------------------------------------------------------- -#define REGEX_ERR(pat, line, col, status) regex_err(pat, line, col, status, __LINE__); - -void RegexTest::regex_err(const char *pat, int32_t errLine, int32_t errCol, - UErrorCode expectedStatus, int32_t line) { - UnicodeString pattern(pat); - - UErrorCode status = U_ZERO_ERROR; - UParseError pe; - RegexPattern *callerPattern = NULL; - - // - // Compile the caller's pattern - // - UnicodeString patString(pat); - callerPattern = RegexPattern::compile(patString, 0, pe, status); - if (status != expectedStatus) { - errln("Line %d: unexpected error %s compiling pattern.", line, u_errorName(status)); - } else { - if (status != U_ZERO_ERROR) { - if (pe.line != errLine || pe.offset != errCol) { - errln("Line %d: incorrect line/offset from UParseError. Expected %d/%d; got %d/%d.\n", - line, errLine, errCol, pe.line, pe.offset); - } - } - } - - delete callerPattern; -} - - - -//--------------------------------------------------------------------------- -// -// Basic Check for basic functionality of regex pattern matching. -// Avoid the use of REGEX_FIND test macro, which has -// substantial dependencies on basic Regex functionality. -// -//--------------------------------------------------------------------------- -void RegexTest::Basic() { - - -// -// Debug - slide failing test cases early -// -#if 0 - { - // REGEX_TESTLM("a\N{LATIN SMALL LETTER B}c", "abc", FALSE, FALSE); - UParseError pe; - UErrorCode status = U_ZERO_ERROR; - RegexPattern::compile("^(?:a?b?)*$", 0, pe, status); - // REGEX_FIND("(?>(abc{2,4}?))(c*)", "<0>ab<1>cc</1><2>ccc</2></0>ddd"); - // REGEX_FIND("(X([abc=X]+)+X)|(y[abc=]+)", "=XX===================="); - } - exit(1); -#endif - - - // - // Pattern with parentheses - // - REGEX_TESTLM("st(abc)ring", "stabcring thing", TRUE, FALSE); - REGEX_TESTLM("st(abc)ring", "stabcring", TRUE, TRUE); - REGEX_TESTLM("st(abc)ring", "stabcrung", FALSE, FALSE); - - // - // Patterns with * - // - REGEX_TESTLM("st(abc)*ring", "string", TRUE, TRUE); - REGEX_TESTLM("st(abc)*ring", "stabcring", TRUE, TRUE); - REGEX_TESTLM("st(abc)*ring", "stabcabcring", TRUE, TRUE); - REGEX_TESTLM("st(abc)*ring", "stabcabcdring", FALSE, FALSE); - REGEX_TESTLM("st(abc)*ring", "stabcabcabcring etc.", TRUE, FALSE); - - REGEX_TESTLM("a*", "", TRUE, TRUE); - REGEX_TESTLM("a*", "b", TRUE, FALSE); - - - // - // Patterns with "." - // - REGEX_TESTLM(".", "abc", TRUE, FALSE); - REGEX_TESTLM("...", "abc", TRUE, TRUE); - REGEX_TESTLM("....", "abc", FALSE, FALSE); - REGEX_TESTLM(".*", "abcxyz123", TRUE, TRUE); - REGEX_TESTLM("ab.*xyz", "abcdefghij", FALSE, FALSE); - REGEX_TESTLM("ab.*xyz", "abcdefg...wxyz", TRUE, TRUE); - REGEX_TESTLM("ab.*xyz", "abcde...wxyz...abc..xyz", TRUE, TRUE); - REGEX_TESTLM("ab.*xyz", "abcde...wxyz...abc..xyz...", TRUE, FALSE); - - // - // Patterns with * applied to chars at end of literal string - // - REGEX_TESTLM("abc*", "ab", TRUE, TRUE); - REGEX_TESTLM("abc*", "abccccc", TRUE, TRUE); - - // - // Supplemental chars match as single chars, not a pair of surrogates. - // - REGEX_TESTLM(".", "\\U00011000", TRUE, TRUE); - REGEX_TESTLM("...", "\\U00011000x\\U00012002", TRUE, TRUE); - REGEX_TESTLM("...", "\\U00011000x\\U00012002y", TRUE, FALSE); - - - // - // UnicodeSets in the pattern - // - REGEX_TESTLM("[1-6]", "1", TRUE, TRUE); - REGEX_TESTLM("[1-6]", "3", TRUE, TRUE); - REGEX_TESTLM("[1-6]", "7", FALSE, FALSE); - REGEX_TESTLM("a[1-6]", "a3", TRUE, TRUE); - REGEX_TESTLM("a[1-6]", "a3", TRUE, TRUE); - REGEX_TESTLM("a[1-6]b", "a3b", TRUE, TRUE); - - REGEX_TESTLM("a[0-9]*b", "a123b", TRUE, TRUE); - REGEX_TESTLM("a[0-9]*b", "abc", TRUE, FALSE); - REGEX_TESTLM("[\\p{Nd}]*", "123456", TRUE, TRUE); - REGEX_TESTLM("[\\p{Nd}]*", "a123456", TRUE, FALSE); // note that * matches 0 occurences. - REGEX_TESTLM("[a][b][[:Zs:]]*", "ab ", TRUE, TRUE); - - // - // OR operator in patterns - // - REGEX_TESTLM("(a|b)", "a", TRUE, TRUE); - REGEX_TESTLM("(a|b)", "b", TRUE, TRUE); - REGEX_TESTLM("(a|b)", "c", FALSE, FALSE); - REGEX_TESTLM("a|b", "b", TRUE, TRUE); - - REGEX_TESTLM("(a|b|c)*", "aabcaaccbcabc", TRUE, TRUE); - REGEX_TESTLM("(a|b|c)*", "aabcaaccbcabdc", TRUE, FALSE); - REGEX_TESTLM("(a(b|c|d)(x|y|z)*|123)", "ac", TRUE, TRUE); - REGEX_TESTLM("(a(b|c|d)(x|y|z)*|123)", "123", TRUE, TRUE); - REGEX_TESTLM("(a|(1|2)*)(b|c|d)(x|y|z)*|123", "123", TRUE, TRUE); - REGEX_TESTLM("(a|(1|2)*)(b|c|d)(x|y|z)*|123", "222211111czzzzw", TRUE, FALSE); - - // - // + - // - REGEX_TESTLM("ab+", "abbc", TRUE, FALSE); - REGEX_TESTLM("ab+c", "ac", FALSE, FALSE); - REGEX_TESTLM("b+", "", FALSE, FALSE); - REGEX_TESTLM("(abc|def)+", "defabc", TRUE, TRUE); - REGEX_TESTLM(".+y", "zippity dooy dah ", TRUE, FALSE); - REGEX_TESTLM(".+y", "zippity dooy", TRUE, TRUE); - - // - // ? - // - REGEX_TESTLM("ab?", "ab", TRUE, TRUE); - REGEX_TESTLM("ab?", "a", TRUE, TRUE); - REGEX_TESTLM("ab?", "ac", TRUE, FALSE); - REGEX_TESTLM("ab?", "abb", TRUE, FALSE); - REGEX_TESTLM("a(b|c)?d", "abd", TRUE, TRUE); - REGEX_TESTLM("a(b|c)?d", "acd", TRUE, TRUE); - REGEX_TESTLM("a(b|c)?d", "ad", TRUE, TRUE); - REGEX_TESTLM("a(b|c)?d", "abcd", FALSE, FALSE); - REGEX_TESTLM("a(b|c)?d", "ab", FALSE, FALSE); - - // - // Escape sequences that become single literal chars, handled internally - // by ICU's Unescape. - // - - // REGEX_TESTLM("\101\142", "Ab", TRUE, TRUE); // Octal TODO: not implemented yet. - REGEX_TESTLM("\\a", "\\u0007", TRUE, TRUE); // BEL - REGEX_TESTLM("\\cL", "\\u000c", TRUE, TRUE); // Control-L - REGEX_TESTLM("\\e", "\\u001b", TRUE, TRUE); // Escape - REGEX_TESTLM("\\f", "\\u000c", TRUE, TRUE); // Form Feed - REGEX_TESTLM("\\n", "\\u000a", TRUE, TRUE); // new line - REGEX_TESTLM("\\r", "\\u000d", TRUE, TRUE); // CR - REGEX_TESTLM("\\t", "\\u0009", TRUE, TRUE); // Tab - REGEX_TESTLM("\\u1234", "\\u1234", TRUE, TRUE); - REGEX_TESTLM("\\U00001234", "\\u1234", TRUE, TRUE); - - REGEX_TESTLM(".*\\Ax", "xyz", TRUE, FALSE); // \A matches only at the beginning of input - REGEX_TESTLM(".*\\Ax", " xyz", FALSE, FALSE); // \A matches only at the beginning of input - - // Escape of special chars in patterns - REGEX_TESTLM("\\\\\\|\\(\\)\\[\\{\\~\\$\\*\\+\\?\\.", "\\\\|()[{~$*+?.", TRUE, TRUE); - - -} - - -//--------------------------------------------------------------------------- -// -// API_Match Test that the API for class RegexMatcher -// is present and nominally working, but excluding functions -// implementing replace operations. -// -//--------------------------------------------------------------------------- -void RegexTest::API_Match() { - UParseError pe; - UErrorCode status=U_ZERO_ERROR; - int32_t flags = 0; - - // - // Debug - slide failing test cases early - // -#if 0 - { - } - return; -#endif - - // - // Simple pattern compilation - // - { - UnicodeString re("abc"); - RegexPattern *pat2; - pat2 = RegexPattern::compile(re, flags, pe, status); - REGEX_CHECK_STATUS; - - UnicodeString inStr1 = "abcdef this is a test"; - UnicodeString instr2 = "not abc"; - UnicodeString empty = ""; - - - // - // Matcher creation and reset. - // - RegexMatcher *m1 = pat2->matcher(inStr1, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(m1->lookingAt(status) == TRUE); - REGEX_ASSERT(m1->input() == inStr1); - m1->reset(instr2); - REGEX_ASSERT(m1->lookingAt(status) == FALSE); - REGEX_ASSERT(m1->input() == instr2); - m1->reset(inStr1); - REGEX_ASSERT(m1->input() == inStr1); - REGEX_ASSERT(m1->lookingAt(status) == TRUE); - m1->reset(empty); - REGEX_ASSERT(m1->lookingAt(status) == FALSE); - REGEX_ASSERT(m1->input() == empty); - REGEX_ASSERT(&m1->pattern() == pat2); - - // - // reset(pos, status) - // - m1->reset(inStr1); - m1->reset(4, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(m1->input() == inStr1); - REGEX_ASSERT(m1->lookingAt(status) == TRUE); - - m1->reset(-1, status); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - status = U_ZERO_ERROR; - - m1->reset(0, status); - REGEX_CHECK_STATUS; - status = U_ZERO_ERROR; - - int32_t len = m1->input().length(); - m1->reset(len-1, status); - REGEX_CHECK_STATUS; - status = U_ZERO_ERROR; - - m1->reset(len, status); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - status = U_ZERO_ERROR; - - // - // match(pos, status) - // - m1->reset(instr2); - REGEX_ASSERT(m1->matches(4, status) == TRUE); - m1->reset(); - REGEX_ASSERT(m1->matches(3, status) == FALSE); - m1->reset(); - REGEX_ASSERT(m1->matches(5, status) == FALSE); - REGEX_ASSERT(m1->matches(4, status) == TRUE); - REGEX_ASSERT(m1->matches(-1, status) == FALSE); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - - // Match() at end of string should fail, but should not - // be an error. - status = U_ZERO_ERROR; - len = m1->input().length(); - REGEX_ASSERT(m1->matches(len, status) == FALSE); - REGEX_CHECK_STATUS; - - // Match beyond end of string should fail with an error. - status = U_ZERO_ERROR; - REGEX_ASSERT(m1->matches(len+1, status) == FALSE); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - - // Successful match at end of string. - { - status = U_ZERO_ERROR; - RegexMatcher m("A?", 0, status); // will match zero length string. - REGEX_CHECK_STATUS; - m.reset(inStr1); - len = inStr1.length(); - REGEX_ASSERT(m.matches(len, status) == TRUE); - REGEX_CHECK_STATUS; - m.reset(empty); - REGEX_ASSERT(m.matches(0, status) == TRUE); - REGEX_CHECK_STATUS; - } - - - // - // lookingAt(pos, status) - // - status = U_ZERO_ERROR; - m1->reset(instr2); // "not abc" - REGEX_ASSERT(m1->lookingAt(4, status) == TRUE); - REGEX_ASSERT(m1->lookingAt(5, status) == FALSE); - REGEX_ASSERT(m1->lookingAt(3, status) == FALSE); - REGEX_ASSERT(m1->lookingAt(4, status) == TRUE); - REGEX_ASSERT(m1->lookingAt(-1, status) == FALSE); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - status = U_ZERO_ERROR; - len = m1->input().length(); - REGEX_ASSERT(m1->lookingAt(len, status) == FALSE); - REGEX_CHECK_STATUS; - REGEX_ASSERT(m1->lookingAt(len+1, status) == FALSE); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - - delete m1; - delete pat2; - } - - - // - // Capture Group. - // RegexMatcher::start(); - // RegexMatcher::end(); - // RegexMatcher::groupCount(); - // - { - int32_t flags=0; - UParseError pe; - UErrorCode status=U_ZERO_ERROR; - - UnicodeString re("01(23(45)67)(.*)"); - RegexPattern *pat = RegexPattern::compile(re, flags, pe, status); - REGEX_CHECK_STATUS; - UnicodeString data = "0123456789"; - - RegexMatcher *matcher = pat->matcher(data, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(matcher->lookingAt(status) == TRUE); - static const int32_t matchStarts[] = {0, 2, 4, 8}; - static const int32_t matchEnds[] = {10, 8, 6, 10}; - int32_t i; - for (i=0; i<4; i++) { - int32_t actualStart = matcher->start(i, status); - REGEX_CHECK_STATUS; - if (actualStart != matchStarts[i]) { - errln("RegexTest failure at line %d, index %d. Expected %d, got %d\n", - __LINE__, i, matchStarts[i], actualStart); - } - int32_t actualEnd = matcher->end(i, status); - REGEX_CHECK_STATUS; - if (actualEnd != matchEnds[i]) { - errln("RegexTest failure at line %d index %d. Expected %d, got %d\n", - __LINE__, i, matchEnds[i], actualEnd); - } - } - - REGEX_ASSERT(matcher->start(0, status) == matcher->start(status)); - REGEX_ASSERT(matcher->end(0, status) == matcher->end(status)); - - REGEX_ASSERT_FAIL(matcher->start(-1, status), U_INDEX_OUTOFBOUNDS_ERROR); - REGEX_ASSERT_FAIL(matcher->start( 4, status), U_INDEX_OUTOFBOUNDS_ERROR); - matcher->reset(); - REGEX_ASSERT_FAIL(matcher->start( 0, status), U_REGEX_INVALID_STATE); - - matcher->lookingAt(status); - REGEX_ASSERT(matcher->group(status) == "0123456789"); - REGEX_ASSERT(matcher->group(0, status) == "0123456789"); - REGEX_ASSERT(matcher->group(1, status) == "234567" ); - REGEX_ASSERT(matcher->group(2, status) == "45" ); - REGEX_ASSERT(matcher->group(3, status) == "89" ); - REGEX_CHECK_STATUS; - REGEX_ASSERT_FAIL(matcher->group(-1, status), U_INDEX_OUTOFBOUNDS_ERROR); - REGEX_ASSERT_FAIL(matcher->group( 4, status), U_INDEX_OUTOFBOUNDS_ERROR); - matcher->reset(); - REGEX_ASSERT_FAIL(matcher->group( 0, status), U_REGEX_INVALID_STATE); - - delete matcher; - delete pat; - - } - - // - // find - // - { - int32_t flags=0; - UParseError pe; - UErrorCode status=U_ZERO_ERROR; - - UnicodeString re("abc"); - RegexPattern *pat = RegexPattern::compile(re, flags, pe, status); - REGEX_CHECK_STATUS; - UnicodeString data = ".abc..abc...abc.."; - // 012345678901234567 - - RegexMatcher *matcher = pat->matcher(data, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(matcher->find()); - REGEX_ASSERT(matcher->start(status) == 1); - REGEX_ASSERT(matcher->find()); - REGEX_ASSERT(matcher->start(status) == 6); - REGEX_ASSERT(matcher->find()); - REGEX_ASSERT(matcher->start(status) == 12); - REGEX_ASSERT(matcher->find() == FALSE); - REGEX_ASSERT(matcher->find() == FALSE); - - matcher->reset(); - REGEX_ASSERT(matcher->find()); - REGEX_ASSERT(matcher->start(status) == 1); - - REGEX_ASSERT(matcher->find(0, status)); - REGEX_ASSERT(matcher->start(status) == 1); - REGEX_ASSERT(matcher->find(1, status)); - REGEX_ASSERT(matcher->start(status) == 1); - REGEX_ASSERT(matcher->find(2, status)); - REGEX_ASSERT(matcher->start(status) == 6); - REGEX_ASSERT(matcher->find(12, status)); - REGEX_ASSERT(matcher->start(status) == 12); - REGEX_ASSERT(matcher->find(13, status) == FALSE); - REGEX_ASSERT(matcher->find(16, status) == FALSE); - REGEX_ASSERT(matcher->find(17, status) == FALSE); - REGEX_ASSERT_FAIL(matcher->start(status), U_REGEX_INVALID_STATE); - - status = U_ZERO_ERROR; - REGEX_ASSERT_FAIL(matcher->find(-1, status), U_INDEX_OUTOFBOUNDS_ERROR); - status = U_ZERO_ERROR; - REGEX_ASSERT_FAIL(matcher->find(18, status), U_INDEX_OUTOFBOUNDS_ERROR); - - REGEX_ASSERT(matcher->groupCount() == 0); - - delete matcher; - delete pat; - } - - - // - // find, with \G in pattern (true if at the end of a previous match). - // - { - int32_t flags=0; - UParseError pe; - UErrorCode status=U_ZERO_ERROR; - - UnicodeString re(".*?(?:(\\Gabc)|(abc))"); - RegexPattern *pat = RegexPattern::compile(re, flags, pe, status); - REGEX_CHECK_STATUS; - UnicodeString data = ".abcabc.abc.."; - // 012345678901234567 - - RegexMatcher *matcher = pat->matcher(data, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(matcher->find()); - REGEX_ASSERT(matcher->start(status) == 0); - REGEX_ASSERT(matcher->start(1, status) == -1); - REGEX_ASSERT(matcher->start(2, status) == 1); - - REGEX_ASSERT(matcher->find()); - REGEX_ASSERT(matcher->start(status) == 4); - REGEX_ASSERT(matcher->start(1, status) == 4); - REGEX_ASSERT(matcher->start(2, status) == -1); - REGEX_CHECK_STATUS; - - delete matcher; - delete pat; - } - - // - // find with zero length matches, match position should bump ahead - // to prevent loops. - // - { - int32_t i; - UErrorCode status=U_ZERO_ERROR; - RegexMatcher m("(?= ?)", 0, status); // This pattern will zero-length matches anywhere, - // using an always-true look-ahead. - REGEX_CHECK_STATUS; - UnicodeString s(" "); - m.reset(s); - for (i=0; ; i++) { - if (m.find() == FALSE) { - break; - } - REGEX_ASSERT(m.start(status) == i); - REGEX_ASSERT(m.end(status) == i); - } - REGEX_ASSERT(i==5); - - // Check that the bump goes over surrogate pairs OK - s = "\\U00010001\\U00010002\\U00010003\\U00010004"; - s = s.unescape(); - m.reset(s); - for (i=0; ; i+=2) { - if (m.find() == FALSE) { - break; - } - REGEX_ASSERT(m.start(status) == i); - REGEX_ASSERT(m.end(status) == i); - } - REGEX_ASSERT(i==10); - } - { - // find() loop breaking test. - // with pattern of /.?/, should see a series of one char matches, then a single - // match of zero length at the end of the input string. - int32_t i; - UErrorCode status=U_ZERO_ERROR; - RegexMatcher m(".?", 0, status); - REGEX_CHECK_STATUS; - UnicodeString s(" "); - m.reset(s); - for (i=0; ; i++) { - if (m.find() == FALSE) { - break; - } - REGEX_ASSERT(m.start(status) == i); - REGEX_ASSERT(m.end(status) == (i<4 ? i+1 : i)); - } - REGEX_ASSERT(i==5); - } - - - // - // Matchers with no input string behave as if they had an empty input string. - // - - { - UErrorCode status = U_ZERO_ERROR; - RegexMatcher m(".?", 0, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(m.find()); - REGEX_ASSERT(m.start(status) == 0); - REGEX_ASSERT(m.input() == ""); - } - { - UErrorCode status = U_ZERO_ERROR; - RegexPattern *p = RegexPattern::compile(".", 0, status); - RegexMatcher *m = p->matcher(status); - REGEX_CHECK_STATUS; - - REGEX_ASSERT(m->find() == FALSE); - REGEX_ASSERT(m->input() == ""); - delete m; - delete p; - } - - // - // Compilation error on reset with UChar * - // These were a hazard that people were stumbling over with runtime errors. - // Changed them to compiler errors by adding private methods that more closely - // matched the incorrect use of the functions. - // -#if 0 - { - UErrorCode status = U_ZERO_ERROR; - UChar ucharString[20]; - RegexMatcher m(".", 0, status); - m.reset(ucharString); // should not compile. - - RegexPattern *p = RegexPattern::compile(".", 0, status); - RegexMatcher *m2 = p->matcher(ucharString, status); // should not compile. - - RegexMatcher m3(".", ucharString, 0, status); // Should not compile - } -#endif - -} - - - - - - -//--------------------------------------------------------------------------- -// -// API_Replace API test for class RegexMatcher, testing the -// Replace family of functions. -// -//--------------------------------------------------------------------------- -void RegexTest::API_Replace() { - // - // Replace - // - int32_t flags=0; - UParseError pe; - UErrorCode status=U_ZERO_ERROR; - - UnicodeString re("abc"); - RegexPattern *pat = RegexPattern::compile(re, flags, pe, status); - REGEX_CHECK_STATUS; - UnicodeString data = ".abc..abc...abc.."; - // 012345678901234567 - RegexMatcher *matcher = pat->matcher(data, status); - - // - // Plain vanilla matches. - // - UnicodeString dest; - dest = matcher->replaceFirst("yz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == ".yz..abc...abc.."); - - dest = matcher->replaceAll("yz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == ".yz..yz...yz.."); - - // - // Plain vanilla non-matches. - // - UnicodeString d2 = ".abx..abx...abx.."; - matcher->reset(d2); - dest = matcher->replaceFirst("yz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == ".abx..abx...abx.."); - - dest = matcher->replaceAll("yz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == ".abx..abx...abx.."); - - // - // Empty source string - // - UnicodeString d3 = ""; - matcher->reset(d3); - dest = matcher->replaceFirst("yz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == ""); - - dest = matcher->replaceAll("yz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == ""); - - // - // Empty substitution string - // - matcher->reset(data); // ".abc..abc...abc.." - dest = matcher->replaceFirst("", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == "...abc...abc.."); - - dest = matcher->replaceAll("", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == "........"); - - // - // match whole string - // - UnicodeString d4 = "abc"; - matcher->reset(d4); - dest = matcher->replaceFirst("xyz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == "xyz"); - - dest = matcher->replaceAll("xyz", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == "xyz"); - - // - // Capture Group, simple case - // - UnicodeString re2("a(..)"); - RegexPattern *pat2 = RegexPattern::compile(re2, flags, pe, status); - REGEX_CHECK_STATUS; - UnicodeString d5 = "abcdefg"; - RegexMatcher *matcher2 = pat2->matcher(d5, status); - REGEX_CHECK_STATUS; - dest = matcher2->replaceFirst("$1$1", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == "bcbcdefg"); - - dest = matcher2->replaceFirst("The value of \\$1 is $1.", status); - REGEX_CHECK_STATUS; - 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"); - - UnicodeString replacement = "Supplemental Digit 1 $\\U0001D7CF."; - replacement = replacement.unescape(); - dest = matcher2->replaceFirst(replacement, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(dest == "Supplemental Digit 1 bc.defg"); - - REGEX_ASSERT_FAIL(matcher2->replaceFirst("bad capture group number $5...",status), U_INDEX_OUTOFBOUNDS_ERROR); - - - // - // Replacement String with \u hex escapes - // - { - UnicodeString src = "abc 1 abc 2 abc 3"; - UnicodeString substitute = "--\\u0043--"; - matcher->reset(src); - UnicodeString result = matcher->replaceAll(substitute, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(result == "--C-- 1 --C-- 2 --C-- 3"); - } - { - UnicodeString src = "abc !"; - UnicodeString substitute = "--\\U00010000--"; - matcher->reset(src); - UnicodeString result = matcher->replaceAll(substitute, status); - REGEX_CHECK_STATUS; - UnicodeString expected = UnicodeString("--"); - expected.append((UChar32)0x10000); - expected.append("-- !"); - REGEX_ASSERT(result == expected); - } - // TODO: need more through testing of capture substitutions. - - // Bug 4057 - // - { - status = U_ZERO_ERROR; - UnicodeString s = "The matches start with ss and end with ee ss stuff ee fin"; - RegexMatcher m("ss(.*?)ee", 0, status); - REGEX_CHECK_STATUS; - UnicodeString result; - - // Multiple finds do NOT bump up the previous appendReplacement postion. - m.reset(s); - m.find(); - m.find(); - m.appendReplacement(result, "ooh", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(result == "The matches start with ss and end with ee ooh"); - - // After a reset into the interior of a string, appendReplacemnt still starts at beginning. - status = U_ZERO_ERROR; - result.truncate(0); - m.reset(10, status); - m.find(); - m.find(); - m.appendReplacement(result, "ooh", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(result == "The matches start with ss and end with ee ooh"); - - // find() at interior of string, appendReplacemnt still starts at beginning. - status = U_ZERO_ERROR; - result.truncate(0); - m.reset(); - m.find(10, status); - m.find(); - m.appendReplacement(result, "ooh", status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(result == "The matches start with ss and end with ee ooh"); - - m.appendTail(result); - REGEX_ASSERT(result == "The matches start with ss and end with ee ooh fin"); - - } - - delete matcher2; - delete pat2; - delete matcher; - delete pat; -} - - -//--------------------------------------------------------------------------- -// -// API_Pattern Test that the API for class RegexPattern is -// present and nominally working. -// -//--------------------------------------------------------------------------- -void RegexTest::API_Pattern() { - RegexPattern pata; // Test default constructor to not crash. - RegexPattern patb; - - REGEX_ASSERT(pata == patb); - REGEX_ASSERT(pata == pata); - - UnicodeString re1("abc[a-l][m-z]"); - UnicodeString re2("def"); - UErrorCode status = U_ZERO_ERROR; - UParseError pe; - - RegexPattern *pat1 = RegexPattern::compile(re1, 0, pe, status); - RegexPattern *pat2 = RegexPattern::compile(re2, 0, pe, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(*pat1 == *pat1); - REGEX_ASSERT(*pat1 != pata); - - // Assign - patb = *pat1; - REGEX_ASSERT(patb == *pat1); - - // Copy Construct - RegexPattern patc(*pat1); - REGEX_ASSERT(patc == *pat1); - REGEX_ASSERT(patb == patc); - REGEX_ASSERT(pat1 != pat2); - patb = *pat2; - REGEX_ASSERT(patb != patc); - REGEX_ASSERT(patb == *pat2); - - // Compile with no flags. - RegexPattern *pat1a = RegexPattern::compile(re1, pe, status); - REGEX_ASSERT(*pat1a == *pat1); - - REGEX_ASSERT(pat1a->flags() == 0); - - // Compile with different flags should be not equal - RegexPattern *pat1b = RegexPattern::compile(re1, UREGEX_CASE_INSENSITIVE, pe, status); - REGEX_CHECK_STATUS; - - REGEX_ASSERT(*pat1b != *pat1a); - REGEX_ASSERT(pat1b->flags() == UREGEX_CASE_INSENSITIVE); - REGEX_ASSERT(pat1a->flags() == 0); - delete pat1b; - - // clone - RegexPattern *pat1c = pat1->clone(); - REGEX_ASSERT(*pat1c == *pat1); - REGEX_ASSERT(*pat1c != *pat2); - - delete pat1c; - delete pat1a; - delete pat1; - delete pat2; - - - // - // Verify that a matcher created from a cloned pattern works. - // (Jitterbug 3423) - // - { - UErrorCode status = U_ZERO_ERROR; - RegexPattern *pSource = RegexPattern::compile("\\p{L}+", 0, status); - RegexPattern *pClone = pSource->clone(); - delete pSource; - RegexMatcher *mFromClone = pClone->matcher(status); - REGEX_CHECK_STATUS; - UnicodeString s = "Hello World"; - mFromClone->reset(s); - REGEX_ASSERT(mFromClone->find() == TRUE); - REGEX_ASSERT(mFromClone->group(status) == "Hello"); - REGEX_ASSERT(mFromClone->find() == TRUE); - REGEX_ASSERT(mFromClone->group(status) == "World"); - REGEX_ASSERT(mFromClone->find() == FALSE); - delete mFromClone; - delete pClone; - } - - // - // matches convenience API - // - REGEX_ASSERT(RegexPattern::matches(".*", "random input", pe, status) == TRUE); - REGEX_CHECK_STATUS; - REGEX_ASSERT(RegexPattern::matches("abc", "random input", pe, status) == FALSE); - REGEX_CHECK_STATUS; - REGEX_ASSERT(RegexPattern::matches(".*nput", "random input", pe, status) == TRUE); - REGEX_CHECK_STATUS; - REGEX_ASSERT(RegexPattern::matches("random input", "random input", pe, status) == TRUE); - REGEX_CHECK_STATUS; - REGEX_ASSERT(RegexPattern::matches(".*u", "random input", pe, status) == FALSE); - REGEX_CHECK_STATUS; - status = U_INDEX_OUTOFBOUNDS_ERROR; - REGEX_ASSERT(RegexPattern::matches("abc", "abc", pe, status) == FALSE); - REGEX_ASSERT(status == U_INDEX_OUTOFBOUNDS_ERROR); - - - // - // Split() - // - status = U_ZERO_ERROR; - pat1 = RegexPattern::compile(" +", pe, status); - REGEX_CHECK_STATUS; - UnicodeString fields[10]; - - int32_t n; - n = pat1->split("Now is the time", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==4); - REGEX_ASSERT(fields[0]=="Now"); - REGEX_ASSERT(fields[1]=="is"); - REGEX_ASSERT(fields[2]=="the"); - REGEX_ASSERT(fields[3]=="time"); - REGEX_ASSERT(fields[4]==""); - - n = pat1->split("Now is the time", fields, 2, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==2); - REGEX_ASSERT(fields[0]=="Now"); - REGEX_ASSERT(fields[1]=="is the time"); - REGEX_ASSERT(fields[2]=="the"); // left over from previous test - - fields[1] = "*"; - status = U_ZERO_ERROR; - n = pat1->split("Now is the time", fields, 1, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==1); - REGEX_ASSERT(fields[0]=="Now is the time"); - REGEX_ASSERT(fields[1]=="*"); - status = U_ZERO_ERROR; - - n = pat1->split(" Now is the time ", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==5); - REGEX_ASSERT(fields[0]==""); - REGEX_ASSERT(fields[1]=="Now"); - REGEX_ASSERT(fields[2]=="is"); - REGEX_ASSERT(fields[3]=="the"); - REGEX_ASSERT(fields[4]=="time"); - REGEX_ASSERT(fields[5]==""); - - n = pat1->split(" ", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==1); - REGEX_ASSERT(fields[0]==""); - - fields[0] = "foo"; - n = pat1->split("", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==0); - REGEX_ASSERT(fields[0]=="foo"); - - delete pat1; - - // split, with a pattern with (capture) - pat1 = RegexPattern::compile("<(\\w*)>", pe, status); - REGEX_CHECK_STATUS; - - status = U_ZERO_ERROR; - n = pat1->split("<a>Now is <b>the time<c>", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==6); - REGEX_ASSERT(fields[0]==""); - REGEX_ASSERT(fields[1]=="a"); - REGEX_ASSERT(fields[2]=="Now is "); - REGEX_ASSERT(fields[3]=="b"); - REGEX_ASSERT(fields[4]=="the time"); - REGEX_ASSERT(fields[5]=="c"); - REGEX_ASSERT(fields[6]==""); - REGEX_ASSERT(status==U_ZERO_ERROR); - - n = pat1->split(" <a>Now is <b>the time<c>", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==6); - REGEX_ASSERT(fields[0]==" "); - REGEX_ASSERT(fields[1]=="a"); - REGEX_ASSERT(fields[2]=="Now is "); - REGEX_ASSERT(fields[3]=="b"); - REGEX_ASSERT(fields[4]=="the time"); - REGEX_ASSERT(fields[5]=="c"); - REGEX_ASSERT(fields[6]==""); - - status = U_ZERO_ERROR; - fields[6] = "foo"; - n = pat1->split(" <a>Now is <b>the time<c>", fields, 6, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==6); - REGEX_ASSERT(fields[0]==" "); - REGEX_ASSERT(fields[1]=="a"); - REGEX_ASSERT(fields[2]=="Now is "); - REGEX_ASSERT(fields[3]=="b"); - REGEX_ASSERT(fields[4]=="the time"); - REGEX_ASSERT(fields[5]=="c"); - REGEX_ASSERT(fields[6]=="foo"); - - status = U_ZERO_ERROR; - fields[5] = "foo"; - n = pat1->split(" <a>Now is <b>the time<c>", fields, 5, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==5); - REGEX_ASSERT(fields[0]==" "); - REGEX_ASSERT(fields[1]=="a"); - REGEX_ASSERT(fields[2]=="Now is "); - REGEX_ASSERT(fields[3]=="b"); - REGEX_ASSERT(fields[4]=="the time<c>"); - REGEX_ASSERT(fields[5]=="foo"); - - status = U_ZERO_ERROR; - fields[5] = "foo"; - n = pat1->split(" <a>Now is <b>the time", fields, 5, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==5); - REGEX_ASSERT(fields[0]==" "); - REGEX_ASSERT(fields[1]=="a"); - REGEX_ASSERT(fields[2]=="Now is "); - REGEX_ASSERT(fields[3]=="b"); - REGEX_ASSERT(fields[4]=="the time"); - REGEX_ASSERT(fields[5]=="foo"); - - status = U_ZERO_ERROR; - n = pat1->split(" <a>Now is <b>the time<c>", fields, 4, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==4); - REGEX_ASSERT(fields[0]==" "); - REGEX_ASSERT(fields[1]=="a"); - REGEX_ASSERT(fields[2]=="Now is "); - REGEX_ASSERT(fields[3]=="the time<c>"); - status = U_ZERO_ERROR; - delete pat1; - - pat1 = RegexPattern::compile("([-,])", pe, status); - REGEX_CHECK_STATUS; - n = pat1->split("1-10,20", fields, 10, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(n==5); - REGEX_ASSERT(fields[0]=="1"); - REGEX_ASSERT(fields[1]=="-"); - REGEX_ASSERT(fields[2]=="10"); - REGEX_ASSERT(fields[3]==","); - REGEX_ASSERT(fields[4]=="20"); - delete pat1; - - - // - // RegexPattern::pattern() - // - pat1 = new RegexPattern(); - REGEX_ASSERT(pat1->pattern() == ""); - delete pat1; - - pat1 = RegexPattern::compile("(Hello, world)*", pe, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(pat1->pattern() == "(Hello, world)*"); - delete pat1; - - - // - // classID functions - // - pat1 = RegexPattern::compile("(Hello, world)*", pe, status); - REGEX_CHECK_STATUS; - REGEX_ASSERT(pat1->getDynamicClassID() == RegexPattern::getStaticClassID()); - REGEX_ASSERT(pat1->getDynamicClassID() != NULL); - UnicodeString Hello("Hello, world."); - RegexMatcher *m = pat1->matcher(Hello, status); - REGEX_ASSERT(pat1->getDynamicClassID() != m->getDynamicClassID()); - REGEX_ASSERT(m->getDynamicClassID() == RegexMatcher::getStaticClassID()); - REGEX_ASSERT(m->getDynamicClassID() != NULL); - delete m; - delete pat1; - -} - -//--------------------------------------------------------------------------- -// -// Extended A more thorough check for features of regex patterns -// The test cases are in a separate data file, -// source/tests/testdata/regextst.txt -// A description of the test data format is included in that file. -// -//--------------------------------------------------------------------------- - -const char * -RegexTest::getPath(char buffer[2048], const char *filename) { - UErrorCode status=U_ZERO_ERROR; - const char *testDataDirectory = IntlTest::getSourceTestData(status); - if (U_FAILURE(status)) { - errln("ERROR: loadTestData() failed - %s", u_errorName(status)); - return NULL; - } - - strcpy(buffer, testDataDirectory); - strcat(buffer, filename); - return buffer; -} - -void RegexTest::Extended() { - char tdd[2048]; - const char *srcPath; - UErrorCode status = U_ZERO_ERROR; - int32_t lineNum = 0; - - // - // Open and read the test data file. - // - srcPath=getPath(tdd, "regextst.txt"); - if(srcPath==NULL) { - return; /* something went wrong, error already output */ - } - - int32_t len; - UChar *testData = ReadAndConvertFile(srcPath, len, status); - if (U_FAILURE(status)) { - return; /* something went wrong, error already output */ - } - - // - // Put the test data into a UnicodeString - // - UnicodeString testString(FALSE, testData, len); - - RegexMatcher quotedStuffMat("\\s*([\\'\\\"/])(.*?)\\1", 0, status); - RegexMatcher commentMat ("\\s*(#.*)?$", 0, status); - RegexMatcher flagsMat ("\\s*([ixsmdtGv2-9]*)([:letter:]*)", 0, status); - - RegexMatcher lineMat("(.*?)\\r?\\n", testString, 0, status); - UnicodeString testPattern; // The pattern for test from the test file. - UnicodeString testFlags; // the flags for a test. - UnicodeString matchString; // The marked up string to be used as input - - if (U_FAILURE(status)){ - dataerrln("Construct RegexMatcher() error."); - delete [] testData; - return; - } - - // - // Loop over the test data file, once per line. - // - while (lineMat.find()) { - lineNum++; - if (U_FAILURE(status)) { - errln("line %d: ICU Error \"%s\"", lineNum, u_errorName(status)); - } - - status = U_ZERO_ERROR; - UnicodeString testLine = lineMat.group(1, status); - if (testLine.length() == 0) { - continue; - } - - // - // Parse the test line. Skip blank and comment only lines. - // Separate out the three main fields - pattern, flags, target. - // - - commentMat.reset(testLine); - if (commentMat.lookingAt(status)) { - // This line is a comment, or blank. - continue; - } - - // - // Pull out the pattern field, remove it from the test file line. - // - quotedStuffMat.reset(testLine); - if (quotedStuffMat.lookingAt(status)) { - testPattern = quotedStuffMat.group(2, status); - testLine.remove(0, quotedStuffMat.end(0, status)); - } else { - errln("Bad pattern (missing quotes?) at test file line %d", lineNum); - continue; - } - - - // - // Pull out the flags from the test file line. - // - flagsMat.reset(testLine); - flagsMat.lookingAt(status); // Will always match, possibly an empty string. - testFlags = flagsMat.group(1, status); - if (flagsMat.group(2, status).length() > 0) { - errln("Bad Match flag at line %d. Scanning %c\n", - lineNum, flagsMat.group(2, status).charAt(0)); - continue; - } - testLine.remove(0, flagsMat.end(0, status)); - - // - // Pull out the match string, as a whole. - // We'll process the <tags> later. - // - quotedStuffMat.reset(testLine); - if (quotedStuffMat.lookingAt(status)) { - matchString = quotedStuffMat.group(2, status); - testLine.remove(0, quotedStuffMat.end(0, status)); - } else { - errln("Bad match string at test file line %d", lineNum); - continue; - } - - // - // The only thing left from the input line should be an optional trailing comment. - // - commentMat.reset(testLine); - if (commentMat.lookingAt(status) == FALSE) { - errln("Line %d: unexpected characters at end of test line.", lineNum); - continue; - } - - // - // Run the test - // - regex_find(testPattern, testFlags, matchString, lineNum); - } - - delete [] testData; - -} - - - -//--------------------------------------------------------------------------- -// -// Errors Check for error handling in patterns. -// -//--------------------------------------------------------------------------- -void RegexTest::Errors() { - // \escape sequences that aren't implemented yet. - //REGEX_ERR("hex format \\x{abcd} not implemented", 1, 13, U_REGEX_UNIMPLEMENTED); - - // Missing close parentheses - REGEX_ERR("Comment (?# with no close", 1, 25, U_REGEX_MISMATCHED_PAREN); - REGEX_ERR("Capturing Parenthesis(...", 1, 25, U_REGEX_MISMATCHED_PAREN); - REGEX_ERR("Grouping only parens (?: blah blah", 1, 34, U_REGEX_MISMATCHED_PAREN); - - // Extra close paren - REGEX_ERR("Grouping only parens (?: blah)) blah", 1, 31, U_REGEX_MISMATCHED_PAREN); - REGEX_ERR(")))))))", 1, 1, U_REGEX_MISMATCHED_PAREN); - REGEX_ERR("(((((((", 1, 7, U_REGEX_MISMATCHED_PAREN); - - // Look-ahead, Look-behind - // TODO: add tests for unbounded length look-behinds. - REGEX_ERR("abc(?<@xyz).*", 1, 7, U_REGEX_RULE_SYNTAX); // illegal construct - - // Attempt to use non-default flags - { - UParseError pe; - UErrorCode status = U_ZERO_ERROR; - int32_t flags = UREGEX_CANON_EQ | - UREGEX_COMMENTS | UREGEX_DOTALL | - UREGEX_MULTILINE; - RegexPattern *pat1= RegexPattern::compile(".*", flags, pe, status); - REGEX_ASSERT(status == U_REGEX_UNIMPLEMENTED); - delete pat1; - } - - - // Quantifiers are allowed only after something that can be quantified. - REGEX_ERR("+", 1, 1, U_REGEX_RULE_SYNTAX); - REGEX_ERR("abc\ndef(*2)", 2, 5, U_REGEX_RULE_SYNTAX); - REGEX_ERR("abc**", 1, 5, U_REGEX_RULE_SYNTAX); - - // Mal-formed {min,max} quantifiers - REGEX_ERR("abc{a,2}",1,5, U_REGEX_BAD_INTERVAL); - REGEX_ERR("abc{4,2}",1,8, U_REGEX_MAX_LT_MIN); - REGEX_ERR("abc{1,b}",1,7, U_REGEX_BAD_INTERVAL); - 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,687865858}", 1, 16, U_REGEX_NUMBER_TOO_BIG); // Overflows regex binary format - REGEX_ERR("abc{687865858,687865859}", 1, 24, U_REGEX_NUMBER_TOO_BIG); - - - // UnicodeSet containing a string - REGEX_ERR("abc[{def}]xyz", 1, 10, U_REGEX_SET_CONTAINS_STRING); - - // Ticket 5389 - REGEX_ERR("*c", 1, 1, U_REGEX_RULE_SYNTAX); - -} - - -//------------------------------------------------------------------------------- -// -// Read a text data file, convert it to UChars, and return the data -// in one big UChar * buffer, which the caller must delete. -// -//-------------------------------------------------------------------------------- -UChar *RegexTest::ReadAndConvertFile(const char *fileName, int32_t &ulen, UErrorCode &status) { - UChar *retPtr = NULL; - char *fileBuf = NULL; - UConverter* conv = NULL; - FILE *f = NULL; - - ulen = 0; - if (U_FAILURE(status)) { - return retPtr; - } - - // - // Open the file. - // - f = fopen(fileName, "rb"); - if (f == 0) { - errln("Error opening test data file %s\n", fileName); - status = U_FILE_ACCESS_ERROR; - return NULL; - } - // - // Read it in - // - int32_t fileSize; - int32_t amt_read; - - fseek( f, 0, SEEK_END); - fileSize = ftell(f); - fileBuf = new char[fileSize]; - fseek(f, 0, SEEK_SET); - amt_read = fread(fileBuf, 1, fileSize, f); - if (amt_read != fileSize || fileSize <= 0) { - errln("Error reading test data file."); - goto cleanUpAndReturn; - } - - // - // Look for a Unicode Signature (BOM) on the data just read - // - int32_t signatureLength; - const char * fileBufC; - const char* encoding; - - fileBufC = fileBuf; - encoding = ucnv_detectUnicodeSignature( - fileBuf, fileSize, &signatureLength, &status); - if(encoding!=NULL ){ - fileBufC += signatureLength; - fileSize -= signatureLength; - } - - // - // Open a converter to take the rule file to UTF-16 - // - conv = ucnv_open(encoding, &status); - if (U_FAILURE(status)) { - goto cleanUpAndReturn; - } - - // - // Convert the rules to UChar. - // Preflight first to determine required buffer size. - // - ulen = ucnv_toUChars(conv, - NULL, // dest, - 0, // destCapacity, - fileBufC, - fileSize, - &status); - if (status == U_BUFFER_OVERFLOW_ERROR) { - // Buffer Overflow is expected from the preflight operation. - status = U_ZERO_ERROR; - - retPtr = new UChar[ulen+1]; - ucnv_toUChars(conv, - retPtr, // dest, - ulen+1, - fileBufC, - fileSize, - &status); - } - -cleanUpAndReturn: - fclose(f); - delete[] fileBuf; - ucnv_close(conv); - if (U_FAILURE(status)) { - errln("ucnv_toUChars: ICU Error \"%s\"\n", u_errorName(status)); - delete retPtr; - retPtr = 0; - ulen = 0; - }; - return retPtr; -} - - -//------------------------------------------------------------------------------- -// -// PerlTests - Run Perl's regular expression tests -// The input file for this test is re_tests, the standard regular -// expression test data distributed with the Perl source code. -// -// Here is Perl's description of the test data file: -// -// # The tests are in a separate file 't/op/re_tests'. -// # Each line in that file is a separate test. -// # There are five columns, separated by tabs. -// # -// # Column 1 contains the pattern, optionally enclosed in C<''>. -// # Modifiers can be put after the closing C<'>. -// # -// # Column 2 contains the string to be matched. -// # -// # Column 3 contains the expected result: -// # y expect a match -// # n expect no match -// # c expect an error -// # B test exposes a known bug in Perl, should be skipped -// # b test exposes a known bug in Perl, should be skipped if noamp -// # -// # Columns 4 and 5 are used only if column 3 contains C<y> or C<c>. -// # -// # Column 4 contains a string, usually C<$&>. -// # -// # Column 5 contains the expected result of double-quote -// # interpolating that string after the match, or start of error message. -// # -// # Column 6, if present, contains a reason why the test is skipped. -// # This is printed with "skipped", for harness to pick up. -// # -// # \n in the tests are interpolated, as are variables of the form ${\w+}. -// # -// # If you want to add a regular expression test that can't be expressed -// # in this format, don't add it here: put it in op/pat.t instead. -// -// For ICU, if field 3 contains an 'i', the test will be skipped. -// The test exposes is some known incompatibility between ICU and Perl regexps. -// (The i is in addition to whatever was there before.) -// -//------------------------------------------------------------------------------- -void RegexTest::PerlTests() { - char tdd[2048]; - const char *srcPath; - UErrorCode status = U_ZERO_ERROR; - UParseError pe; - - // - // Open and read the test data file. - // - srcPath=getPath(tdd, "re_tests.txt"); - if(srcPath==NULL) { - return; /* something went wrong, error already output */ - } - - int32_t len; - UChar *testData = ReadAndConvertFile(srcPath, len, status); - if (U_FAILURE(status)) { - return; /* something went wrong, error already output */ - } - - // - // Put the test data into a UnicodeString - // - UnicodeString testDataString(FALSE, testData, len); - - // - // Regex to break the input file into lines, and strip the new lines. - // One line per match, capture group one is the desired data. - // - RegexPattern* linePat = RegexPattern::compile("(.+?)[\\r\\n]+", 0, pe, status); - if (U_FAILURE(status)) { - dataerrln("RegexPattern::compile() error"); - return; - } - RegexMatcher* lineMat = linePat->matcher(testDataString, status); - - // - // Regex to split a test file line into fields. - // There are six fields, separated by tabs. - // - RegexPattern* fieldPat = RegexPattern::compile("\\t", 0, pe, status); - - // - // Regex to identify test patterns with flag settings, and to separate them. - // Test patterns with flags look like 'pattern'i - // Test patterns without flags are not quoted: pattern - // Coming out, capture group 2 is the pattern, capture group 3 is the flags. - // - RegexPattern *flagPat = RegexPattern::compile("('?)(.*)\\1(.*)", 0, pe, status); - RegexMatcher* flagMat = flagPat->matcher(status); - - // - // The Perl tests reference several perl-isms, which are evaluated/substituted - // in the test data. Not being perl, this must be done explicitly. Here - // are string constants and REs for these constructs. - // - UnicodeString nulnulSrc("${nulnul}"); - UnicodeString nulnul("\\u0000\\u0000"); - nulnul = nulnul.unescape(); - - UnicodeString ffffSrc("${ffff}"); - UnicodeString ffff("\\uffff"); - ffff = ffff.unescape(); - - // regexp for $-[0], $+[2], etc. - RegexPattern *groupsPat = RegexPattern::compile("\\$([+\\-])\\[(\\d+)\\]", 0, pe, status); - RegexMatcher *groupsMat = groupsPat->matcher(status); - - // regexp for $0, $1, $2, etc. - RegexPattern *cgPat = RegexPattern::compile("\\$(\\d+)", 0, pe, status); - RegexMatcher *cgMat = cgPat->matcher(status); - - - // - // Main Loop for the Perl Tests, runs once per line from the - // test data file. - // - int32_t lineNum = 0; - int32_t skippedUnimplementedCount = 0; - while (lineMat->find()) { - lineNum++; - - // - // Get a line, break it into its fields, do the Perl - // variable substitutions. - // - UnicodeString line = lineMat->group(1, status); - UnicodeString fields[7]; - fieldPat->split(line, fields, 7, status); - - flagMat->reset(fields[0]); - flagMat->matches(status); - UnicodeString pattern = flagMat->group(2, status); - pattern.findAndReplace("${bang}", "!"); - pattern.findAndReplace(nulnulSrc, "\\u0000\\u0000"); - pattern.findAndReplace(ffffSrc, ffff); - - // - // Identify patterns that include match flag settings, - // split off the flags, remove the extra quotes. - // - UnicodeString flagStr = flagMat->group(3, status); - if (U_FAILURE(status)) { - errln("ucnv_toUChars: ICU Error \"%s\"\n", u_errorName(status)); - return; - } - int32_t flags = 0; - const UChar UChar_c = 0x63; // Char constants for the flag letters. - const UChar UChar_i = 0x69; // (Damn the lack of Unicode support in C) - const UChar UChar_m = 0x6d; - const UChar UChar_x = 0x78; - const UChar UChar_y = 0x79; - if (flagStr.indexOf(UChar_i) != -1) { - flags |= UREGEX_CASE_INSENSITIVE; - } - if (flagStr.indexOf(UChar_m) != -1) { - flags |= UREGEX_MULTILINE; - } - if (flagStr.indexOf(UChar_x) != -1) { - flags |= UREGEX_COMMENTS; - } - - // - // Compile the test pattern. - // - status = U_ZERO_ERROR; - RegexPattern *testPat = RegexPattern::compile(pattern, flags, pe, status); - if (status == U_REGEX_UNIMPLEMENTED) { - // - // Test of a feature that is planned for ICU, but not yet implemented. - // skip the test. - skippedUnimplementedCount++; - delete testPat; - status = U_ZERO_ERROR; - continue; - } - - if (U_FAILURE(status)) { - // Some tests are supposed to generate errors. - // Only report an error for tests that are supposed to succeed. - if (fields[2].indexOf(UChar_c) == -1 && // Compilation is not supposed to fail AND - fields[2].indexOf(UChar_i) == -1) // it's not an accepted ICU incompatibility - { - errln("line %d: ICU Error \"%s\"\n", lineNum, u_errorName(status)); - } - status = U_ZERO_ERROR; - delete testPat; - continue; - } - - if (fields[2].indexOf(UChar_i) >= 0) { - // ICU should skip this test. - delete testPat; - continue; - } - - if (fields[2].indexOf(UChar_c) >= 0) { - // This pattern should have caused a compilation error, but didn't/ - errln("line %d: Expected a pattern compile error, got success.", lineNum); - delete testPat; - continue; - } - - // - // replace the Perl variables that appear in some of the - // match data strings. - // - UnicodeString matchString = fields[1]; - matchString.findAndReplace(nulnulSrc, nulnul); - matchString.findAndReplace(ffffSrc, ffff); - - // Replace any \n in the match string with an actual new-line char. - // Don't do full unescape, as this unescapes more than Perl does, which - // causes other spurious failures in the tests. - matchString.findAndReplace("\\n", "\n"); - - - - // - // Run the test, check for expected match/don't match result. - // - RegexMatcher *testMat = testPat->matcher(matchString, status); - UBool found = testMat->find(); - UBool expected = FALSE; - if (fields[2].indexOf(UChar_y) >=0) { - expected = TRUE; - } - if (expected != found) { - errln("line %d: Expected %smatch, got %smatch", - lineNum, expected?"":"no ", found?"":"no " ); - continue; - } - - // - // Interpret the Perl expression from the fourth field of the data file, - // building up an ICU string from the results of the ICU match. - // The Perl expression will contain references to the results of - // a regex match, including the matched string, capture group strings, - // group starting and ending indicies, etc. - // - UnicodeString resultString; - UnicodeString perlExpr = fields[3]; - groupsMat->reset(perlExpr); - cgMat->reset(perlExpr); - - while (perlExpr.length() > 0) { - if (perlExpr.startsWith("$&")) { - resultString.append(testMat->group(status)); - perlExpr.remove(0, 2); - } - - else if (groupsMat->lookingAt(status)) { - // $-[0] $+[2] etc. - UnicodeString digitString = groupsMat->group(2, status); - int32_t t = 0; - int32_t groupNum = ICU_Utility::parseNumber(digitString, t, 10); - UnicodeString plusOrMinus = groupsMat->group(1, status); - int32_t matchPosition; - if (plusOrMinus.compare("+") == 0) { - matchPosition = testMat->end(groupNum, status); - } else { - matchPosition = testMat->start(groupNum, status); - } - if (matchPosition != -1) { - ICU_Utility::appendNumber(resultString, matchPosition); - } - perlExpr.remove(0, groupsMat->end(status)); - } - - else if (cgMat->lookingAt(status)) { - // $1, $2, $3, etc. - UnicodeString digitString = cgMat->group(1, status); - int32_t t = 0; - int32_t groupNum = ICU_Utility::parseNumber(digitString, t, 10); - if (U_SUCCESS(status)) { - resultString.append(testMat->group(groupNum, status)); - status = U_ZERO_ERROR; - } - perlExpr.remove(0, cgMat->end(status)); - } - - else if (perlExpr.startsWith("@-")) { - int32_t i; - for (i=0; i<=testMat->groupCount(); i++) { - if (i>0) { - resultString.append(" "); - } - ICU_Utility::appendNumber(resultString, testMat->start(i, status)); - } - perlExpr.remove(0, 2); - } - - else if (perlExpr.startsWith("@+")) { - int32_t i; - for (i=0; i<=testMat->groupCount(); i++) { - if (i>0) { - resultString.append(" "); - } - ICU_Utility::appendNumber(resultString, testMat->end(i, status)); - } - perlExpr.remove(0, 2); - } - - else if (perlExpr.startsWith("\\")) { // \Escape. Take following char as a literal. - // or as an escaped sequence (e.g. \n) - if (perlExpr.length() > 1) { - perlExpr.remove(0, 1); // Remove the '\', but only if not last char. - } - UChar c = perlExpr.charAt(0); - switch (c) { - case 'n': c = '\n'; break; - // add any other escape sequences that show up in the test expected results. - } - resultString.append(c); - perlExpr.remove(0, 1); - } - - else { - // Any characters from the perl expression that we don't explicitly - // recognize before here are assumed to be literals and copied - // as-is to the expected results. - resultString.append(perlExpr.charAt(0)); - perlExpr.remove(0, 1); - } - - if (U_FAILURE(status)) { - errln("Line %d: ICU Error \"%s\"", lineNum, u_errorName(status)); - break; - } - } - - // - // Expected Results Compare - // - UnicodeString expectedS(fields[4]); - expectedS.findAndReplace(nulnulSrc, nulnul); - expectedS.findAndReplace(ffffSrc, ffff); - expectedS.findAndReplace("\\n", "\n"); - - - if (expectedS.compare(resultString) != 0) { - err("Line %d: Incorrect perl expression results.", lineNum); - errln((UnicodeString)"Expected \""+expectedS+(UnicodeString)"\"; got \""+resultString+(UnicodeString)"\""); - } - - delete testMat; - delete testPat; - } - - // - // All done. Clean up allocated stuff. - // - delete cgMat; - delete cgPat; - - delete groupsMat; - delete groupsPat; - - delete flagMat; - delete flagPat; - - delete lineMat; - delete linePat; - - delete fieldPat; - delete [] testData; - - - logln("%d tests skipped because of unimplemented regexp features.", skippedUnimplementedCount); - -} - - - -#endif /* !UCONFIG_NO_REGULAR_EXPRESSIONS */ - |