X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/b75a7d8f3b4adbae880cab104ce2c6a50eee4db2..3d1f044b704633e2e541231cd17ae9ecf9ad5c7a:/icuSources/test/intltest/testutil.cpp diff --git a/icuSources/test/intltest/testutil.cpp b/icuSources/test/intltest/testutil.cpp index 56ec038a..31eaf202 100644 --- a/icuSources/test/intltest/testutil.cpp +++ b/icuSources/test/intltest/testutil.cpp @@ -1,6 +1,8 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /* ********************************************************************** -* Copyright (C) 2001, International Business Machines +* Copyright (C) 2001-2009, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * Date Name Description @@ -8,13 +10,25 @@ ********************************************************************** */ +#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::hex(UChar ch) { - UnicodeString buf; +UnicodeString &TestUtility::appendHex(UnicodeString &buf, UChar32 ch) { + if (ch >= 0x10000) { + if (ch >= 0x100000) { + buf.append(HEX[0xF&(ch>>20)]); + } + buf.append(HEX[0xF&(ch>>16)]); + } buf.append(HEX[0xF&(ch>>12)]); buf.append(HEX[0xF&(ch>>8)]); buf.append(HEX[0xF&(ch>>4)]); @@ -22,16 +36,268 @@ UnicodeString TestUtility::hex(UChar ch) { return buf; } +UnicodeString TestUtility::hex(UChar32 ch) { + UnicodeString buf; + appendHex(buf, ch); + return buf; +} + UnicodeString TestUtility::hex(const UnicodeString& s) { - return hex(s, 44 /*,*/); + return hex(s, u','); } UnicodeString TestUtility::hex(const UnicodeString& s, UChar sep) { - if (s.length() == 0) return ""; - UnicodeString result = hex(s.charAt(0)); - for (int32_t i = 1; i < s.length(); ++i) { - result.append(sep); - result.append(hex(s.charAt(i))); + UnicodeString result; + if (s.isEmpty()) return result; + UChar32 c; + for (int32_t i = 0; i < s.length(); i += U16_LENGTH(c)) { + c = s.char32At(i); + if (i > 0) { + result.append(sep); + } + appendHex(result, c); } return result; } + +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; +} + +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)); + } + } + } +}