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 &copy, const Arguments::Writer &copyWriter,
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("&lt;fredrikh&gt; 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 }