/* * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im> * * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3.0 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3.0 along with this program. If not, see * <https://www.gnu.org/licenses/>. */ #include "config.h" #include <string.h> #import "ObjFW.h" #import "ObjFWTest.h" @interface OFMessagePackTests: OTTestCase @end const char *smallDictionaryRepresentation = "\xDE\x00\x10\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06" "\x07\x07\x08\x08\x09\x09\x0A\x0A\x0B\x0B\x0C\x0C\x0D\x0D\x0E\x0E" "\x0F\x0F\x10"; @implementation OFMessagePackTests - (void)testMessagePackRepresentationForNull { OTAssertEqualObjects([[OFNull null] messagePackRepresentation], [OFData dataWithItems: "\xC0" count: 1]); } - (void)testObjectByParsingMessagePackForNull { OTAssertEqualObjects([[OFData dataWithItems: "\xC0" count: 1] objectByParsingMessagePack], [OFNull null]); } - (void)testMessagePackRepresentationForNumber { OTAssertEqualObjects([[OFNumber numberWithChar: -30] messagePackRepresentation], [OFData dataWithItems: "\xE2" count: 1]); OTAssertEqualObjects([[OFNumber numberWithChar: -33] messagePackRepresentation], [OFData dataWithItems: "\xD0\xDF" count: 2]); OTAssertEqualObjects([[OFNumber numberWithUnsignedChar: 127] messagePackRepresentation], [OFData dataWithItems: "\x7F" count: 1]); OTAssertEqualObjects([[OFNumber numberWithUnsignedChar: 128] messagePackRepresentation], [OFData dataWithItems: "\xCC\x80" count: 2]); OTAssertEqualObjects([[OFNumber numberWithShort: -129] messagePackRepresentation], [OFData dataWithItems: "\xD1\xFF\x7F" count: 3]); OTAssertEqualObjects([[OFNumber numberWithUnsignedShort: 256] messagePackRepresentation], [OFData dataWithItems: "\xCD\x01\x00" count: 3]); OTAssertEqualObjects([[OFNumber numberWithLong: -32769] messagePackRepresentation], [OFData dataWithItems: "\xD2\xFF\xFF\x7F\xFF" count: 5]); OTAssertEqualObjects([[OFNumber numberWithUnsignedLong: 65536] messagePackRepresentation], [OFData dataWithItems: "\xCE\x00\x01\x00\x00" count: 5]); OTAssertEqualObjects([[OFNumber numberWithLongLong: -2147483649] messagePackRepresentation], [OFData dataWithItems: "\xD3\xFF\xFF\xFF\xFF\x7F\xFF\xFF\xFF" count: 9]); OTAssertEqualObjects([[OFNumber numberWithUnsignedLongLong: 4294967296] messagePackRepresentation], [OFData dataWithItems: "\xCF\x00\x00\x00\x01\x00\x00\x00\x00" count: 9]); OTAssertEqualObjects([[OFNumber numberWithFloat: 1.25f] messagePackRepresentation], [OFData dataWithItems: "\xCA\x3F\xA0\x00\x00" count: 5]); OTAssertEqualObjects([[OFNumber numberWithDouble: 1.25] messagePackRepresentation], [OFData dataWithItems: "\xCB\x3F\xF4\x00\x00\x00\x00\x00\x00" count: 9]); OTAssertEqualObjects( [[OFNumber numberWithBool: true] messagePackRepresentation], [OFData dataWithItems: "\xC3" count: 1]); OTAssertEqualObjects( [[OFNumber numberWithBool: false] messagePackRepresentation], [OFData dataWithItems: "\xC2" count: 1]); } - (void)testObjectByParsingMessagePackForNumber { OTAssertEqualObjects([[OFData dataWithItems: "\xE2" count: 1] objectByParsingMessagePack], [OFNumber numberWithChar: -30]); OTAssertEqualObjects([[OFData dataWithItems: "\xD0\xDF" count: 2] objectByParsingMessagePack], [OFNumber numberWithChar: -33]); OTAssertEqualObjects([[OFData dataWithItems: "\x7F" count: 1] objectByParsingMessagePack], [OFNumber numberWithUnsignedChar: 127]); OTAssertEqualObjects([[OFData dataWithItems: "\xCC\x80" count: 2] objectByParsingMessagePack], [OFNumber numberWithUnsignedChar: 128]); OTAssertEqualObjects([[OFData dataWithItems: "\xD1\xFF\x7F" count: 3] objectByParsingMessagePack], [OFNumber numberWithShort: -129]); OTAssertEqualObjects([[OFData dataWithItems: "\xCD\x01\x00" count: 3] objectByParsingMessagePack], [OFNumber numberWithUnsignedShort: 256]); OTAssertEqualObjects( [[OFData dataWithItems: "\xD2\xFF\xFF\x7F\xFF" count: 5] objectByParsingMessagePack], [OFNumber numberWithLong: -32769]); OTAssertEqualObjects( [[OFData dataWithItems: "\xCE\x00\x01\x00\x00" count: 5] objectByParsingMessagePack], [OFNumber numberWithUnsignedLong: 65536]); OTAssertEqualObjects( [[OFData dataWithItems: "\xD3\xFF\xFF\xFF\xFF\x7F\xFF\xFF\xFF" count: 9] objectByParsingMessagePack], [OFNumber numberWithLongLong: -2147483649]); OTAssertEqualObjects( [[OFData dataWithItems: "\xCF\x00\x00\x00\x01\x00\x00\x00\x00" count: 9] objectByParsingMessagePack], [OFNumber numberWithUnsignedLongLong: 4294967296]); OTAssertEqualObjects( [[OFData dataWithItems: "\xCA\x3F\xA0\x00\x00" count: 5] objectByParsingMessagePack], [OFNumber numberWithFloat: 1.25f]); OTAssertEqualObjects( [[OFData dataWithItems: "\xCB\x3F\xF4\x00\x00\x00\x00\x00\x00" count: 9] objectByParsingMessagePack], [OFNumber numberWithDouble: 1.25]); OTAssertEqualObjects([[OFData dataWithItems: "\xC3" count: 1] objectByParsingMessagePack], [OFNumber numberWithBool: true]); OTAssertEqualObjects([[OFData dataWithItems: "\xC2" count: 1] objectByParsingMessagePack], [OFNumber numberWithBool: false]); } static void generateStringAndData(OFString **string, OFMutableData **data, size_t length, const char *dataPrefix, size_t dataPrefixLength) { *data = [OFMutableData dataWithCapacity: length + dataPrefixLength]; [*data addItems: dataPrefix count: dataPrefixLength]; [*data increaseCountBy: length]; memset([*data mutableItemAtIndex: dataPrefixLength], 'x', length); *string = [OFString stringWithUTF8String: [*data itemAtIndex: dataPrefixLength] length: length]; } - (void)testMessagePackRepresentationForString { OFString *string; OFMutableData *data; OTAssertEqualObjects(@"x".messagePackRepresentation, [OFData dataWithItems: "\xA1x" count: 2]); generateStringAndData(&string, &data, 32, "\xD9\x20", 2); OTAssertEqualObjects(string.messagePackRepresentation, data); generateStringAndData(&string, &data, 256, "\xDA\x01\x00", 3); OTAssertEqualObjects(string.messagePackRepresentation, data); generateStringAndData(&string, &data, 65536, "\xDB\x00\x01\x00\x00", 5); OTAssertEqualObjects(string.messagePackRepresentation, data); } - (void)testObjectByParsingMessagePackForString { OFString *string; OFMutableData *data; OTAssertEqualObjects([[OFData dataWithItems: "\xA1x" count: 2] objectByParsingMessagePack], @"x"); generateStringAndData(&string, &data, 32, "\xD9\x20", 2); OTAssertEqualObjects(data.objectByParsingMessagePack, string); generateStringAndData(&string, &data, 256, "\xDA\x01\x00", 3); OTAssertEqualObjects(data.objectByParsingMessagePack, string); generateStringAndData(&string, &data, 65536, "\xDB\x00\x01\x00\x00", 5); OTAssertEqualObjects(data.objectByParsingMessagePack, string); } - (void)testMessagePackRepresentationForData { OFMutableData *data; OTAssertEqualObjects( [[OFData dataWithItems: "x" count: 1] messagePackRepresentation], [OFData dataWithItems: "\xC4\x01x" count: 3]); data = [OFMutableData data]; [data addItems: "\xC5\x01\x00" count: 3]; [data increaseCountBy: 256]; memset([data mutableItemAtIndex: 3], 'x', 256); OTAssertEqualObjects([[data subdataWithRange: OFMakeRange(3, 256)] messagePackRepresentation], data); data = [OFMutableData data]; [data addItems: "\xC6\x00\x01\x00\x00" count: 5]; [data increaseCountBy: 65536]; memset([data mutableItemAtIndex: 5], 'x', 65536); OTAssertEqualObjects([[data subdataWithRange: OFMakeRange(5, 65536)] messagePackRepresentation], data); } - (void)testObjectByParsingMessagePackForData { OFMutableData *data; OTAssertEqualObjects([[OFData dataWithItems: "\xC4\x01x" count: 3] objectByParsingMessagePack], [OFData dataWithItems: "x" count: 1]); data = [OFMutableData data]; [data addItems: "\xC5\x01\x00" count: 3]; [data increaseCountBy: 256]; memset([data mutableItemAtIndex: 3], 'x', 256); OTAssertEqualObjects(data.objectByParsingMessagePack, [data subdataWithRange: OFMakeRange(3, 256)]); data = [OFMutableData data]; [data addItems: "\xC6\x00\x01\x00\x00" count: 5]; [data increaseCountBy: 65536]; memset([data mutableItemAtIndex: 5], 'x', 65536); OTAssertEqualObjects(data.objectByParsingMessagePack, [data subdataWithRange: OFMakeRange(5, 65536)]); } - (void)testMessagePackRepresentationForArray { OFMutableArray *array = [OFMutableArray arrayWithCapacity: 65536]; OFNumber *number = [OFNumber numberWithUnsignedInt: 1]; OFMutableData *data; OTAssertEqualObjects([[OFArray array] messagePackRepresentation], [OFData dataWithItems: "\x90" count: 1]); OTAssertEqualObjects( [[OFArray arrayWithObject: number] messagePackRepresentation], [OFData dataWithItems: "\x91\x01" count: 2]); data = [OFMutableData dataWithCapacity: 19]; [data addItems: "\xDC\x00\x10" count: 3]; [data increaseCountBy: 16]; memset([data mutableItemAtIndex: 3], '\x01', 16); for (size_t i = 0; i < 16; i++) [array addObject: number]; OTAssertEqualObjects(array.messagePackRepresentation, data); data = [OFMutableData dataWithCapacity: 65541]; [data addItems: "\xDD\x00\x01\x00\x00" count: 5]; [data increaseCountBy: 65536]; memset([data mutableItemAtIndex: 5], '\x01', 65536); for (size_t i = 16; i < 65536; i++) [array addObject: number]; OTAssertEqualObjects(array.messagePackRepresentation, data); } - (void)testObjectByParsingMessagePackForArray { OFMutableArray *array = [OFMutableArray arrayWithCapacity: 65536]; OFNumber *number = [OFNumber numberWithUnsignedInt: 1]; OFMutableData *data; OTAssertEqualObjects([[OFData dataWithItems: "\x90" count: 1] objectByParsingMessagePack], [OFArray array]); OTAssertEqualObjects([[OFData dataWithItems: "\x91\x01" count: 2] objectByParsingMessagePack], [OFArray arrayWithObject: number]); data = [OFMutableData dataWithCapacity: 19]; [data addItems: "\xDC\x00\x10" count: 3]; [data increaseCountBy: 16]; memset([data mutableItemAtIndex: 3], '\x01', 16); for (size_t i = 0; i < 16; i++) [array addObject: number]; OTAssertEqualObjects(data.objectByParsingMessagePack, array); data = [OFMutableData dataWithCapacity: 65541]; [data addItems: "\xDD\x00\x01\x00\x00" count: 5]; [data increaseCountBy: 65536]; memset([data mutableItemAtIndex: 5], '\x01', 65536); for (size_t i = 16; i < 65536; i++) [array addObject: number]; OTAssertEqualObjects(data.objectByParsingMessagePack, array); } - (void)testMessagePackRepresentationForDictionary { OFMutableArray *keys = [OFMutableArray arrayWithCapacity: 65536]; OFMutableArray *objects = [OFMutableArray arrayWithCapacity: 65536]; OTAssertEqualObjects([[OFDictionary dictionary] messagePackRepresentation], [OFData dataWithItems: "\x80" count: 1]); OTAssertEqualObjects([[OFDictionary dictionaryWithObject: [OFNumber numberWithUnsignedInt: 2] forKey: [OFNumber numberWithUnsignedInt: 1]] messagePackRepresentation], [OFData dataWithItems: "\x81\x01\x02" count: 3]); for (unsigned int i = 0; i < 16; i++) { [keys addObject: [OFNumber numberWithUnsignedInt: i]]; [objects addObject: [OFNumber numberWithUnsignedInt: i + 1]]; } OTAssertEqualObjects([[OTOrderedDictionary dictionaryWithObjects: objects.objects forKeys: keys.objects count: 16] messagePackRepresentation], [OFData dataWithItems: smallDictionaryRepresentation count: 35]); for (unsigned int i = 16; i < 65536; i++) { [keys addObject: [OFNumber numberWithUnsignedInt: i]]; [objects addObject: [OFNumber numberWithUnsignedInt: i + 1]]; } OTAssertEqualObjects([[OTOrderedDictionary dictionaryWithObjects: objects.objects forKeys: keys.objects count: 65536] messagePackRepresentation], [OFData dataWithContentsOfIRI: [OFIRI IRIWithString: @"gzip:embedded:big_dictionary.msgpack.gz"]]); } - (void)testObjectByParsingMessagePackForDictionary { OFMutableDictionary *dictionary = [OFMutableDictionary dictionaryWithCapacity: 65536]; OTAssertEqualObjects([[OFData dataWithItems: "\x80" count: 1] objectByParsingMessagePack], [OFDictionary dictionary]); OTAssertEqualObjects([[OFData dataWithItems: "\x81\x01\x02" count: 3] objectByParsingMessagePack], [OFDictionary dictionaryWithObject: [OFNumber numberWithUnsignedInt: 2] forKey: [OFNumber numberWithUnsignedInt: 1]]); for (unsigned int i = 0; i < 16; i++) [dictionary setObject: [OFNumber numberWithUnsignedInt: i + 1] forKey: [OFNumber numberWithUnsignedInt: i]]; OTAssertEqualObjects( [[OFData dataWithItems: smallDictionaryRepresentation count: 35] objectByParsingMessagePack], dictionary); for (unsigned int i = 16; i < 65536; i++) [dictionary setObject: [OFNumber numberWithUnsignedInt: i + 1] forKey: [OFNumber numberWithUnsignedInt: i]]; OTAssertEqualObjects(dictionary, [[OFData dataWithContentsOfIRI: [OFIRI IRIWithString: @"gzip:embedded:big_dictionary.msgpack.gz"]] objectByParsingMessagePack]); } - (void)testMessagePackRepresentationForExtension { OFMessagePackExtension *extension; OFMutableData *data; extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "x" count: 1]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xD4\x01x" count: 3]); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "xy" count: 2]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xD5\x01xy" count: 4]); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "abcd" count: 4]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xD6\x01" "abcd" count: 6]); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "12345678" count: 8]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xD7\x01" "12345678" count: 10]); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "12345678ABCDEFGH" count: 16]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xD8\x01" "12345678ABCDEFGH" count: 18]); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData data]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xC7\x00\x01" count: 3]); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "abc" count: 3]]; OTAssertEqualObjects(extension.messagePackRepresentation, [OFData dataWithItems: "\xC7\x03\x01" "abc" count: 6]); data = [OFMutableData dataWithCapacity: 260]; [data addItems: "\xC8\x01\x00\x01" count: 4]; [data increaseCountBy: 256]; memset([data mutableItemAtIndex: 4], 'x', 256); extension = [OFMessagePackExtension extensionWithType: 1 data: [data subdataWithRange: OFMakeRange(4, 256)]]; OTAssertEqualObjects(extension.messagePackRepresentation, data); data = [OFMutableData dataWithCapacity: 65542]; [data addItems: "\xC9\x00\x01\x00\x00\x01" count: 6]; [data increaseCountBy: 65536]; memset([data mutableItemAtIndex: 6], 'x', 65536); extension = [OFMessagePackExtension extensionWithType: 1 data: [data subdataWithRange: OFMakeRange(6, 65536)]]; OTAssertEqualObjects(extension.messagePackRepresentation, data); } - (void)testObjectByParsingMessagePackForExtension { OFMessagePackExtension *extension; OFMutableData *data; extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "x" count: 1]]; OTAssertEqualObjects([[OFData dataWithItems: "\xD4\x01x" count: 3] objectByParsingMessagePack], extension); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "xy" count: 2]]; OTAssertEqualObjects([[OFData dataWithItems: "\xD5\x01xy" count: 4] objectByParsingMessagePack], extension); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "abcd" count: 4]]; OTAssertEqualObjects( [[OFData dataWithItems: "\xD6\x01" "abcd" count: 6] objectByParsingMessagePack], extension); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "12345678" count: 8]]; OTAssertEqualObjects( [[OFData dataWithItems: "\xD7\x01" "12345678" count: 10] objectByParsingMessagePack], extension); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "12345678ABCDEFGH" count: 16]]; OTAssertEqualObjects( [[OFData dataWithItems: "\xD8\x01" "12345678ABCDEFGH" count: 18] objectByParsingMessagePack], extension); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData data]]; OTAssertEqualObjects([[OFData dataWithItems: "\xC7\x00\x01" count: 3] objectByParsingMessagePack], extension); extension = [OFMessagePackExtension extensionWithType: 1 data: [OFData dataWithItems: "abc" count: 3]]; OTAssertEqualObjects( [[OFData dataWithItems: "\xC7\x03\x01" "abc" count: 6] objectByParsingMessagePack], extension); data = [OFMutableData dataWithCapacity: 260]; [data addItems: "\xC8\x01\x00\x01" count: 4]; [data increaseCountBy: 256]; memset([data mutableItemAtIndex: 4], 'x', 256); extension = [OFMessagePackExtension extensionWithType: 1 data: [data subdataWithRange: OFMakeRange(4, 256)]]; OTAssertEqualObjects(data.objectByParsingMessagePack, extension); data = [OFMutableData dataWithCapacity: 65542]; [data addItems: "\xC9\x00\x01\x00\x00\x01" count: 6]; [data increaseCountBy: 65536]; memset([data mutableItemAtIndex: 6], 'x', 65536); extension = [OFMessagePackExtension extensionWithType: 1 data: [data subdataWithRange: OFMakeRange(6, 65536)]]; OTAssertEqualObjects(data.objectByParsingMessagePack, extension); } - (void)testMessagePackRepresentationForDate { OTAssertEqualObjects([[OFDate dateWithTimeIntervalSince1970: 1] messagePackRepresentation], [OFData dataWithItems: "\xD6\xFF\x00\x00\x00\x01" count: 6]); OTAssertEqualObjects([[OFDate dateWithTimeIntervalSince1970: 1.25] messagePackRepresentation], [OFData dataWithItems: "\xD7\xFF\x3B\x9A\xCA\x00\x00\x00\x00\x01" count: 10]); OTAssertEqualObjects( [[OFDate dateWithTimeIntervalSince1970: 0x400000000 + 0.25] messagePackRepresentation], [OFData dataWithItems: "\xC7\x0C\xFF\x0E\xE6\xB2\x80\x00\x00\x00" "\x04\x00\x00\x00\x00" count: 15]); } - (void)testObjectByParsingMessagePackForDate { OTAssertEqualObjects( [[OFData dataWithItems: "\xD6\xFF\x00\x00\x00\x01" count: 6] objectByParsingMessagePack], [OFDate dateWithTimeIntervalSince1970: 1]); OTAssertEqualObjects( [[OFData dataWithItems: "\xD7\xFF\x3B\x9A\xCA\x00\x00\x00\x00\x01" count: 10] objectByParsingMessagePack], [OFDate dateWithTimeIntervalSince1970: 1.25]); OTAssertEqualObjects( [[OFData dataWithItems: "\xC7\x0C\xFF\x0E\xE6\xB2\x80\x00\x00\x00" "\x04\x00\x00\x00\x00" count: 15] objectByParsingMessagePack], [OFDate dateWithTimeIntervalSince1970: 0x400000000 + 0.25]); } @end