Add tests to verify QByteArray's zero termination
For data allocated and maintained by QByteArray, there's a guarantee that data() is null-terminated. This holds true even for null and empty, where logically the terminating character should never be dereferenced. For tests that modify or generate QByteArrays, this ensures the invariant is kept. In the toFromHex() text, const-ness of temporary variables was dropped to enable the test macro to be used, as the qualification didn't add much to the test otherwise. Change-Id: I7ee52e79e3a9df7de18c743f3698dab688e6bf0e Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
This commit is contained in:
parent
be4554d934
commit
4ef5a6269c
@ -147,8 +147,40 @@ private slots:
|
||||
void movablity_data();
|
||||
void movablity();
|
||||
void literals();
|
||||
|
||||
void zeroTermination_data();
|
||||
void zeroTermination();
|
||||
};
|
||||
|
||||
// Except for QByteArrays initialized with fromRawData(), QByteArray ensures
|
||||
// that data() is null-terminated. VERIFY_ZERO_TERMINATION checks that
|
||||
// invariant and goes further by testing that the null-character is in writable
|
||||
// memory allocated by QByteArray. If the invariant is broken, tools like
|
||||
// valgrind should be able to detect this.
|
||||
#define VERIFY_ZERO_TERMINATION(ba) \
|
||||
do { \
|
||||
/* Statics could be anything, as can fromRawData() strings */ \
|
||||
QByteArray::DataPtr baDataPtr = ba.data_ptr(); \
|
||||
if (!baDataPtr->ref.isStatic() \
|
||||
&& baDataPtr->offset == QByteArray().data_ptr()->offset) { \
|
||||
int baSize = ba.size(); \
|
||||
QCOMPARE(ba.constData()[baSize], '\0'); \
|
||||
\
|
||||
QByteArray baCopy(ba.constData(), baSize); \
|
||||
if (!baDataPtr->ref.isShared()) { \
|
||||
/* Don't detach, assumes no setSharable support */ \
|
||||
char *baData = ba.data(); \
|
||||
baData[baSize] = 'x'; \
|
||||
\
|
||||
QCOMPARE(ba.constData()[baSize], 'x'); \
|
||||
QCOMPARE(ba, baCopy); \
|
||||
\
|
||||
baData[baSize] = '\0'; /* Sanity restored */ \
|
||||
} \
|
||||
} \
|
||||
} while (false) \
|
||||
/**/
|
||||
|
||||
struct StaticByteArrays {
|
||||
struct Standard {
|
||||
QByteArrayData data;
|
||||
@ -224,8 +256,13 @@ void tst_QByteArray::qCompress_data()
|
||||
void tst_QByteArray::qCompress()
|
||||
{
|
||||
QFETCH( QByteArray, ba );
|
||||
|
||||
QByteArray compressed = ::qCompress( ba );
|
||||
QTEST( ::qUncompress( compressed ), "ba" );
|
||||
QByteArray uncompressed = ::qUncompress(compressed);
|
||||
|
||||
QCOMPARE(uncompressed, ba);
|
||||
VERIFY_ZERO_TERMINATION(compressed);
|
||||
VERIFY_ZERO_TERMINATION(uncompressed);
|
||||
}
|
||||
|
||||
void tst_QByteArray::qUncompressCorruptedData_data()
|
||||
@ -257,9 +294,11 @@ void tst_QByteArray::qUncompressCorruptedData()
|
||||
QByteArray res;
|
||||
res = ::qUncompress(in);
|
||||
QCOMPARE(res, QByteArray());
|
||||
VERIFY_ZERO_TERMINATION(res);
|
||||
|
||||
res = ::qUncompress(in + "blah");
|
||||
QCOMPARE(res, QByteArray());
|
||||
VERIFY_ZERO_TERMINATION(res);
|
||||
#else
|
||||
QSKIP("This test freezes on this platform");
|
||||
#endif
|
||||
@ -269,7 +308,7 @@ void tst_QByteArray::qCompressionZeroTermination()
|
||||
{
|
||||
QString s = "Hello, I'm a string.";
|
||||
QByteArray ba = ::qUncompress(::qCompress(s.toLocal8Bit()));
|
||||
QVERIFY((int) *(ba.data() + ba.size()) == 0);
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -289,6 +328,7 @@ void tst_QByteArray::constByteArray()
|
||||
QVERIFY(cba.constData()[1] == 'b');
|
||||
QVERIFY(cba.constData()[2] == 'c');
|
||||
QVERIFY(cba.constData()[3] == '\0');
|
||||
VERIFY_ZERO_TERMINATION(cba);
|
||||
}
|
||||
|
||||
void tst_QByteArray::leftJustified()
|
||||
@ -506,9 +546,11 @@ void tst_QByteArray::base64()
|
||||
|
||||
QByteArray arr = QByteArray::fromBase64(base64);
|
||||
QCOMPARE(arr, rawdata);
|
||||
VERIFY_ZERO_TERMINATION(arr);
|
||||
|
||||
QByteArray arr64 = rawdata.toBase64();
|
||||
QCOMPARE(arr64, base64);
|
||||
VERIFY_ZERO_TERMINATION(arr64);
|
||||
}
|
||||
|
||||
//different from the previous test as the input are invalid
|
||||
@ -733,17 +775,24 @@ void tst_QByteArray::chop()
|
||||
|
||||
src.chop(choplength);
|
||||
QCOMPARE(src, expected);
|
||||
VERIFY_ZERO_TERMINATION(src);
|
||||
}
|
||||
|
||||
void tst_QByteArray::prepend()
|
||||
{
|
||||
QByteArray ba("foo");
|
||||
QCOMPARE(ba.prepend((char*)0), QByteArray("foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.prepend(QByteArray()), QByteArray("foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.prepend("1"), QByteArray("1foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.prepend(QByteArray("2")), QByteArray("21foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.prepend('3'), QByteArray("321foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.prepend("\0 ", 2), QByteArray::fromRawData("\0 321foo", 8));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
}
|
||||
|
||||
void tst_QByteArray::prependExtended_data()
|
||||
@ -767,11 +816,17 @@ void tst_QByteArray::prependExtended()
|
||||
QCOMPARE(QByteArray("").prepend(array), QByteArray("data"));
|
||||
|
||||
QCOMPARE(array.prepend((char*)0), QByteArray("data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.prepend(QByteArray()), QByteArray("data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.prepend("1"), QByteArray("1data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.prepend(QByteArray("2")), QByteArray("21data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.prepend('3'), QByteArray("321data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.prepend("\0 ", 2), QByteArray::fromRawData("\0 321data", 9));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.size(), 9);
|
||||
}
|
||||
|
||||
@ -779,12 +834,19 @@ void tst_QByteArray::append()
|
||||
{
|
||||
QByteArray ba("foo");
|
||||
QCOMPARE(ba.append((char*)0), QByteArray("foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.append(QByteArray()), QByteArray("foo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.append("1"), QByteArray("foo1"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.append(QByteArray("2")), QByteArray("foo12"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.append('3'), QByteArray("foo123"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.append("\0"), QByteArray("foo123"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.append("\0", 1), QByteArray::fromRawData("foo123\0", 7));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.size(), 7);
|
||||
}
|
||||
|
||||
@ -801,12 +863,19 @@ void tst_QByteArray::appendExtended()
|
||||
QCOMPARE(QByteArray("").append(array), QByteArray("data"));
|
||||
|
||||
QCOMPARE(array.append((char*)0), QByteArray("data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.append(QByteArray()), QByteArray("data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.append("1"), QByteArray("data1"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.append(QByteArray("2")), QByteArray("data12"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.append('3'), QByteArray("data123"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.append("\0"), QByteArray("data123"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.append("\0", 1), QByteArray::fromRawData("data123\0", 8));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
QCOMPARE(array.size(), 8);
|
||||
}
|
||||
|
||||
@ -814,22 +883,28 @@ void tst_QByteArray::insert()
|
||||
{
|
||||
QByteArray ba("Meal");
|
||||
QCOMPARE(ba.insert(1, QByteArray("ontr")), QByteArray("Montreal"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
QCOMPARE(ba.insert(ba.size(), "foo"), QByteArray("Montrealfoo"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
|
||||
ba = QByteArray("13");
|
||||
QCOMPARE(ba.insert(1, QByteArray("2")), QByteArray("123"));
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
|
||||
ba = "ac";
|
||||
QCOMPARE(ba.insert(1, 'b'), QByteArray("abc"));
|
||||
QCOMPARE(ba.size(), 3);
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
|
||||
ba = "ikl";
|
||||
QCOMPARE(ba.insert(1, "j"), QByteArray("ijkl"));
|
||||
QCOMPARE(ba.size(), 4);
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
|
||||
ba = "ab";
|
||||
QCOMPARE(ba.insert(1, "\0X\0", 3), QByteArray::fromRawData("a\0X\0b", 5));
|
||||
QCOMPARE(ba.size(), 5);
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
}
|
||||
|
||||
void tst_QByteArray::insertExtended_data()
|
||||
@ -842,6 +917,7 @@ void tst_QByteArray::insertExtended()
|
||||
QFETCH(QByteArray, array);
|
||||
QCOMPARE(array.insert(1, "i"), QByteArray("diata"));
|
||||
QCOMPARE(array.size(), 5);
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
}
|
||||
|
||||
void tst_QByteArray::remove_data()
|
||||
@ -872,6 +948,7 @@ void tst_QByteArray::remove()
|
||||
QFETCH(int, length);
|
||||
QFETCH(QByteArray, expected);
|
||||
QCOMPARE(src.remove(position, length), expected);
|
||||
VERIFY_ZERO_TERMINATION(src);
|
||||
}
|
||||
|
||||
void tst_QByteArray::replace_data()
|
||||
@ -913,6 +990,8 @@ void tst_QByteArray::replace()
|
||||
|
||||
QCOMPARE(str1.replace(pos, len, after).constData(), expected.constData());
|
||||
QCOMPARE(str2.replace(pos, len, after.data()), expected);
|
||||
VERIFY_ZERO_TERMINATION(str1);
|
||||
VERIFY_ZERO_TERMINATION(str2);
|
||||
}
|
||||
|
||||
void tst_QByteArray::replaceWithSpecifiedLength()
|
||||
@ -925,6 +1004,7 @@ void tst_QByteArray::replaceWithSpecifiedLength()
|
||||
const char _expected[] = "zxc\0vbcdefghjk";
|
||||
QByteArray expected(_expected,sizeof(_expected)-1);
|
||||
QCOMPARE(ba,expected);
|
||||
VERIFY_ZERO_TERMINATION(ba);
|
||||
}
|
||||
|
||||
void tst_QByteArray::indexOf_data()
|
||||
@ -1227,6 +1307,7 @@ void tst_QByteArray::appendAfterFromRawData()
|
||||
data[0] = 'Y';
|
||||
}
|
||||
QVERIFY(arr.at(0) == 'X');
|
||||
VERIFY_ZERO_TERMINATION(arr);
|
||||
}
|
||||
|
||||
void tst_QByteArray::toFromHex_data()
|
||||
@ -1298,15 +1379,17 @@ void tst_QByteArray::toFromHex()
|
||||
QFETCH(QByteArray, hex_alt1);
|
||||
|
||||
{
|
||||
const QByteArray th = str.toHex();
|
||||
QByteArray th = str.toHex();
|
||||
QCOMPARE(th.size(), hex.size());
|
||||
QCOMPARE(th, hex);
|
||||
VERIFY_ZERO_TERMINATION(th);
|
||||
}
|
||||
|
||||
{
|
||||
const QByteArray fh = QByteArray::fromHex(hex);
|
||||
QByteArray fh = QByteArray::fromHex(hex);
|
||||
QCOMPARE(fh.size(), str.size());
|
||||
QCOMPARE(fh, str);
|
||||
VERIFY_ZERO_TERMINATION(fh);
|
||||
}
|
||||
|
||||
QCOMPARE(QByteArray::fromHex(hex_alt1), str);
|
||||
@ -1319,14 +1402,17 @@ void tst_QByteArray::toFromPercentEncoding()
|
||||
QByteArray data = arr.toPercentEncoding();
|
||||
QCOMPARE(QString(data), QString("Qt%20is%20great%21"));
|
||||
QCOMPARE(QByteArray::fromPercentEncoding(data), arr);
|
||||
VERIFY_ZERO_TERMINATION(data);
|
||||
|
||||
data = arr.toPercentEncoding("! ", "Qt");
|
||||
QCOMPARE(QString(data), QString("%51%74 is grea%74!"));
|
||||
QCOMPARE(QByteArray::fromPercentEncoding(data), arr);
|
||||
VERIFY_ZERO_TERMINATION(data);
|
||||
|
||||
data = arr.toPercentEncoding(QByteArray(), "abcdefghijklmnopqrstuvwxyz", 'Q');
|
||||
QCOMPARE(QString(data), QString("Q51Q74Q20Q69Q73Q20Q67Q72Q65Q61Q74Q21"));
|
||||
QCOMPARE(QByteArray::fromPercentEncoding(data, 'Q'), arr);
|
||||
VERIFY_ZERO_TERMINATION(data);
|
||||
|
||||
// verify that to/from percent encoding preserves nullity
|
||||
arr = "";
|
||||
@ -1336,6 +1422,7 @@ void tst_QByteArray::toFromPercentEncoding()
|
||||
QVERIFY(!arr.toPercentEncoding().isNull());
|
||||
QVERIFY(QByteArray::fromPercentEncoding("").isEmpty());
|
||||
QVERIFY(!QByteArray::fromPercentEncoding("").isNull());
|
||||
VERIFY_ZERO_TERMINATION(arr);
|
||||
|
||||
arr = QByteArray();
|
||||
QVERIFY(arr.isEmpty());
|
||||
@ -1344,6 +1431,7 @@ void tst_QByteArray::toFromPercentEncoding()
|
||||
QVERIFY(arr.toPercentEncoding().isNull());
|
||||
QVERIFY(QByteArray::fromPercentEncoding(QByteArray()).isEmpty());
|
||||
QVERIFY(QByteArray::fromPercentEncoding(QByteArray()).isNull());
|
||||
VERIFY_ZERO_TERMINATION(arr);
|
||||
}
|
||||
|
||||
void tst_QByteArray::fromPercentEncoding_data()
|
||||
@ -1585,6 +1673,9 @@ void tst_QByteArray::repeated() const
|
||||
QFETCH(int, count);
|
||||
|
||||
QCOMPARE(string.repeated(count), expected);
|
||||
|
||||
QByteArray repeats = string.repeated(count);
|
||||
VERIFY_ZERO_TERMINATION(repeats);
|
||||
}
|
||||
|
||||
void tst_QByteArray::repeated_data() const
|
||||
@ -1676,6 +1767,7 @@ void tst_QByteArray::byteRefDetaching() const
|
||||
copy[0] = 'S';
|
||||
|
||||
QCOMPARE(str, QByteArray("str"));
|
||||
VERIFY_ZERO_TERMINATION(copy);
|
||||
}
|
||||
|
||||
{
|
||||
@ -1684,6 +1776,7 @@ void tst_QByteArray::byteRefDetaching() const
|
||||
str[0] = 'S';
|
||||
|
||||
QCOMPARE(buf[0], char('s'));
|
||||
VERIFY_ZERO_TERMINATION(str);
|
||||
}
|
||||
|
||||
{
|
||||
@ -1694,6 +1787,7 @@ void tst_QByteArray::byteRefDetaching() const
|
||||
str[0] = 'S';
|
||||
|
||||
QCOMPARE(buf[0], char('s'));
|
||||
VERIFY_ZERO_TERMINATION(str);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1703,19 +1797,25 @@ void tst_QByteArray::reserve()
|
||||
QByteArray qba;
|
||||
qba.reserve(capacity);
|
||||
QVERIFY(qba.capacity() == capacity);
|
||||
char *data = qba.data();
|
||||
VERIFY_ZERO_TERMINATION(qba);
|
||||
|
||||
char *data = qba.data();
|
||||
for (int i = 0; i < capacity; i++) {
|
||||
qba.resize(i);
|
||||
QVERIFY(capacity == qba.capacity());
|
||||
QVERIFY(data == qba.data());
|
||||
VERIFY_ZERO_TERMINATION(qba);
|
||||
}
|
||||
|
||||
QByteArray nil1, nil2;
|
||||
nil1.reserve(0);
|
||||
VERIFY_ZERO_TERMINATION(nil1);
|
||||
nil2.squeeze();
|
||||
VERIFY_ZERO_TERMINATION(nil2);
|
||||
nil1.squeeze();
|
||||
VERIFY_ZERO_TERMINATION(nil1);
|
||||
nil2.reserve(0);
|
||||
VERIFY_ZERO_TERMINATION(nil2);
|
||||
}
|
||||
|
||||
void tst_QByteArray::reserveExtended_data()
|
||||
@ -1726,12 +1826,16 @@ void tst_QByteArray::reserveExtended_data()
|
||||
void tst_QByteArray::reserveExtended()
|
||||
{
|
||||
QFETCH(QByteArray, array);
|
||||
|
||||
array.reserve(1024);
|
||||
QVERIFY(array.capacity() == 1024);
|
||||
QCOMPARE(array, QByteArray("data"));
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
|
||||
array.squeeze();
|
||||
QCOMPARE(array, QByteArray("data"));
|
||||
QCOMPARE(array.capacity(), array.size());
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
}
|
||||
|
||||
void tst_QByteArray::movablity_data()
|
||||
@ -1824,6 +1928,7 @@ void tst_QByteArray::literals()
|
||||
QVERIFY(str == "abcd");
|
||||
QVERIFY(str.data_ptr()->ref.isStatic());
|
||||
QVERIFY(str.data_ptr()->offset == sizeof(QByteArrayData));
|
||||
VERIFY_ZERO_TERMINATION(str);
|
||||
|
||||
const char *s = str.constData();
|
||||
QByteArray str2 = str;
|
||||
@ -1831,14 +1936,27 @@ void tst_QByteArray::literals()
|
||||
|
||||
// detach on non const access
|
||||
QVERIFY(str.data() != s);
|
||||
VERIFY_ZERO_TERMINATION(str);
|
||||
|
||||
QVERIFY(str2.constData() == s);
|
||||
QVERIFY(str2.data() != s);
|
||||
VERIFY_ZERO_TERMINATION(str2);
|
||||
#else
|
||||
QSKIP("Only tested on c++0x compliant compiler or gcc");
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QByteArray::zeroTermination_data()
|
||||
{
|
||||
movablity_data();
|
||||
}
|
||||
|
||||
void tst_QByteArray::zeroTermination()
|
||||
{
|
||||
QFETCH(QByteArray, array);
|
||||
VERIFY_ZERO_TERMINATION(array);
|
||||
}
|
||||
|
||||
const char globalChar = '1';
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QByteArray)
|
||||
|
Loading…
Reference in New Issue
Block a user