X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/f3c0d7a59d99c2a94c6b8822291f0e42be3773c9..refs/heads/master:/icuSources/test/intltest/testutil.cpp?ds=inline diff --git a/icuSources/test/intltest/testutil.cpp b/icuSources/test/intltest/testutil.cpp index 8ed110a2..31eaf202 100644 --- a/icuSources/test/intltest/testutil.cpp +++ b/icuSources/test/intltest/testutil.cpp @@ -10,10 +10,17 @@ ********************************************************************** */ +#include +#include +#include "unicode/utypes.h" +#include "unicode/edits.h" #include "unicode/unistr.h" +#include "unicode/utf16.h" +#include "cmemory.h" #include "testutil.h" +#include "intltest.h" -static const UChar HEX[16]={48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70}; +static const UChar HEX[] = u"0123456789ABCDEF"; UnicodeString &TestUtility::appendHex(UnicodeString &buf, UChar32 ch) { if (ch >= 0x10000) { @@ -36,7 +43,7 @@ UnicodeString TestUtility::hex(UChar32 ch) { } UnicodeString TestUtility::hex(const UnicodeString& s) { - return hex(s, 44 /*,*/); + return hex(s, u','); } UnicodeString TestUtility::hex(const UnicodeString& s, UChar sep) { @@ -54,10 +61,243 @@ UnicodeString TestUtility::hex(const UnicodeString& s, UChar sep) { } UnicodeString TestUtility::hex(const uint8_t* bytes, int32_t len) { - UnicodeString buf; - for (int32_t i = 0; i < len; ++i) { - buf.append(HEX[0x0F & (bytes[i] >> 4)]); - buf.append(HEX[0x0F & bytes[i]]); - } - return buf; + UnicodeString buf; + for (int32_t i = 0; i < len; ++i) { + buf.append(HEX[0x0F & (bytes[i] >> 4)]); + buf.append(HEX[0x0F & bytes[i]]); + } + return buf; +} + +namespace { + +UnicodeString printOneEdit(const Edits::Iterator &ei) { + if (ei.hasChange()) { + return UnicodeString() + ei.oldLength() + u"->" + ei.newLength(); + } else { + return UnicodeString() + ei.oldLength() + u"=" + ei.newLength(); + } +} + +/** + * Maps indexes according to the expected edits. + * A destination index can occur multiple times when there are source deletions. + * Map according to the last occurrence, normally in a non-empty destination span. + * Simplest is to search from the back. + */ +int32_t srcIndexFromDest(const EditChange expected[], int32_t expLength, + int32_t srcLength, int32_t destLength, int32_t index) { + int32_t srcIndex = srcLength; + int32_t destIndex = destLength; + int32_t i = expLength; + while (index < destIndex && i > 0) { + --i; + int32_t prevSrcIndex = srcIndex - expected[i].oldLength; + int32_t prevDestIndex = destIndex - expected[i].newLength; + if (index == prevDestIndex) { + return prevSrcIndex; + } else if (index > prevDestIndex) { + if (expected[i].change) { + // In a change span, map to its end. + return srcIndex; + } else { + // In an unchanged span, offset within it. + return prevSrcIndex + (index - prevDestIndex); + } + } + srcIndex = prevSrcIndex; + destIndex = prevDestIndex; + } + // index is outside the string. + return srcIndex; +} + +int32_t destIndexFromSrc(const EditChange expected[], int32_t expLength, + int32_t srcLength, int32_t destLength, int32_t index) { + int32_t srcIndex = srcLength; + int32_t destIndex = destLength; + int32_t i = expLength; + while (index < srcIndex && i > 0) { + --i; + int32_t prevSrcIndex = srcIndex - expected[i].oldLength; + int32_t prevDestIndex = destIndex - expected[i].newLength; + if (index == prevSrcIndex) { + return prevDestIndex; + } else if (index > prevSrcIndex) { + if (expected[i].change) { + // In a change span, map to its end. + return destIndex; + } else { + // In an unchanged span, offset within it. + return prevDestIndex + (index - prevSrcIndex); + } + } + srcIndex = prevSrcIndex; + destIndex = prevDestIndex; + } + // index is outside the string. + return destIndex; +} + +} // namespace + +// For debugging, set -v to see matching edits up to a failure. +UBool TestUtility::checkEqualEdits(IntlTest &test, const UnicodeString &name, + const Edits &e1, const Edits &e2, UErrorCode &errorCode) { + Edits::Iterator ei1 = e1.getFineIterator(); + Edits::Iterator ei2 = e2.getFineIterator(); + UBool ok = TRUE; + for (int32_t i = 0; ok; ++i) { + UBool ei1HasNext = ei1.next(errorCode); + UBool ei2HasNext = ei2.next(errorCode); + ok &= test.assertEquals(name + u" next()[" + i + u"]" + __LINE__, + ei1HasNext, ei2HasNext); + ok &= test.assertSuccess(name + u" errorCode[" + i + u"]" + __LINE__, errorCode); + ok &= test.assertEquals(name + u" edit[" + i + u"]" + __LINE__, + printOneEdit(ei1), printOneEdit(ei2)); + if (!ei1HasNext || !ei2HasNext) { + break; + } + test.logln(); + } + return ok; +} + +void TestUtility::checkEditsIter( + IntlTest &test, + const UnicodeString &name, + Edits::Iterator ei1, Edits::Iterator ei2, // two equal iterators + const EditChange expected[], int32_t expLength, UBool withUnchanged, + UErrorCode &errorCode) { + test.assertFalse(name + u":" + __LINE__, ei2.findSourceIndex(-1, errorCode)); + test.assertFalse(name + u":" + __LINE__, ei2.findDestinationIndex(-1, errorCode)); + + int32_t expSrcIndex = 0; + int32_t expDestIndex = 0; + int32_t expReplIndex = 0; + for (int32_t expIndex = 0; expIndex < expLength; ++expIndex) { + const EditChange &expect = expected[expIndex]; + UnicodeString msg = UnicodeString(name).append(u' ') + expIndex; + if (withUnchanged || expect.change) { + test.assertTrue(msg + u":" + __LINE__, ei1.next(errorCode)); + test.assertEquals(msg + u":" + __LINE__, expect.change, ei1.hasChange()); + test.assertEquals(msg + u":" + __LINE__, expect.oldLength, ei1.oldLength()); + test.assertEquals(msg + u":" + __LINE__, expect.newLength, ei1.newLength()); + test.assertEquals(msg + u":" + __LINE__, expSrcIndex, ei1.sourceIndex()); + test.assertEquals(msg + u":" + __LINE__, expDestIndex, ei1.destinationIndex()); + test.assertEquals(msg + u":" + __LINE__, expReplIndex, ei1.replacementIndex()); + } + + if (expect.oldLength > 0) { + test.assertTrue(msg + u":" + __LINE__, ei2.findSourceIndex(expSrcIndex, errorCode)); + test.assertEquals(msg + u":" + __LINE__, expect.change, ei2.hasChange()); + test.assertEquals(msg + u":" + __LINE__, expect.oldLength, ei2.oldLength()); + test.assertEquals(msg + u":" + __LINE__, expect.newLength, ei2.newLength()); + test.assertEquals(msg + u":" + __LINE__, expSrcIndex, ei2.sourceIndex()); + test.assertEquals(msg + u":" + __LINE__, expDestIndex, ei2.destinationIndex()); + test.assertEquals(msg + u":" + __LINE__, expReplIndex, ei2.replacementIndex()); + if (!withUnchanged) { + // For some iterators, move past the current range + // so that findSourceIndex() has to look before the current index. + ei2.next(errorCode); + ei2.next(errorCode); + } + } + + if (expect.newLength > 0) { + test.assertTrue(msg + u":" + __LINE__, ei2.findDestinationIndex(expDestIndex, errorCode)); + test.assertEquals(msg + u":" + __LINE__, expect.change, ei2.hasChange()); + test.assertEquals(msg + u":" + __LINE__, expect.oldLength, ei2.oldLength()); + test.assertEquals(msg + u":" + __LINE__, expect.newLength, ei2.newLength()); + test.assertEquals(msg + u":" + __LINE__, expSrcIndex, ei2.sourceIndex()); + test.assertEquals(msg + u":" + __LINE__, expDestIndex, ei2.destinationIndex()); + test.assertEquals(msg + u":" + __LINE__, expReplIndex, ei2.replacementIndex()); + if (!withUnchanged) { + // For some iterators, move past the current range + // so that findSourceIndex() has to look before the current index. + ei2.next(errorCode); + ei2.next(errorCode); + } + } + + expSrcIndex += expect.oldLength; + expDestIndex += expect.newLength; + if (expect.change) { + expReplIndex += expect.newLength; + } + } + UnicodeString msg = UnicodeString(name).append(u" end"); + test.assertFalse(msg + u":" + __LINE__, ei1.next(errorCode)); + test.assertFalse(msg + u":" + __LINE__, ei1.hasChange()); + test.assertEquals(msg + u":" + __LINE__, 0, ei1.oldLength()); + test.assertEquals(msg + u":" + __LINE__, 0, ei1.newLength()); + test.assertEquals(msg + u":" + __LINE__, expSrcIndex, ei1.sourceIndex()); + test.assertEquals(msg + u":" + __LINE__, expDestIndex, ei1.destinationIndex()); + test.assertEquals(msg + u":" + __LINE__, expReplIndex, ei1.replacementIndex()); + + test.assertFalse(name + u":" + __LINE__, ei2.findSourceIndex(expSrcIndex, errorCode)); + test.assertFalse(name + u":" + __LINE__, ei2.findDestinationIndex(expDestIndex, errorCode)); + + // Check mapping of all indexes against a simple implementation + // that works on the expected changes. + // Iterate once forward, once backward, to cover more runtime conditions. + int32_t srcLength = expSrcIndex; + int32_t destLength = expDestIndex; + std::vector srcIndexes; + std::vector destIndexes; + srcIndexes.push_back(-1); + destIndexes.push_back(-1); + int32_t srcIndex = 0; + int32_t destIndex = 0; + for (int32_t i = 0; i < expLength; ++i) { + if (expected[i].oldLength > 0) { + srcIndexes.push_back(srcIndex); + if (expected[i].oldLength > 1) { + srcIndexes.push_back(srcIndex + 1); + if (expected[i].oldLength > 2) { + srcIndexes.push_back(srcIndex + expected[i].oldLength - 1); + } + } + } + if (expected[i].newLength > 0) { + destIndexes.push_back(destIndex); + if (expected[i].newLength > 1) { + destIndexes.push_back(destIndex + 1); + if (expected[i].newLength > 2) { + destIndexes.push_back(destIndex + expected[i].newLength - 1); + } + } + } + srcIndex += expected[i].oldLength; + destIndex += expected[i].newLength; + } + srcIndexes.push_back(srcLength); + destIndexes.push_back(destLength); + srcIndexes.push_back(srcLength + 1); + destIndexes.push_back(destLength + 1); + std::reverse(destIndexes.begin(), destIndexes.end()); + // Zig-zag across the indexes to stress next() <-> previous(). + static const int32_t ZIG_ZAG[] = { 0, 1, 2, 3, 2, 1 }; + for (auto i = 0; i < (int32_t)srcIndexes.size(); ++i) { + for (int32_t ij = 0; ij < UPRV_LENGTHOF(ZIG_ZAG); ++ij) { + int32_t j = ZIG_ZAG[ij]; + if ((i + j) < (int32_t)srcIndexes.size()) { + int32_t si = srcIndexes[i + j]; + test.assertEquals(name + u" destIndexFromSrc(" + si + u"):" + __LINE__, + destIndexFromSrc(expected, expLength, srcLength, destLength, si), + ei2.destinationIndexFromSourceIndex(si, errorCode)); + } + } + } + for (auto i = 0; i < (int32_t)destIndexes.size(); ++i) { + for (int32_t ij = 0; ij < UPRV_LENGTHOF(ZIG_ZAG); ++ij) { + int32_t j = ZIG_ZAG[ij]; + if ((i + j) < (int32_t)destIndexes.size()) { + int32_t di = destIndexes[i + j]; + test.assertEquals(name + u" srcIndexFromDest(" + di + u"):" + __LINE__, + srcIndexFromDest(expected, expLength, srcLength, destLength, di), + ei2.sourceIndexFromDestinationIndex(di, errorCode)); + } + } + } }