File indexing completed on 2024-05-12 17:15:22
0001 /* 0002 Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LGPL. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 0019 Alternatively, this file is available under the Mozilla Public License 0020 Version 1.1. You may obtain a copy of the License at 0021 http://www.mozilla.org/MPL/ 0022 */ 0023 0024 #include "arguments.h" 0025 0026 #include "../testutil.h" 0027 0028 #include <algorithm> 0029 #include <cstdint> 0030 #include <cstring> 0031 #include <iostream> 0032 0033 // Handy helpers 0034 0035 static void printChunk(chunk a) 0036 { 0037 std::cout << "Array: "; 0038 for (uint32 i = 0; i < a.length; i++) { 0039 std::cout << int(a.ptr[i]) << '|'; 0040 } 0041 std::cout << '\n'; 0042 } 0043 0044 static bool chunksEqual(chunk a1, chunk a2) 0045 { 0046 if (a1.length != a2.length) { 0047 std::cout << "Different lengths.\n"; 0048 printChunk(a1); 0049 printChunk(a2); 0050 return false; 0051 } 0052 for (uint32 i = 0; i < a1.length; i++) { 0053 if (a1.ptr[i] != a2.ptr[i]) { 0054 std::cout << "Different content.\n"; 0055 printChunk(a1); 0056 printChunk(a2); 0057 return false; 0058 } 0059 } 0060 return true; 0061 } 0062 0063 static bool stringsEqual(cstring s1, cstring s2) 0064 { 0065 return chunksEqual(chunk(s1.ptr, s1.length), chunk(s2.ptr, s2.length)); 0066 } 0067 0068 static void maybeBeginDictEntry(Arguments::Writer *writer) 0069 { 0070 (void) writer; 0071 #ifdef WITH_DICT_ENTRY 0072 writer->beginDictEntry(); 0073 #endif 0074 } 0075 0076 static void maybeEndDictEntry(Arguments::Writer *writer) 0077 { 0078 (void) writer; 0079 #ifdef WITH_DICT_ENTRY 0080 writer->endDictEntry(); 0081 #endif 0082 } 0083 0084 // This class does: 0085 // 1) iterates over the full Arguments with m_reader 0086 // 2) skips whole aggregates at and below nesting level m_skipAggregatesFromLevel with m_skippingReader 0087 // 3) skips nil arrays at and below nil array nesting level m_skipNilArraysFromLevel with m_skippingReader 0088 // It even skips aggregates inside nil arrays as 2) + 3) imply. 0089 // It checks: 0090 // a) where nothing is skipped, that the aggregate structure and data read are the same 0091 class SkipChecker 0092 { 0093 public: 0094 SkipChecker(Arguments::Reader *reader, Arguments::Reader *skippingReader, 0095 int skipAggregatesFromLevel, int skipNilArraysFromLevel) 0096 : m_nestingLevel(0), 0097 m_nilArrayNesting(0), 0098 m_skipAggregatesFromLevel(skipAggregatesFromLevel), 0099 m_skipNilArraysFromLevel(skipNilArraysFromLevel), 0100 m_reader(reader), 0101 m_skippingReader(skippingReader) 0102 {} 0103 0104 template<typename F> 0105 void readAndCompare(F readFunc) 0106 { 0107 Arguments::IoState rState = m_reader->state(); 0108 auto rval = (*m_reader.*readFunc)(); 0109 if (m_nestingLevel < m_skipAggregatesFromLevel && m_nilArrayNesting < m_skipNilArraysFromLevel) { 0110 Arguments::IoState sState = m_skippingReader->state(); 0111 TEST(rState == sState); 0112 auto sval = (*m_skippingReader.*readFunc)(); 0113 if (!m_nilArrayNesting) { 0114 TEST(myEqual(rval, sval)); 0115 } 0116 } 0117 } 0118 #ifdef WITH_DICT_ENTRY 0119 void beginDictEntry() 0120 { 0121 m_reader->beginDictEntry(); 0122 if (m_nestingLevel < m_skipAggregatesFromLevel && m_nilArrayNesting < m_skipNilArraysFromLevel) { 0123 m_skippingReader->beginDictEntry(); 0124 } 0125 } 0126 0127 void endDictEntry() 0128 { 0129 m_reader->endDictEntry(); 0130 if (m_nestingLevel < m_skipAggregatesFromLevel && m_nilArrayNesting < m_skipNilArraysFromLevel) { 0131 m_skippingReader->endDictEntry(); 0132 } 0133 0134 } 0135 #endif 0136 template<typename F, typename G> 0137 void beginAggregate(F beginFunc, G skipFunc) 0138 { 0139 (*m_reader.*beginFunc)(); 0140 m_nestingLevel++; 0141 0142 if (m_nilArrayNesting < m_skipNilArraysFromLevel) { 0143 if (m_nestingLevel < m_skipAggregatesFromLevel) { 0144 (*m_skippingReader.*beginFunc)(); 0145 } else if (m_nestingLevel == m_skipAggregatesFromLevel) { 0146 (*m_skippingReader.*skipFunc)(); 0147 } 0148 } 0149 } 0150 0151 template<typename F, typename G> 0152 void beginArrayAggregate(F beginFunc, G skipFunc) 0153 { 0154 const bool hasData = (*m_reader.*beginFunc)(Arguments::Reader::ReadTypesOnlyIfEmpty); 0155 m_nestingLevel++; 0156 m_nilArrayNesting += hasData ? 0 : 1; 0157 0158 if (m_nestingLevel > m_skipAggregatesFromLevel || m_nilArrayNesting > m_skipNilArraysFromLevel) { 0159 // we're already skipping, do nothing 0160 } else if (m_nestingLevel == m_skipAggregatesFromLevel) { 0161 (*m_skippingReader.*skipFunc)(); 0162 } else if (m_nilArrayNesting == m_skipNilArraysFromLevel) { 0163 (*m_skippingReader.*beginFunc)(Arguments::Reader::SkipIfEmpty); 0164 } else { 0165 (*m_skippingReader.*beginFunc)(Arguments::Reader::ReadTypesOnlyIfEmpty); 0166 } 0167 } 0168 0169 template<typename F> 0170 void endAggregate(F endFunc, bool isArrayType) 0171 { 0172 (*m_reader.*endFunc)(); 0173 0174 // when skipping a nil array: do the last part of the beginArray(), endArray() sequence 0175 // when using skip*(): do not call end() on that level, skip*() moves right past the aggregate 0176 if (m_nestingLevel < m_skipAggregatesFromLevel && 0177 (m_nilArrayNesting < m_skipNilArraysFromLevel || 0178 (isArrayType && m_nilArrayNesting == m_skipNilArraysFromLevel))) { 0179 (*m_skippingReader.*endFunc)(); 0180 } else { 0181 // we've already skipped the current aggregate 0182 } 0183 0184 m_nestingLevel--; 0185 if (isArrayType && m_nilArrayNesting) { 0186 m_nilArrayNesting--; 0187 } 0188 } 0189 0190 int m_nestingLevel; 0191 int m_nilArrayNesting; 0192 const int m_skipAggregatesFromLevel; 0193 const int m_skipNilArraysFromLevel; 0194 0195 private: 0196 template<typename T> bool myEqual(const T &a, const T &b) { return a == b; } 0197 bool myEqua(const chunk &a, const chunk &b) { return chunksEqual(a, b); } 0198 bool myEqual(const cstring &a, const cstring &b) { return stringsEqual(a, b); } 0199 0200 Arguments::Reader *m_reader; 0201 Arguments::Reader *m_skippingReader; 0202 }; 0203 0204 static void testReadWithSkip(const Arguments &arg, bool debugPrint) 0205 { 0206 // it would be even better to decide when to skip more "randomly", but given that it doesn't make 0207 // much difference in the implementation, this should do. 0208 // loop over when to skip aggregates voluntarily (on "skipper") 0209 for (int aggregateSkipLevel = 1 /* 1 orig, 15 = H4X disabled*/; aggregateSkipLevel < 16; 0210 aggregateSkipLevel++) { 0211 // loop over when to skip empty aka nil arrays - on "reader", which: 0212 // - cross checks aggregate skipping vs. skipping nil arrays 0213 // - is also the primary test for nil arrays 0214 for (int nilArraySkipLevel = 1; nilArraySkipLevel < 8; nilArraySkipLevel++) { 0215 // loop over *how* to skip empty aka nil arrays, 0216 // beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty) or skipArray() 0217 0218 Arguments::Reader reader(arg); 0219 Arguments::Reader skippingReader(arg); 0220 SkipChecker checker(&reader, &skippingReader, aggregateSkipLevel, nilArraySkipLevel); 0221 0222 bool isDone = false; 0223 0224 while (!isDone) { 0225 TEST(reader.state() != Arguments::InvalidData); 0226 TEST(skippingReader.state() != Arguments::InvalidData); 0227 0228 if (debugPrint) { 0229 std::cerr << "Reader state: " << reader.stateString().ptr << '\n'; 0230 std::cerr << "Skipping reader state: " << skippingReader.stateString().ptr << '\n'; 0231 } 0232 0233 switch(reader.state()) { 0234 case Arguments::Finished: 0235 TEST(checker.m_nestingLevel == 0); 0236 TEST(checker.m_nilArrayNesting == 0); 0237 isDone = true; 0238 break; 0239 case Arguments::BeginStruct: 0240 //std::cerr << "Beginning struct\n"; 0241 checker.beginAggregate(&Arguments::Reader::beginStruct, &Arguments::Reader::skipStruct); 0242 break; 0243 case Arguments::EndStruct: 0244 checker.endAggregate(&Arguments::Reader::endStruct, false); 0245 break; 0246 case Arguments::BeginVariant: 0247 //std::cerr << "Beginning variant\n"; 0248 checker.beginAggregate(&Arguments::Reader::beginVariant, &Arguments::Reader::skipVariant); 0249 break; 0250 case Arguments::EndVariant: 0251 checker.endAggregate(&Arguments::Reader::endVariant, false); 0252 break; 0253 case Arguments::BeginArray: 0254 checker.beginArrayAggregate(&Arguments::Reader::beginArray, &Arguments::Reader::skipArray); 0255 break; 0256 case Arguments::EndArray: 0257 checker.endAggregate(&Arguments::Reader::endArray, true); 0258 break; 0259 case Arguments::BeginDict: 0260 checker.beginArrayAggregate(&Arguments::Reader::beginDict, &Arguments::Reader::skipDict); 0261 break; 0262 #ifdef WITH_DICT_ENTRY 0263 case Arguments::BeginDictEntry: 0264 checker.beginDictEntry(); 0265 break; 0266 case Arguments::EndDictEntry: 0267 checker.endDictEntry(); 0268 break; 0269 #endif 0270 case Arguments::EndDict: 0271 checker.endAggregate(&Arguments::Reader::endDict, true); 0272 break; 0273 case Arguments::Byte: 0274 checker.readAndCompare(&Arguments::Reader::readByte); 0275 break; 0276 case Arguments::Boolean: 0277 checker.readAndCompare(&Arguments::Reader::readBoolean); 0278 break; 0279 case Arguments::Int16: 0280 checker.readAndCompare(&Arguments::Reader::readInt16); 0281 break; 0282 case Arguments::Uint16: 0283 checker.readAndCompare(&Arguments::Reader::readUint16); 0284 break; 0285 case Arguments::Int32: 0286 checker.readAndCompare(&Arguments::Reader::readInt32); 0287 break; 0288 case Arguments::Uint32: 0289 checker.readAndCompare(&Arguments::Reader::readUint32); 0290 break; 0291 case Arguments::Int64: 0292 checker.readAndCompare(&Arguments::Reader::readInt64); 0293 break; 0294 case Arguments::Uint64: 0295 checker.readAndCompare(&Arguments::Reader::readUint64); 0296 break; 0297 case Arguments::Double: 0298 checker.readAndCompare(&Arguments::Reader::readDouble); 0299 break; 0300 case Arguments::String: 0301 checker.readAndCompare(&Arguments::Reader::readString); 0302 break; 0303 case Arguments::ObjectPath: 0304 checker.readAndCompare(&Arguments::Reader::readObjectPath); 0305 break; 0306 case Arguments::Signature: 0307 checker.readAndCompare(&Arguments::Reader::readSignature); 0308 break; 0309 case Arguments::UnixFd: 0310 checker.readAndCompare(&Arguments::Reader::readUnixFd); 0311 break; 0312 0313 case Arguments::NeedMoreData: 0314 // ### would be nice to test this as well 0315 default: 0316 TEST(false); 0317 break; 0318 } 0319 } 0320 0321 TEST(reader.state() == Arguments::Finished); 0322 TEST(skippingReader.state() == Arguments::Finished); 0323 } 0324 } 0325 } 0326 0327 // When using this to iterate over the reader, it will make an exact copy using the Writer. 0328 // You need to do something only in states where something special should happen. 0329 static void defaultReadToWrite(Arguments::Reader *reader, Arguments::Writer *writer) 0330 { 0331 switch(reader->state()) { 0332 case Arguments::BeginStruct: 0333 case Arguments::EndStruct: 0334 case Arguments::BeginVariant: 0335 case Arguments::EndVariant: 0336 case Arguments::EndArray: 0337 #ifdef WITH_DICT_ENTRY 0338 case Arguments::BeginDictEntry: 0339 case Arguments::EndDictEntry: 0340 #endif 0341 case Arguments::EndDict: 0342 case Arguments::Byte: 0343 case Arguments::Boolean: 0344 case Arguments::Int16: 0345 case Arguments::Uint16: 0346 case Arguments::Int32: 0347 case Arguments::Uint32: 0348 case Arguments::Int64: 0349 case Arguments::Uint64: 0350 case Arguments::Double: 0351 case Arguments::UnixFd: 0352 Arguments::copyOneElement(reader, writer); 0353 break; 0354 // special handling for BeginArray and BeginDict to avoid "fast copy" for primitive arrays 0355 case Arguments::BeginArray: { 0356 const bool hasData = reader->beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); 0357 writer->beginArray(hasData ? Arguments::Writer::NonEmptyArray 0358 : Arguments::Writer::WriteTypesOfEmptyArray); 0359 break; } 0360 case Arguments::BeginDict: { 0361 const bool hasData = reader->beginDict(Arguments::Reader::ReadTypesOnlyIfEmpty); 0362 writer->beginDict(hasData ? Arguments::Writer::NonEmptyArray 0363 : Arguments::Writer::WriteTypesOfEmptyArray); 0364 break; } 0365 case Arguments::String: { 0366 const cstring s = reader->readString(); 0367 if (!reader->isInsideEmptyArray()) { 0368 TEST(Arguments::isStringValid(s)); 0369 } 0370 writer->writeString(s); 0371 break; } 0372 case Arguments::ObjectPath: { 0373 const cstring objectPath = reader->readObjectPath(); 0374 if (!reader->isInsideEmptyArray()) { 0375 TEST(Arguments::isObjectPathValid(objectPath)); 0376 } 0377 writer->writeObjectPath(objectPath); 0378 break; } 0379 case Arguments::Signature: { 0380 const cstring signature = reader->readSignature(); 0381 if (!reader->isInsideEmptyArray()) { 0382 TEST(Arguments::isSignatureValid(signature)); 0383 } 0384 writer->writeSignature(signature); 0385 break; } 0386 // special cases follow 0387 case Arguments::Finished: 0388 break; // You *probably* want to handle that one in the caller, but you don't have to 0389 case Arguments::NeedMoreData: 0390 TEST(false); // No way to handle that one here 0391 break; 0392 default: 0393 TEST(false); 0394 break; 0395 } 0396 } 0397 0398 static void verifyAfterRoundtrip(const Arguments &original, const Arguments::Reader &originalReader, 0399 const Arguments ©, const Arguments::Writer ©Writer, 0400 bool debugPrint) 0401 { 0402 TEST(originalReader.state() == Arguments::Finished); 0403 TEST(copyWriter.state() == Arguments::Finished); 0404 cstring originalSignature = original.signature(); 0405 cstring copySignature = copy.signature(); 0406 if (originalSignature.length) { 0407 TEST(Arguments::isSignatureValid(copySignature)); 0408 TEST(stringsEqual(originalSignature, copySignature)); 0409 } else { 0410 TEST(copySignature.length == 0); 0411 } 0412 0413 chunk originalData = original.data(); 0414 0415 chunk copyData = copy.data(); 0416 TEST(originalData.length == copyData.length); 0417 if (debugPrint && !chunksEqual(originalData, copyData)) { 0418 printChunk(originalData); 0419 printChunk(copyData); 0420 } 0421 TEST(chunksEqual(originalData, copyData)); 0422 } 0423 0424 static void doRoundtripWithShortReads(const Arguments &original, uint32 dataIncrement, bool debugPrint) 0425 { 0426 const chunk data = original.data(); 0427 chunk shortData; 0428 0429 Arguments arg(nullptr, original.signature(), shortData, original.fileDescriptors()); 0430 Arguments::Reader reader(arg); 0431 Arguments::Writer writer; 0432 0433 bool isDone = false; 0434 0435 while (!isDone) { 0436 TEST(writer.state() != Arguments::InvalidData); 0437 if (debugPrint) { 0438 std::cout << "Reader state: " << reader.stateString().ptr << '\n'; 0439 } 0440 0441 switch(reader.state()) { 0442 case Arguments::Finished: 0443 isDone = true; 0444 break; 0445 case Arguments::NeedMoreData: { 0446 TEST(shortData.length < data.length); 0447 // reallocate shortData to test that Reader can handle the data moving around - and 0448 // allocate the new one before destroying the old one to make sure that the pointer differs 0449 chunk oldData = shortData; 0450 shortData.length = std::min(shortData.length + dataIncrement, data.length); 0451 shortData.ptr = reinterpret_cast<byte *>(malloc(shortData.length)); 0452 for (uint32 i = 0; i < shortData.length; i++) { 0453 shortData.ptr[i] = data.ptr[i]; 0454 } 0455 // clobber it to provoke errors that only valgrind might find otherwise 0456 for (uint32 i = 0; i < oldData.length; i++) { 0457 oldData.ptr[i] = 0xff; 0458 } 0459 if (oldData.ptr) { 0460 free(oldData.ptr); 0461 } 0462 reader.replaceData(shortData); 0463 break; } 0464 default: 0465 defaultReadToWrite(&reader, &writer); 0466 break; 0467 } 0468 } 0469 0470 Arguments copy = writer.finish(); 0471 verifyAfterRoundtrip(original, reader, copy, writer, debugPrint); 0472 if (shortData.ptr) { 0473 free(shortData.ptr); 0474 } 0475 } 0476 0477 static void doRoundtripWithReaderCopy(const Arguments &original, uint32 dataIncrement, bool debugPrint) 0478 { 0479 Arguments::Reader *reader = new Arguments::Reader(original); 0480 Arguments::Writer writer; 0481 0482 bool isDone = false; 0483 uint32 i = 0; 0484 0485 while (!isDone) { 0486 TEST(writer.state() != Arguments::InvalidData); 0487 if (i++ == dataIncrement) { 0488 Arguments::Reader *copy = new Arguments::Reader(*reader); 0489 delete reader; 0490 reader = copy; 0491 } 0492 if (debugPrint) { 0493 std::cout << "Reader state: " << reader->stateString().ptr << '\n'; 0494 } 0495 switch(reader->state()) { 0496 case Arguments::Finished: 0497 isDone = true; 0498 break; 0499 default: 0500 defaultReadToWrite(reader, &writer); 0501 break; 0502 } 0503 } 0504 0505 Arguments copy = writer.finish(); 0506 verifyAfterRoundtrip(original, *reader, copy, writer, debugPrint); 0507 delete reader; 0508 } 0509 0510 static void doRoundtripWithWriterCopy(const Arguments &original, uint32 dataIncrement, bool debugPrint) 0511 { 0512 Arguments::Reader reader(original); 0513 Arguments::Writer *writer = new Arguments::Writer; 0514 0515 bool isDone = false; 0516 uint32 i = 0; 0517 0518 while (!isDone) { 0519 TEST(writer->state() != Arguments::InvalidData); 0520 if (i++ == dataIncrement) { 0521 Arguments::Writer *copy = new Arguments::Writer(*writer); 0522 delete writer; 0523 writer = copy; 0524 } 0525 if (debugPrint) { 0526 std::cout << "Reader state: " << reader.stateString().ptr << '\n'; 0527 } 0528 switch(reader.state()) { 0529 case Arguments::Finished: 0530 isDone = true; 0531 break; 0532 default: 0533 defaultReadToWrite(&reader, writer); 0534 break; 0535 } 0536 } 0537 0538 Arguments copy = writer->finish(); 0539 verifyAfterRoundtrip(original, reader, copy, *writer, debugPrint); 0540 delete writer; 0541 } 0542 0543 static void doRoundtripForReal(const Arguments &original, uint32 dataIncrement, bool debugPrint) 0544 { 0545 doRoundtripWithShortReads(original, dataIncrement, debugPrint); 0546 doRoundtripWithReaderCopy(original, dataIncrement, debugPrint); 0547 doRoundtripWithWriterCopy(original, dataIncrement, debugPrint); 0548 } 0549 0550 // not returning by value to avoid the move constructor or assignment operator - 0551 // those should have separate tests 0552 static Arguments *shallowCopy(const Arguments &original) 0553 { 0554 // File descriptors can't do shallow copies - don't care for now, file descriptors are an identity 0555 // type, not a value type (and therefore don't fit well into the whole data model), and in the vast 0556 // majority of messages there aren't any. 0557 cstring signature = original.signature(); 0558 chunk data = original.data(); 0559 return new Arguments(nullptr, signature, data, original.fileDescriptors()); 0560 } 0561 0562 static void shallowAssign(Arguments *copy, const Arguments &original) 0563 { 0564 cstring signature = original.signature(); 0565 chunk data = original.data(); 0566 *copy = Arguments(nullptr, signature, data, original.fileDescriptors()); 0567 } 0568 0569 static void doRoundtripWithCopyAssignEtc(const Arguments &arg_in, uint32 dataIncrement, bool debugPrint) 0570 { 0571 { 0572 // just pass through 0573 doRoundtripForReal(arg_in, dataIncrement, debugPrint); 0574 } 0575 { 0576 // shallow copy 0577 Arguments *shallowDuplicate = shallowCopy(arg_in); 0578 doRoundtripForReal(*shallowDuplicate, dataIncrement, debugPrint); 0579 delete shallowDuplicate; 0580 } 0581 { 0582 // assignment from shallow copy 0583 Arguments shallowAssigned; 0584 shallowAssign(&shallowAssigned, arg_in); 0585 doRoundtripForReal(shallowAssigned, dataIncrement, debugPrint); 0586 } 0587 { 0588 // deep copy 0589 Arguments original(arg_in); 0590 doRoundtripForReal(original, dataIncrement, debugPrint); 0591 } 0592 { 0593 // move construction from shallow copy 0594 Arguments *shallowDuplicate = shallowCopy(arg_in); 0595 Arguments shallowMoveConstructed(std::move(*shallowDuplicate)); 0596 doRoundtripForReal(shallowMoveConstructed, dataIncrement, debugPrint); 0597 delete shallowDuplicate; 0598 } 0599 { 0600 // move assignment (hopefully, may the compiler optimize this to move-construction?) from shallow copy 0601 Arguments *shallowDuplicate = shallowCopy(arg_in); 0602 Arguments shallowMoveAssigned; 0603 shallowMoveAssigned = std::move(*shallowDuplicate); 0604 doRoundtripForReal(shallowMoveAssigned, dataIncrement, debugPrint); 0605 delete shallowDuplicate; 0606 } 0607 { 0608 // move construction from deep copy 0609 Arguments duplicate(arg_in); 0610 Arguments moveConstructed(std::move(duplicate)); 0611 doRoundtripForReal(moveConstructed, dataIncrement, debugPrint); 0612 } 0613 { 0614 // move assignment (hopefully, may the compiler optimize this to move-construction?) from deep copy 0615 Arguments duplicate(arg_in); 0616 Arguments moveAssigned; 0617 moveAssigned = std::move(duplicate); 0618 doRoundtripForReal(moveAssigned, dataIncrement, debugPrint); 0619 } 0620 } 0621 0622 static void doRoundtrip(const Arguments &arg, bool debugPrint = false) 0623 { 0624 const uint32 maxIncrement = arg.data().length; 0625 for (uint32 i = 1; i <= maxIncrement; i++) { 0626 doRoundtripWithCopyAssignEtc(arg, i, debugPrint); 0627 } 0628 0629 testReadWithSkip(arg, debugPrint); 0630 } 0631 0632 0633 0634 // Tests proper 0635 0636 0637 0638 static void test_stringValidation() 0639 { 0640 { 0641 cstring emptyWithNull(""); 0642 cstring emptyWithoutNull; 0643 0644 TEST(!Arguments::isStringValid(emptyWithoutNull)); 0645 TEST(Arguments::isStringValid(emptyWithNull)); 0646 0647 TEST(!Arguments::isObjectPathValid(emptyWithoutNull)); 0648 TEST(!Arguments::isObjectPathValid(emptyWithNull)); 0649 0650 TEST(Arguments::isSignatureValid(emptyWithNull)); 0651 TEST(!Arguments::isSignatureValid(emptyWithoutNull)); 0652 TEST(!Arguments::isSignatureValid(emptyWithNull, Arguments::VariantSignature)); 0653 TEST(!Arguments::isSignatureValid(emptyWithoutNull, Arguments::VariantSignature)); 0654 } 0655 { 0656 cstring trivial("i"); 0657 TEST(Arguments::isSignatureValid(trivial)); 0658 TEST(Arguments::isSignatureValid(trivial, Arguments::VariantSignature)); 0659 } 0660 { 0661 cstring list("iqb"); 0662 TEST(Arguments::isSignatureValid(list)); 0663 TEST(!Arguments::isSignatureValid(list, Arguments::VariantSignature)); 0664 cstring list2("aii"); 0665 TEST(Arguments::isSignatureValid(list2)); 0666 TEST(!Arguments::isSignatureValid(list2, Arguments::VariantSignature)); 0667 } 0668 { 0669 cstring simpleArray("ai"); 0670 TEST(Arguments::isSignatureValid(simpleArray)); 0671 TEST(Arguments::isSignatureValid(simpleArray, Arguments::VariantSignature)); 0672 } 0673 { 0674 cstring messyArray("a(iaia{ia{iv}})"); 0675 TEST(Arguments::isSignatureValid(messyArray)); 0676 TEST(Arguments::isSignatureValid(messyArray, Arguments::VariantSignature)); 0677 } 0678 { 0679 cstring dictFail("a{vi}"); 0680 TEST(!Arguments::isSignatureValid(dictFail)); 0681 TEST(!Arguments::isSignatureValid(dictFail, Arguments::VariantSignature)); 0682 } 0683 { 0684 cstring emptyStruct("()"); 0685 TEST(!Arguments::isSignatureValid(emptyStruct)); 0686 TEST(!Arguments::isSignatureValid(emptyStruct, Arguments::VariantSignature)); 0687 cstring emptyStruct2("(())"); 0688 TEST(!Arguments::isSignatureValid(emptyStruct2)); 0689 TEST(!Arguments::isSignatureValid(emptyStruct2, Arguments::VariantSignature)); 0690 cstring miniStruct("(t)"); 0691 TEST(Arguments::isSignatureValid(miniStruct)); 0692 TEST(Arguments::isSignatureValid(miniStruct, Arguments::VariantSignature)); 0693 cstring badStruct("((i)"); 0694 TEST(!Arguments::isSignatureValid(badStruct)); 0695 TEST(!Arguments::isSignatureValid(badStruct, Arguments::VariantSignature)); 0696 cstring badStruct2("(i))"); 0697 TEST(!Arguments::isSignatureValid(badStruct2)); 0698 TEST(!Arguments::isSignatureValid(badStruct2, Arguments::VariantSignature)); 0699 } 0700 { 0701 cstring nullStr; 0702 cstring emptyStr(""); 0703 TEST(!Arguments::isObjectPathValid(nullStr)); 0704 TEST(!Arguments::isObjectPathValid(emptyStr)); 0705 TEST(Arguments::isObjectPathValid(cstring("/"))); 0706 TEST(!Arguments::isObjectPathValid(cstring("/abc/"))); 0707 TEST(Arguments::isObjectPathValid(cstring("/abc"))); 0708 TEST(Arguments::isObjectPathValid(cstring("/abc/def"))); 0709 TEST(!Arguments::isObjectPathValid(cstring("/abc&def"))); 0710 TEST(!Arguments::isObjectPathValid(cstring("/abc//def"))); 0711 TEST(Arguments::isObjectPathValid(cstring("/aZ/0123_zAZa9_/_"))); 0712 } 0713 { 0714 cstring maxStruct("((((((((((((((((((((((((((((((((i" 0715 "))))))))))))))))))))))))))))))))"); 0716 TEST(Arguments::isSignatureValid(maxStruct)); 0717 TEST(Arguments::isSignatureValid(maxStruct, Arguments::VariantSignature)); 0718 cstring struct33("(((((((((((((((((((((((((((((((((i" // too much nesting by one 0719 ")))))))))))))))))))))))))))))))))"); 0720 TEST(!Arguments::isSignatureValid(struct33)); 0721 TEST(!Arguments::isSignatureValid(struct33, Arguments::VariantSignature)); 0722 0723 cstring maxArray("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaai"); 0724 TEST(Arguments::isSignatureValid(maxArray)); 0725 TEST(Arguments::isSignatureValid(maxArray, Arguments::VariantSignature)); 0726 cstring array33("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaai"); 0727 TEST(!Arguments::isSignatureValid(array33)); 0728 TEST(!Arguments::isSignatureValid(array33, Arguments::VariantSignature)); 0729 } 0730 } 0731 0732 static void test_nesting() 0733 { 0734 { 0735 Arguments::Writer writer; 0736 for (int i = 0; i < 32; i++) { 0737 writer.beginArray(); 0738 } 0739 TEST(writer.state() != Arguments::InvalidData); 0740 writer.beginArray(); 0741 TEST(writer.state() == Arguments::InvalidData); 0742 } 0743 { 0744 Arguments::Writer writer; 0745 for (int i = 0; i < 32; i++) { 0746 writer.beginDict(); 0747 maybeBeginDictEntry(&writer); 0748 writer.writeInt32(i); // key, next nested dict is value 0749 } 0750 TEST(writer.state() != Arguments::InvalidData); 0751 writer.beginStruct(); 0752 TEST(writer.state() == Arguments::InvalidData); 0753 } 0754 { 0755 Arguments::Writer writer; 0756 for (int i = 0; i < 32; i++) { 0757 writer.beginDict(); 0758 maybeBeginDictEntry(&writer); 0759 writer.writeInt32(i); // key, next nested dict is value 0760 } 0761 TEST(writer.state() != Arguments::InvalidData); 0762 writer.beginArray(); 0763 TEST(writer.state() == Arguments::InvalidData); 0764 } 0765 { 0766 Arguments::Writer writer; 0767 for (int i = 0; i < 64; i++) { 0768 writer.beginVariant(); 0769 } 0770 TEST(writer.state() != Arguments::InvalidData); 0771 writer.beginVariant(); 0772 TEST(writer.state() == Arguments::InvalidData); 0773 } 0774 } 0775 0776 struct LengthPrefixedData 0777 { 0778 uint32 length; 0779 byte data[256]; 0780 }; 0781 0782 static void test_roundtrip() 0783 { 0784 doRoundtrip(Arguments(nullptr, cstring(""), chunk())); 0785 { 0786 byte data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 0787 doRoundtrip(Arguments(nullptr, cstring("i"), chunk(data, 4))); 0788 doRoundtrip(Arguments(nullptr, cstring("yyyy"), chunk(data, 4))); 0789 doRoundtrip(Arguments(nullptr, cstring("iy"), chunk(data, 5))); 0790 doRoundtrip(Arguments(nullptr, cstring("iiy"), chunk(data, 9))); 0791 doRoundtrip(Arguments(nullptr, cstring("nquy"), chunk(data, 9))); 0792 doRoundtrip(Arguments(nullptr, cstring("unqy"), chunk(data, 9))); 0793 doRoundtrip(Arguments(nullptr, cstring("nqy"), chunk(data, 5))); 0794 doRoundtrip(Arguments(nullptr, cstring("qny"), chunk(data, 5))); 0795 doRoundtrip(Arguments(nullptr, cstring("yyny"), chunk(data, 5))); 0796 doRoundtrip(Arguments(nullptr, cstring("qyyy"), chunk(data, 5))); 0797 doRoundtrip(Arguments(nullptr, cstring("d"), chunk(data, 8))); 0798 doRoundtrip(Arguments(nullptr, cstring("dy"), chunk(data, 9))); 0799 doRoundtrip(Arguments(nullptr, cstring("x"), chunk(data, 8))); 0800 doRoundtrip(Arguments(nullptr, cstring("xy"), chunk(data, 9))); 0801 doRoundtrip(Arguments(nullptr, cstring("t"), chunk(data, 8))); 0802 doRoundtrip(Arguments(nullptr, cstring("ty"), chunk(data, 9))); 0803 } 0804 { 0805 LengthPrefixedData testArray = {0, {0}}; 0806 for (int i = 0; i < 64; i++) { 0807 testArray.data[i] = i; 0808 } 0809 byte *testData = reinterpret_cast<byte *>(&testArray); 0810 0811 testArray.length = 1; 0812 doRoundtrip(Arguments(nullptr, cstring("ay"), chunk(testData, 5))); 0813 testArray.length = 4; 0814 doRoundtrip(Arguments(nullptr, cstring("ai"), chunk(testData, 8))); 0815 testArray.length = 8; 0816 doRoundtrip(Arguments(nullptr, cstring("ai"), chunk(testData, 12))); 0817 testArray.length = 64; 0818 doRoundtrip(Arguments(nullptr, cstring("ai"), chunk(testData, 68))); 0819 doRoundtrip(Arguments(nullptr, cstring("an"), chunk(testData, 68))); 0820 0821 testArray.data[0] = 0; testArray.data[1] = 0; // zero out padding 0822 testArray.data[2] = 0; testArray.data[3] = 0; 0823 testArray.length = 56; 0824 doRoundtrip(Arguments(nullptr, cstring("ad"), chunk(testData, 64))); 0825 } 0826 { 0827 LengthPrefixedData testString; 0828 for (int i = 0; i < 200; i++) { 0829 testString.data[i] = 'A' + i % 53; // stay in the 7-bit ASCII range 0830 } 0831 testString.data[200] = '\0'; 0832 testString.length = 200; 0833 byte *testData = reinterpret_cast<byte *>(&testString); 0834 doRoundtrip(Arguments(nullptr, cstring("s"), chunk(testData, 205))); 0835 } 0836 { 0837 LengthPrefixedData testDict; 0838 testDict.length = 2; 0839 testDict.data[0] = 0; testDict.data[1] = 0; // zero padding; dict entries are always 8-aligned. 0840 testDict.data[2] = 0; testDict.data[3] = 0; 0841 0842 testDict.data[4] = 23; 0843 testDict.data[5] = 42; 0844 byte *testData = reinterpret_cast<byte *>(&testDict); 0845 doRoundtrip(Arguments(nullptr, cstring("a{yy}"), chunk(testData, 10))); 0846 } 0847 { 0848 byte testData[36] = { 0849 5, // variant signature length 0850 '(', 'y', 'g', 'd', ')', '\0', // signature: struct of: byte, signature (easiest because 0851 // its length prefix is byte order independent), double 0852 0, // pad to 8-byte boundary for struct 0853 23, // the byte 0854 6, 'i', 'a', '{', 'i', 'v', '}', '\0', // the signature 0855 0, 0, 0, 0, 0, 0, 0, // padding to 24 bytes (next 8-byte boundary) 0856 1, 2, 3, 4, 5, 6, 7, 8, // the double 0857 20, 21, 22, 23 // the int (not part of the variant) 0858 }; 0859 doRoundtrip(Arguments(nullptr, cstring("vi"), chunk(testData, sizeof(testData)))); 0860 } 0861 { 0862 // Spec says: alignment padding after array length, even if the array contains no data. Test this 0863 // with different types and alignment situations. 0864 byte testData[40] = { 0865 0, 0, 0, 0, // length of array of uint64s - zero 0866 0, 0, 0, 0, // alignment padding to 8 bytes (= natural alignment of uint64) 0867 // ... zero uint64s ... 0868 1, 2, 3, 4, // a uint32 to change the alignment, just to test 0869 0, 0, 0, 0, // length of array of int64s - zero 0870 // no alignment padding needed here 0871 0, 0, 0, 0, // length of dict {uint32, uint32} - zero 0872 0, 0, 0, 0, // alignment padding to 8 bytes (= alignment of dict entry) 0873 // some data (single bytes) between the arrays to prevent all those zeros from accidentally 0874 // looking valid when the Reader is confused. Also upset the alignment a bit. 0875 101, 102, 103, 104, 105, 0876 0, 0, 0, // padding to alignment of array size 0877 0, 0, 0, 0, // length of array of structs - zero 0878 0, 0, 0, 0 // alignment padding to 8 bytes (= alignment of struct) 0879 }; 0880 doRoundtrip(Arguments(nullptr, cstring("atuaxa{uu}yyyyya(u)"), chunk(testData, sizeof(testData)))); 0881 } 0882 } 0883 0884 static void test_writerMisuse() 0885 { 0886 // Array 0887 { 0888 Arguments::Writer writer; 0889 writer.beginArray(); 0890 writer.endArray(); // wrong, must contain exactly one type 0891 TEST(writer.state() == Arguments::InvalidData); 0892 } 0893 { 0894 Arguments::Writer writer; 0895 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 0896 writer.endArray(); // even with no elements it, must contain exactly one type 0897 TEST(writer.state() == Arguments::InvalidData); 0898 } 0899 { 0900 Arguments::Writer writer; 0901 writer.beginArray(); 0902 writer.writeByte(1); 0903 writer.endArray(); 0904 TEST(writer.state() != Arguments::InvalidData); 0905 } 0906 { 0907 Arguments::Writer writer; 0908 writer.beginArray(); 0909 writer.endArray(); // wrong, must contain exactly one type 0910 TEST(writer.state() == Arguments::InvalidData); 0911 } 0912 { 0913 Arguments::Writer writer; 0914 writer.beginArray(); 0915 writer.writeByte(1); 0916 writer.writeUint16(2); // wrong, different from first element 0917 TEST(writer.state() == Arguments::InvalidData); 0918 } 0919 { 0920 Arguments::Writer writer; 0921 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 0922 writer.beginVariant(); 0923 writer.endVariant(); // empty variants are okay if and only if inside an empty array 0924 writer.endArray(); 0925 TEST(writer.state() != Arguments::InvalidData); 0926 } 0927 // Dict 0928 { 0929 Arguments::Writer writer; 0930 writer.beginDict(); 0931 writer.endDict(); // wrong, must contain exactly two types 0932 TEST(writer.state() == Arguments::InvalidData); 0933 } 0934 { 0935 Arguments::Writer writer; 0936 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 0937 writer.endDict(); // wrong, must contain exactly two types 0938 TEST(writer.state() == Arguments::InvalidData); 0939 } 0940 { 0941 Arguments::Writer writer; 0942 writer.beginDict(); 0943 writer.writeByte(1); 0944 writer.endDict(); // wrong, must contain exactly two types 0945 TEST(writer.state() == Arguments::InvalidData); 0946 } 0947 { 0948 Arguments::Writer writer; 0949 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 0950 writer.writeByte(1); 0951 writer.endDict(); // wrong, must contain exactly two types 0952 TEST(writer.state() == Arguments::InvalidData); 0953 } 0954 { 0955 Arguments::Writer writer; 0956 writer.beginDict(); 0957 maybeBeginDictEntry(&writer); 0958 writer.writeByte(1); 0959 writer.writeByte(2); 0960 maybeEndDictEntry(&writer); 0961 writer.endDict(); 0962 TEST(writer.state() != Arguments::InvalidData); 0963 } 0964 { 0965 Arguments::Writer writer; 0966 writer.beginDict(); 0967 maybeBeginDictEntry(&writer); 0968 writer.writeByte(1); 0969 writer.writeByte(2); 0970 maybeEndDictEntry(&writer); 0971 // second key-value pair 0972 maybeBeginDictEntry(&writer); 0973 TEST(writer.state() != Arguments::InvalidData); 0974 writer.writeUint16(3); // wrong, incompatible with first element 0975 TEST(writer.state() == Arguments::InvalidData); 0976 } 0977 { 0978 Arguments::Writer writer; 0979 writer.beginDict(); 0980 maybeBeginDictEntry(&writer); 0981 writer.writeByte(1); 0982 writer.writeByte(2); 0983 maybeEndDictEntry(&writer); 0984 // second key-value pair 0985 maybeBeginDictEntry(&writer); 0986 writer.writeByte(3); 0987 TEST(writer.state() != Arguments::InvalidData); 0988 writer.writeUint16(4); // wrong, incompatible with first element 0989 TEST(writer.state() == Arguments::InvalidData); 0990 } 0991 0992 { 0993 Arguments::Writer writer; 0994 writer.beginDict(); 0995 maybeBeginDictEntry(&writer); 0996 writer.beginVariant(); // wrong, key type must be basic 0997 TEST(writer.state() == Arguments::InvalidData); 0998 } 0999 // Variant 1000 { 1001 // this and the next are a baseline to make sure that the following test fails for a good reason 1002 Arguments::Writer writer; 1003 writer.beginVariant(); 1004 writer.writeByte(1); 1005 writer.endVariant(); 1006 TEST(writer.state() != Arguments::InvalidData); 1007 } 1008 { 1009 Arguments::Writer writer; 1010 writer.beginVariant(); 1011 writer.endVariant(); 1012 TEST(writer.state() == Arguments::InvalidData); 1013 } 1014 { 1015 Arguments::Writer writer; 1016 writer.beginVariant(); 1017 writer.writeByte(1); 1018 writer.writeByte(2); // wrong, a variant may contain only one or zero single complete types 1019 TEST(writer.state() == Arguments::InvalidData); 1020 } 1021 { 1022 Arguments::Writer writer; 1023 writer.beginStruct(); 1024 writer.writeByte(1); 1025 TEST(writer.state() != Arguments::InvalidData); 1026 Arguments arg = writer.finish(); 1027 TEST(writer.state() == Arguments::InvalidData); // can't finish while inside an aggregate 1028 TEST(arg.signature().length == 0); // should not be written on error 1029 } 1030 } 1031 1032 static void addSomeVariantStuff(Arguments::Writer *writer) 1033 { 1034 // maybe should have typed the following into hackertyper.com to make it look more "legit" ;) 1035 static const char *aVeryLongString = "ujfgosuideuvcevfgeoauiyetoraedtmzaubeodtraueonuljfgonuiljofnuilojf" 1036 "0ij948h534ownlyejglunh4owny9hw3v9woni09ulgh4wuvc<l9foehujfigosuij" 1037 "ofgnua0j3409k0ae9nyatrnoadgiaeh0j98hejuohslijolsojiaeojaufhesoujh"; 1038 writer->beginVariant(); 1039 writer->beginVariant(); 1040 writer->beginVariant(); 1041 writer->beginStruct(); 1042 writer->writeString(cstring("Smoerebroed smoerebroed")); 1043 writer->beginStruct(); 1044 writer->writeString(cstring(aVeryLongString)); 1045 writer->writeString(cstring("Bork bork bork")); 1046 writer->beginVariant(); 1047 writer->beginStruct(); 1048 writer->writeString(cstring("Quite nesty")); 1049 writer->writeObjectPath(cstring("/path/to/object")); 1050 writer->writeUint64(234234234); 1051 writer->writeByte(2); 1052 writer->writeUint64(234234223434); 1053 writer->writeUint16(34); 1054 writer->endStruct(); 1055 writer->endVariant(); 1056 writer->beginStruct(); 1057 writer->writeByte(34); 1058 writer->endStruct(); 1059 writer->endStruct(); 1060 writer->writeString(cstring("Another string")); 1061 writer->endStruct(); 1062 writer->endVariant(); 1063 writer->endVariant(); 1064 writer->endVariant(); 1065 } 1066 1067 static void test_complicated() 1068 { 1069 Arguments arg; 1070 { 1071 Arguments::Writer writer; 1072 // NeedMoreData-related bugs are less dangerous inside arrays, so we try to provoke one here; 1073 // the reason for arrays preventing failures is that they have a length prefix which enables 1074 // and encourages pre-fetching all the array's data before processing *anything* inside the 1075 // array. therefore no NeedMoreData state happens while really deserializing the array's 1076 // contents. but we exactly want NeedMoreData while in the middle of deserializing something 1077 // meaty, specifically variants. see Reader::replaceData(). 1078 addSomeVariantStuff(&writer); 1079 1080 writer.writeInt64(234234); 1081 writer.writeByte(115); 1082 writer.beginVariant(); 1083 writer.beginDict(); 1084 maybeBeginDictEntry(&writer); 1085 writer.writeByte(23); 1086 writer.beginVariant(); 1087 writer.writeString(cstring("twenty-three")); 1088 writer.endVariant(); 1089 maybeEndDictEntry(&writer); 1090 // key-value pair 2 1091 maybeBeginDictEntry(&writer); 1092 writer.writeByte(83); 1093 writer.beginVariant(); 1094 writer.writeObjectPath(cstring("/foo/bar/object")); 1095 writer.endVariant(); 1096 maybeEndDictEntry(&writer); 1097 // key-value pair 3 1098 maybeBeginDictEntry(&writer); 1099 writer.writeByte(234); 1100 writer.beginVariant(); 1101 writer.beginArray(); 1102 writer.writeUint16(234); 1103 writer.writeUint16(234); 1104 writer.writeUint16(234); 1105 writer.endArray(); 1106 writer.endVariant(); 1107 maybeEndDictEntry(&writer); 1108 // key-value pair 4 1109 maybeBeginDictEntry(&writer); 1110 writer.writeByte(25); 1111 writer.beginVariant(); 1112 addSomeVariantStuff(&writer); 1113 writer.endVariant(); 1114 maybeEndDictEntry(&writer); 1115 writer.endDict(); 1116 writer.endVariant(); 1117 writer.writeString("Hello D-Bus!"); 1118 writer.beginArray(); 1119 writer.writeDouble(1.567898); 1120 writer.writeDouble(1.523428); 1121 writer.writeDouble(1.621133); 1122 writer.writeDouble(1.982342); 1123 writer.endArray(); 1124 TEST(writer.state() != Arguments::InvalidData); 1125 arg = writer.finish(); 1126 TEST(writer.state() != Arguments::InvalidData); 1127 } 1128 doRoundtrip(arg); 1129 } 1130 1131 static void test_alignment() 1132 { 1133 { 1134 Arguments::Writer writer; 1135 writer.writeByte(123); 1136 writer.beginArray(); 1137 writer.writeByte(64); 1138 writer.endArray(); 1139 for (int i = 123; i < 150; i++) { 1140 writer.writeByte(i); 1141 } 1142 1143 TEST(writer.state() != Arguments::InvalidData); 1144 Arguments arg = writer.finish(); 1145 TEST(writer.state() != Arguments::InvalidData); 1146 doRoundtrip(arg); 1147 } 1148 { 1149 Arguments::Writer writer; 1150 writer.writeByte(123); 1151 writer.beginStruct(); 1152 writer.writeByte(110); 1153 writer.endStruct(); 1154 writer.writeByte(200); 1155 Arguments arg = writer.finish(); 1156 doRoundtrip(arg); 1157 } 1158 } 1159 1160 static void test_arrayOfVariant() 1161 { 1162 // non-empty array 1163 { 1164 Arguments::Writer writer; 1165 writer.writeByte(123); 1166 writer.beginArray(); 1167 writer.beginVariant(); 1168 writer.writeByte(64); 1169 writer.endVariant(); 1170 writer.endArray(); 1171 writer.writeByte(123); 1172 1173 TEST(writer.state() != Arguments::InvalidData); 1174 Arguments arg = writer.finish(); 1175 TEST(writer.state() != Arguments::InvalidData); 1176 doRoundtrip(arg); 1177 } 1178 // empty array 1179 { 1180 Arguments::Writer writer; 1181 writer.writeByte(123); 1182 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1183 writer.beginVariant(); 1184 writer.endVariant(); 1185 writer.endArray(); 1186 writer.writeByte(123); 1187 1188 TEST(writer.state() != Arguments::InvalidData); 1189 Arguments arg = writer.finish(); 1190 TEST(writer.state() != Arguments::InvalidData); 1191 doRoundtrip(arg); 1192 } 1193 } 1194 1195 static void test_realMessage() 1196 { 1197 Arguments arg; 1198 // non-empty array 1199 { 1200 Arguments::Writer writer; 1201 1202 writer.writeString(cstring("message")); 1203 writer.writeString(cstring("konversation")); 1204 1205 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1206 writer.beginVariant(); 1207 writer.endVariant(); 1208 writer.endArray(); 1209 1210 writer.writeString(cstring("")); 1211 writer.writeString(cstring("<fredrikh> he's never on irc")); 1212 1213 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1214 writer.writeByte(123); // may not show up in the output 1215 writer.endArray(); 1216 1217 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1218 writer.writeString(cstring("dummy, I may not show up in the output!")); 1219 writer.endArray(); 1220 1221 writer.writeInt32(-1); 1222 writer.writeInt64(46137372); 1223 1224 TEST(writer.state() != Arguments::InvalidData); 1225 arg = writer.finish(); 1226 TEST(writer.state() != Arguments::InvalidData); 1227 } 1228 doRoundtrip(arg); 1229 } 1230 1231 static void test_isWritingSignatureBug() 1232 { 1233 { 1234 // This was the original test, so it's the one with the comments :) 1235 Arguments::Writer writer; 1236 writer.beginArray(); 1237 writer.beginStruct(); 1238 writer.beginDict(); 1239 maybeBeginDictEntry(&writer); 1240 writer.writeByte(1); 1241 writer.writeByte(2); 1242 maybeEndDictEntry(&writer); 1243 writer.endDict(); 1244 // Must add more stuff after the inner dict to ensure that the signature position of the 1245 // dict's value is well inside the existing signature in the second dict entry. 1246 // See isWritingSignature in Writer::advanceState(). 1247 writer.writeUint16(1); 1248 writer.writeUint16(2); 1249 writer.endStruct(); 1250 writer.beginStruct(); 1251 writer.beginDict(); 1252 maybeBeginDictEntry(&writer); 1253 writer.writeByte(1); 1254 writer.writeByte(2); 1255 maybeEndDictEntry(&writer); 1256 // In the second pass, we are definitely NOT writing a new part of the dict signature, 1257 // which used to go (that was the bug!!) through a different code path in 1258 // Arguments::Writer::advanceState(). 1259 maybeBeginDictEntry(&writer); 1260 writer.writeByte(1); 1261 TEST(writer.state() != Arguments::InvalidData); 1262 writer.writeUint16(2); 1263 TEST(writer.state() == Arguments::InvalidData); 1264 } 1265 { 1266 // For completeness, do the equivalent of the previous test with an array inside 1267 Arguments::Writer writer; 1268 writer.beginArray(); 1269 writer.beginStruct(); 1270 writer.beginArray(); 1271 writer.writeByte(1); 1272 writer.endArray(); 1273 writer.writeUint16(1); 1274 writer.writeUint16(2); 1275 writer.endStruct(); 1276 writer.beginStruct(); 1277 writer.beginArray(); 1278 writer.writeByte(1); 1279 writer.writeByte(1); 1280 TEST(writer.state() != Arguments::InvalidData); 1281 writer.writeUint16(2); 1282 TEST(writer.state() == Arguments::InvalidData); 1283 } 1284 } 1285 1286 static void writeValue(Arguments::Writer *writer, uint32 typeIndex, const void *value) 1287 { 1288 switch (typeIndex) { 1289 case 0: 1290 break; 1291 case 1: 1292 writer->writeByte(*static_cast<const byte *>(value)); break; 1293 case 2: 1294 writer->writeUint16(*static_cast<const uint16 *>(value)); break; 1295 case 3: 1296 writer->writeUint32(*static_cast<const uint32 *>(value)); break; 1297 case 4: 1298 writer->writeUint64(*static_cast<const uint64 *>(value)); break; 1299 default: 1300 TEST(false); 1301 } 1302 } 1303 1304 static bool checkValue(Arguments::Reader *reader, uint32 typeIndex, const void *expected) 1305 { 1306 switch (typeIndex) { 1307 case 0: 1308 return true; 1309 case 1: 1310 return reader->readByte() == *static_cast<const byte *>(expected); 1311 case 2: 1312 return reader->readUint16() == *static_cast<const uint16 *>(expected); 1313 case 3: 1314 return reader->readUint32() == *static_cast<const uint32 *>(expected); 1315 case 4: 1316 return reader->readUint64() == *static_cast<const uint64 *>(expected); 1317 default: 1318 TEST(false); 1319 } 1320 return false; 1321 } 1322 1323 static void test_primitiveArray() 1324 { 1325 // TODO also test some error cases 1326 1327 static const uint32 testDataSize = 16384; 1328 byte testData[testDataSize]; 1329 for (uint32 i = 0; i < testDataSize; i++) { 1330 testData[i] = i & 0xff; 1331 } 1332 1333 for (uint i = 0; i < 4; i++) { 1334 1335 const bool writeAsPrimitive = i & 0x1; 1336 const bool readAsPrimitive = i & 0x2; 1337 1338 static const uint32 arrayTypesCount = 5; 1339 // those types must be compatible with writeValue() and readValue() 1340 static Arguments::IoState arrayTypes[arrayTypesCount] = { 1341 Arguments::InvalidData, 1342 Arguments::Byte, 1343 Arguments::Uint16, 1344 Arguments::Uint32, 1345 Arguments::Uint64 1346 }; 1347 1348 for (uint otherType = 0; otherType < arrayTypesCount; otherType++) { 1349 1350 // an array with no type in it is ill-formed, so we start with 1 (Byte) 1351 for (uint typeInArray = 1; typeInArray < arrayTypesCount; typeInArray++) { 1352 1353 static const uint32 arraySizesCount = 12; 1354 static const uint32 arraySizes[arraySizesCount] = { 1355 0, 1356 1, 1357 2, 1358 3, 1359 4, 1360 7, 1361 8, 1362 9, 1363 511, 1364 512, 1365 513, 1366 2048 // dataSize / sizeof(uint64) == 2048 1367 }; 1368 1369 for (uint k = 0; k < arraySizesCount; k++) { 1370 1371 static const uint64_t otherValue = ~0llu; 1372 const uint32 arraySize = arraySizes[k]; 1373 const uint32 dataSize = arraySize << (typeInArray - 1); 1374 TEST(dataSize <= testDataSize); 1375 1376 Arguments arg; 1377 { 1378 Arguments::Writer writer; 1379 1380 // write something before the array to test different starting position alignments 1381 writeValue(&writer, otherType, &otherValue); 1382 1383 if (writeAsPrimitive) { 1384 writer.writePrimitiveArray(arrayTypes[typeInArray], chunk(testData, dataSize)); 1385 } else { 1386 writer.beginArray(arraySize ? Arguments::Writer::NonEmptyArray 1387 : Arguments::Writer::WriteTypesOfEmptyArray); 1388 byte *testDataPtr = testData; 1389 if (arraySize) { 1390 for (uint m = 0; m < arraySize; m++) { 1391 writeValue(&writer, typeInArray, testDataPtr); 1392 testDataPtr += 1 << (typeInArray - 1); 1393 } 1394 } else { 1395 writeValue(&writer, typeInArray, testDataPtr); 1396 } 1397 writer.endArray(); 1398 } 1399 1400 TEST(writer.state() != Arguments::InvalidData); 1401 // TEST(writer.state() == Arguments::AnyData); 1402 // TODO do we handle AnyData consistently, and do we really need it anyway? 1403 writeValue(&writer, otherType, &otherValue); 1404 TEST(writer.state() != Arguments::InvalidData); 1405 arg = writer.finish(); 1406 TEST(writer.state() == Arguments::Finished); 1407 } 1408 1409 { 1410 Arguments::Reader reader(arg); 1411 1412 TEST(checkValue(&reader, otherType, &otherValue)); 1413 1414 if (readAsPrimitive) { 1415 TEST(reader.state() == Arguments::BeginArray); 1416 std::pair<Arguments::IoState, chunk> ret = reader.readPrimitiveArray(); 1417 TEST(ret.first == arrayTypes[typeInArray]); 1418 TEST(chunksEqual(chunk(testData, dataSize), ret.second)); 1419 } else { 1420 TEST(reader.state() == Arguments::BeginArray); 1421 const bool hasData = reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); 1422 TEST(hasData == (arraySize != 0)); 1423 TEST(reader.state() != Arguments::InvalidData); 1424 byte *testDataPtr = testData; 1425 1426 if (arraySize) { 1427 for (uint m = 0; m < arraySize; m++) { 1428 TEST(reader.state() != Arguments::InvalidData); 1429 TEST(checkValue(&reader, typeInArray, testDataPtr)); 1430 TEST(reader.state() != Arguments::InvalidData); 1431 testDataPtr += 1 << (typeInArray - 1); 1432 } 1433 } else { 1434 TEST(reader.state() == arrayTypes[typeInArray]); 1435 // next: dummy read, necessary to move forward; value is ignored 1436 checkValue(&reader, typeInArray, testDataPtr); 1437 TEST(reader.state() != Arguments::InvalidData); 1438 } 1439 1440 TEST(reader.state() == Arguments::EndArray); 1441 reader.endArray(); 1442 TEST(reader.state() != Arguments::InvalidData); 1443 } 1444 1445 TEST(reader.state() != Arguments::InvalidData); 1446 TEST(checkValue(&reader, otherType, &otherValue)); 1447 TEST(reader.state() == Arguments::Finished); 1448 } 1449 1450 // the data generated here nicely stresses the empty array skipping code 1451 if (i == 0 && arraySize < 100) { 1452 testReadWithSkip(arg, false); 1453 } 1454 } 1455 } 1456 } 1457 } 1458 } 1459 1460 static void test_signatureLengths() 1461 { 1462 for (int i = 0; i <= 256; i++) { 1463 Arguments::Writer writer; 1464 for (int j = 0; j < i; j++) { 1465 writer.writeByte(255); 1466 } 1467 if (i == 256) { 1468 TEST(writer.state() == Arguments::InvalidData); 1469 break; 1470 } 1471 TEST(writer.state() != Arguments::InvalidData); 1472 Arguments arg = writer.finish(); 1473 TEST(writer.state() == Arguments::Finished); 1474 1475 // The full doRoundtrip() just here makes this whole file take several seconds to execute 1476 // instead of a fraction of a second. This way is much quicker. 1477 doRoundtripForReal(arg, 2048, false); 1478 Arguments argCopy = arg; 1479 doRoundtripForReal(argCopy, 2048, false); 1480 } 1481 for (int i = 1 /* variants may not be empty */; i <= 256; i++) { 1482 Arguments::Writer writer; 1483 1484 writer.beginVariant(); 1485 switch (i) { 1486 case 0: 1487 TEST(false); 1488 break; 1489 case 1: 1490 writer.writeByte(255); 1491 break; 1492 case 2: 1493 // "ay" signature is two letters 1494 writer.beginArray(); 1495 writer.writeByte(255); 1496 writer.endArray(); 1497 break; 1498 default: 1499 // (y), (yy), ... 1500 writer.beginStruct(); 1501 for (int j = strlen("()"); j < i; j++) { 1502 writer.writeByte(255); 1503 } 1504 writer.endStruct(); 1505 break; 1506 } 1507 writer.endVariant(); 1508 1509 if (i == 256) { 1510 TEST(writer.state() == Arguments::InvalidData); 1511 break; 1512 } 1513 TEST(writer.state() != Arguments::InvalidData); 1514 Arguments arg = writer.finish(); 1515 TEST(writer.state() == Arguments::Finished); 1516 1517 doRoundtripForReal(arg, 2048, false); 1518 Arguments argCopy = arg; 1519 doRoundtripForReal(argCopy, 2048, false); 1520 } 1521 } 1522 1523 static void test_emptyArrayAndDict() 1524 { 1525 // Arrays 1526 { 1527 Arguments::Writer writer; 1528 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1529 writer.writeByte(0); 1530 writer.endArray(); 1531 TEST(writer.state() != Arguments::InvalidData); 1532 Arguments arg = writer.finish(); 1533 TEST(writer.state() == Arguments::Finished); 1534 doRoundtrip(arg, false); 1535 } 1536 { 1537 Arguments::Writer writer; 1538 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1539 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1540 writer.writeByte(0); 1541 writer.endArray(); 1542 writer.endArray(); 1543 TEST(writer.state() != Arguments::InvalidData); 1544 Arguments arg = writer.finish(); 1545 TEST(writer.state() == Arguments::Finished); 1546 doRoundtrip(arg, false); 1547 } 1548 { 1549 Arguments::Writer writer; 1550 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1551 writer.beginStruct(); 1552 writer.writeByte(0); 1553 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1554 writer.writeByte(0); 1555 writer.endArray(); 1556 writer.endStruct(); 1557 writer.endArray(); 1558 TEST(writer.state() != Arguments::InvalidData); 1559 Arguments arg = writer.finish(); 1560 TEST(writer.state() == Arguments::Finished); 1561 doRoundtrip(arg, false); 1562 } 1563 { 1564 Arguments::Writer writer; 1565 writer.writeUint32(987654321); 1566 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1567 writer.beginStruct(); 1568 writer.writeDouble(0); 1569 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1570 writer.writeByte(0); 1571 writer.endArray(); 1572 writer.endStruct(); 1573 writer.endArray(); 1574 TEST(writer.state() != Arguments::InvalidData); 1575 Arguments arg = writer.finish(); 1576 TEST(writer.state() == Arguments::Finished); 1577 doRoundtrip(arg, false); 1578 } 1579 { 1580 Arguments::Writer writer; 1581 writer.writeString(cstring("xy")); 1582 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1583 writer.beginStruct(); 1584 writer.writeUint32(12345678); 1585 //It is implicitly clear that an array inside a nil array is also nil 1586 //writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1587 //TODO add a test for writing >1 element in nested empty array - I've tried that and it fails 1588 // like it should, but it needs a proper standalone test 1589 writer.beginArray(); 1590 writer.writeByte(0); 1591 writer.endArray(); 1592 writer.writeByte(12); 1593 writer.endStruct(); 1594 writer.endArray(); 1595 TEST(writer.state() != Arguments::InvalidData); 1596 Arguments arg = writer.finish(); 1597 TEST(writer.state() == Arguments::Finished); 1598 doRoundtrip(arg, false); 1599 } 1600 { 1601 Arguments::Writer writer; 1602 writer.writeString(cstring("xy")); 1603 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1604 writer.beginStruct(); 1605 writer.writeByte(123); 1606 writer.beginVariant(); 1607 writer.endVariant(); 1608 writer.endStruct(); 1609 writer.endArray(); 1610 TEST(writer.state() != Arguments::InvalidData); 1611 Arguments arg = writer.finish(); 1612 TEST(writer.state() == Arguments::Finished); 1613 doRoundtrip(arg, false); 1614 } 1615 { 1616 for (int i = 0; i < 8; i++) { 1617 Arguments::Writer writer; 1618 writer.beginStruct(); 1619 writer.writeByte(123); 1620 writer.beginArray(i ? Arguments::Writer::NonEmptyArray 1621 : Arguments::Writer::WriteTypesOfEmptyArray); 1622 for (int j = 0; j < std::max(i, 1); j++) { 1623 writer.writeUint16(52345); 1624 } 1625 writer.endArray(); 1626 writer.writeByte(123); 1627 writer.endStruct(); 1628 TEST(writer.state() != Arguments::InvalidData); 1629 Arguments arg = writer.finish(); 1630 TEST(writer.state() == Arguments::Finished); 1631 doRoundtrip(arg, false); 1632 } 1633 } 1634 for (int i = 0; i < 4; i++) { 1635 // Test RestartEmptyArrayToWriteTypes and writing an empty array inside the >1st iteration of another array 1636 Arguments::Writer writer; 1637 writer.beginArray((i & 2) ? Arguments::Writer::WriteTypesOfEmptyArray : Arguments::Writer::NonEmptyArray); 1638 // v don't care, the logic error is only in the second iteration 1639 writer.beginArray(Arguments::Writer::NonEmptyArray); 1640 writer.writeString(cstring("a")); 1641 writer.endArray(); 1642 if (i & 1) { 1643 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1644 } else { 1645 writer.beginArray(Arguments::Writer::NonEmptyArray); 1646 writer.beginArray(Arguments::Writer::RestartEmptyArrayToWriteTypes); 1647 } 1648 writer.writeString(cstring("a")); 1649 writer.endArray(); 1650 writer.endArray(); 1651 TEST(writer.state() != Arguments::InvalidData); 1652 Arguments arg = writer.finish(); 1653 TEST(writer.state() == Arguments::Finished); 1654 doRoundtrip(arg, false); 1655 } 1656 for (int i = 0; i < 3; i++) { 1657 // Test arrays inside empty arrays and especially peekPrimitiveArray / readPrimitiveArray 1658 Arguments::Writer writer; 1659 const bool outerEmpty = i > 1; 1660 const bool innerEmpty = i > 0; 1661 writer.beginArray(outerEmpty ? Arguments::Writer::WriteTypesOfEmptyArray 1662 : Arguments::Writer::NonEmptyArray); 1663 writer.beginArray(innerEmpty ? Arguments::Writer::WriteTypesOfEmptyArray 1664 : Arguments::Writer::NonEmptyArray); 1665 // Iterating several times through an empty array is allowed while writing 1666 writer.writeUint64(1234); 1667 writer.writeUint64(1234); 1668 TEST(writer.state() != Arguments::InvalidData); 1669 writer.endArray(); 1670 writer.endArray(); 1671 Arguments arg = writer.finish(); 1672 TEST(writer.state() == Arguments::Finished); 1673 { 1674 Arguments::Reader reader(arg); 1675 reader.beginArray(); 1676 if (outerEmpty) { 1677 TEST(reader.state() == Arguments::EndArray); 1678 reader.endArray(); 1679 } else { 1680 TEST(reader.state() == Arguments::BeginArray); // the inner array 1681 reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); 1682 TEST(reader.state() == Arguments::Uint64); 1683 reader.readUint64(); 1684 if (!innerEmpty) { 1685 reader.readUint64(); 1686 } 1687 TEST(reader.state() == Arguments::EndArray); 1688 reader.endArray(); 1689 reader.endArray(); 1690 } 1691 TEST(reader.state() == Arguments::Finished); 1692 } 1693 { 1694 Arguments::Reader reader(arg); 1695 TEST(reader.peekPrimitiveArray(Arguments::Reader::ReadTypesOnlyIfEmpty) == Arguments::BeginArray); 1696 reader.beginArray(Arguments::Reader::ReadTypesOnlyIfEmpty); 1697 TEST(reader.state() == Arguments::BeginArray); 1698 if (innerEmpty) { 1699 TEST(reader.peekPrimitiveArray() == Arguments::BeginArray); 1700 } else { 1701 TEST(reader.peekPrimitiveArray() == Arguments::Uint64); 1702 } 1703 TEST(reader.peekPrimitiveArray(Arguments::Reader::ReadTypesOnlyIfEmpty) == Arguments::Uint64); 1704 1705 std::pair<Arguments::IoState, chunk> array = reader.readPrimitiveArray(); 1706 TEST(array.first == Arguments::Uint64); 1707 if (innerEmpty) { 1708 TEST(array.second.ptr == nullptr); 1709 TEST(array.second.length == 0); 1710 } else { 1711 TEST(array.second.length == 2 * sizeof(uint64)); 1712 } 1713 reader.endArray(); 1714 TEST(reader.state() == Arguments::Finished); 1715 } 1716 } 1717 { 1718 for (int i = 0; i <= 32; i++) { 1719 Arguments::Writer writer; 1720 for (int j = 0; j <= i; j++) { 1721 writer.beginArray(Arguments::Writer::WriteTypesOfEmptyArray); 1722 if (j == 32) { 1723 TEST(writer.state() == Arguments::InvalidData); 1724 } 1725 } 1726 if (i == 32) { 1727 TEST(writer.state() == Arguments::InvalidData); 1728 break; 1729 } 1730 writer.writeUint16(52345); 1731 for (int j = 0; j <= i; j++) { 1732 writer.endArray(); 1733 } 1734 TEST(writer.state() != Arguments::InvalidData); 1735 Arguments arg = writer.finish(); 1736 TEST(writer.state() == Arguments::Finished); 1737 doRoundtrip(arg, false); 1738 } 1739 } 1740 1741 // Dicts 1742 1743 { 1744 Arguments::Writer writer; 1745 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 1746 maybeBeginDictEntry(&writer); 1747 writer.writeByte(0); 1748 writer.writeString(cstring("a")); 1749 maybeEndDictEntry(&writer); 1750 writer.endDict(); 1751 TEST(writer.state() != Arguments::InvalidData); 1752 Arguments arg = writer.finish(); 1753 TEST(writer.state() == Arguments::Finished); 1754 doRoundtrip(arg, false); 1755 } 1756 { 1757 Arguments::Writer writer; 1758 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 1759 maybeBeginDictEntry(&writer); 1760 writer.writeString(cstring("a")); 1761 writer.beginVariant(); 1762 writer.endVariant(); 1763 maybeEndDictEntry(&writer); 1764 writer.endDict(); 1765 TEST(writer.state() != Arguments::InvalidData); 1766 Arguments arg = writer.finish(); 1767 TEST(writer.state() == Arguments::Finished); 1768 doRoundtrip(arg, false); 1769 } 1770 { 1771 Arguments::Writer writer; 1772 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 1773 maybeBeginDictEntry(&writer); 1774 writer.writeString(cstring("a")); 1775 writer.beginVariant(); 1776 writer.endVariant(); 1777 maybeEndDictEntry(&writer); 1778 maybeBeginDictEntry(&writer); 1779 writer.writeString(cstring("a")); 1780 writer.beginVariant(); 1781 writer.endVariant(); 1782 maybeEndDictEntry(&writer); 1783 writer.endDict(); 1784 TEST(writer.state() != Arguments::InvalidData); 1785 Arguments arg = writer.finish(); 1786 TEST(writer.state() == Arguments::Finished); 1787 doRoundtrip(arg, false); 1788 } 1789 { 1790 Arguments::Writer writer; 1791 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 1792 maybeBeginDictEntry(&writer); 1793 writer.writeString(cstring("a")); 1794 writer.beginVariant(); 1795 TEST(writer.state() != Arguments::InvalidData); 1796 writer.writeByte(0); 1797 // variants in nil arrays may contain data but it will be discarded, i.e. there will only be an 1798 // empty variant in the output 1799 writer.endVariant(); 1800 maybeEndDictEntry(&writer); 1801 writer.endDict(); 1802 Arguments arg = writer.finish(); 1803 TEST(writer.state() == Arguments::Finished); 1804 doRoundtrip(arg, false); 1805 } 1806 for (int i = 0; i < 4; i++) { 1807 // Test RestartEmptyArrayToWriteTypes and writing an empty dict inside the >1st iteration of another dict 1808 Arguments::Writer writer; 1809 writer.beginDict((i & 2) ? Arguments::Writer::WriteTypesOfEmptyArray : Arguments::Writer::NonEmptyArray); 1810 maybeBeginDictEntry(&writer); 1811 writer.writeString(cstring("a")); 1812 // v don't care, the logic error is only in the second iteration 1813 writer.beginDict(Arguments::Writer::NonEmptyArray); 1814 maybeBeginDictEntry(&writer); 1815 writer.writeString(cstring("a")); 1816 writer.writeInt32(1234); 1817 maybeEndDictEntry(&writer); 1818 writer.endDict(); 1819 maybeEndDictEntry(&writer); 1820 maybeBeginDictEntry(&writer); 1821 writer.writeString(cstring("a")); 1822 if (i & 1) { 1823 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 1824 maybeBeginDictEntry(&writer); 1825 } else { 1826 writer.beginDict(Arguments::Writer::NonEmptyArray); 1827 writer.beginDict(Arguments::Writer::RestartEmptyArrayToWriteTypes); 1828 maybeBeginDictEntry(&writer); 1829 } 1830 writer.writeString(cstring("a")); 1831 writer.writeInt32(1234); 1832 maybeEndDictEntry(&writer); 1833 writer.endDict(); 1834 maybeEndDictEntry(&writer); 1835 writer.endDict(); 1836 TEST(writer.state() != Arguments::InvalidData); 1837 Arguments arg = writer.finish(); 1838 TEST(writer.state() == Arguments::Finished); 1839 doRoundtrip(arg, false); 1840 } 1841 { 1842 for (int i = 0; i <= 32; i++) { 1843 Arguments::Writer writer; 1844 for (int j = 0; j <= i; j++) { 1845 writer.beginDict(Arguments::Writer::WriteTypesOfEmptyArray); 1846 maybeBeginDictEntry(&writer); 1847 if (j == 32) { 1848 TEST(writer.state() == Arguments::InvalidData); 1849 } 1850 writer.writeUint16(12345); 1851 } 1852 if (i == 32) { 1853 TEST(writer.state() == Arguments::InvalidData); 1854 break; 1855 } 1856 writer.writeUint16(52345); 1857 for (int j = 0; j <= i; j++) { 1858 maybeEndDictEntry(&writer); 1859 writer.endDict(); 1860 } 1861 TEST(writer.state() != Arguments::InvalidData); 1862 Arguments arg = writer.finish(); 1863 TEST(writer.state() == Arguments::Finished); 1864 doRoundtrip(arg, false); 1865 } 1866 } 1867 } 1868 1869 static void test_fileDescriptors() 1870 { 1871 #ifdef __unix__ 1872 { 1873 Arguments::Writer writer; 1874 writer.writeUnixFd(200); 1875 writer.writeByte(12); 1876 writer.writeUnixFd(1); 1877 Arguments arg = writer.finish(); 1878 doRoundtrip(arg, false); 1879 // doRoundtrip only checks the serialized data, but unfortunately file descriptors 1880 // are out of band, so check explicitly 1881 Arguments::Reader reader(arg); 1882 TEST(reader.readUnixFd() == 200); 1883 TEST(reader.readByte() == 12); 1884 TEST(reader.readUnixFd() == 1); 1885 TEST(reader.state() == Arguments::Finished); 1886 } 1887 { 1888 Arguments::Writer writer; 1889 writer.writeUnixFd(400); 1890 Arguments arg = writer.finish(); 1891 doRoundtrip(arg, false); 1892 // doRoundtrip only checks the serialized data, but unfortunately file descriptors 1893 // are out of band, so check explicitly 1894 Arguments::Reader reader(arg); 1895 TEST(reader.state() == Arguments::UnixFd); 1896 TEST(reader.readUnixFd() == 400); 1897 TEST(reader.state() == Arguments::Finished); 1898 1899 } 1900 #endif 1901 } 1902 1903 static void test_closeWrongAggregate() 1904 { 1905 for (int i = 0; i < 8; i++) { 1906 for (int j = 0; j < 4; j++) { 1907 Arguments::Writer writer; 1908 switch (i % 4) { 1909 case 0: writer.beginStruct(); break; 1910 case 1: writer.beginVariant(); break; 1911 case 2: writer.beginArray(); break; 1912 case 3: writer.beginDict(); break; 1913 } 1914 1915 if (i < 4) { 1916 writer.writeByte(123); 1917 if (i == 3) { 1918 writer.writeByte(123); // value for dict 1919 } 1920 } 1921 1922 switch (j) { 1923 case 0: writer.endStruct(); break; 1924 case 1: writer.endVariant(); break; 1925 case 2: writer.endArray(); break; 1926 case 3: writer.endDict(); break; 1927 } 1928 1929 const bool isValid = writer.state() != Arguments::InvalidData; 1930 TEST(isValid == (i == j)); 1931 } 1932 } 1933 } 1934 1935 // TODO: test where we compare data and signature lengths of all combinations of zero/nonzero array 1936 // length and long/short type signature, to make sure that the signature is written but not 1937 // any data if the array is zero-length. 1938 1939 // TODO test empty dicts, too 1940 1941 int main(int, char *[]) 1942 { 1943 test_stringValidation(); 1944 test_nesting(); 1945 test_roundtrip(); 1946 test_writerMisuse(); 1947 // TODO test arrays where array length does not align with end of an element 1948 // (corruption of serialized data) 1949 test_complicated(); 1950 test_alignment(); 1951 test_arrayOfVariant(); 1952 test_realMessage(); 1953 test_isWritingSignatureBug(); 1954 test_primitiveArray(); 1955 test_signatureLengths(); 1956 test_emptyArrayAndDict(); 1957 test_fileDescriptors(); 1958 1959 // TODO (maybe): specific tests for begin/endDictEntry() for both Reader and Writer. 1960 1961 // TODO more misuse tests for Writer and maybe some for Reader 1962 test_closeWrongAggregate(); 1963 1964 std::cout << "Passed!\n"; 1965 }