1# Copyright (C) 2003-2013 Python Software Foundation 2import copy 3import operator 4import pickle 5import struct 6import unittest 7import plistlib 8import os 9import datetime 10import codecs 11import binascii 12import collections 13from test import support 14from test.support import os_helper 15from io import BytesIO 16 17from plistlib import UID 18 19ALL_FORMATS=(plistlib.FMT_XML, plistlib.FMT_BINARY) 20 21# The testdata is generated using Mac/Tools/plistlib_generate_testdata.py 22# (which using PyObjC to control the Cocoa classes for generating plists) 23TESTDATA={ 24 plistlib.FMT_XML: binascii.a2b_base64(b''' 25 PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU 26 WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO 27 IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w 28 LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+YUJp 29 Z0ludDwva2V5PgoJPGludGVnZXI+OTIyMzM3MjAzNjg1NDc3NTc2NDwvaW50 30 ZWdlcj4KCTxrZXk+YUJpZ0ludDI8L2tleT4KCTxpbnRlZ2VyPjkyMjMzNzIw 31 MzY4NTQ3NzU4NTI8L2ludGVnZXI+Cgk8a2V5PmFEYXRlPC9rZXk+Cgk8ZGF0 32 ZT4yMDA0LTEwLTI2VDEwOjMzOjMzWjwvZGF0ZT4KCTxrZXk+YURpY3Q8L2tl 33 eT4KCTxkaWN0PgoJCTxrZXk+YUZhbHNlVmFsdWU8L2tleT4KCQk8ZmFsc2Uv 34 PgoJCTxrZXk+YVRydWVWYWx1ZTwva2V5PgoJCTx0cnVlLz4KCQk8a2V5PmFV 35 bmljb2RlVmFsdWU8L2tleT4KCQk8c3RyaW5nPk3DpHNzaWcsIE1hw588L3N0 36 cmluZz4KCQk8a2V5PmFub3RoZXJTdHJpbmc8L2tleT4KCQk8c3RyaW5nPiZs 37 dDtoZWxsbyAmYW1wOyAnaGknIHRoZXJlISZndDs8L3N0cmluZz4KCQk8a2V5 38 PmRlZXBlckRpY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5hPC9rZXk+CgkJ 39 CTxpbnRlZ2VyPjE3PC9pbnRlZ2VyPgoJCQk8a2V5PmI8L2tleT4KCQkJPHJl 40 YWw+MzIuNTwvcmVhbD4KCQkJPGtleT5jPC9rZXk+CgkJCTxhcnJheT4KCQkJ 41 CTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8aW50ZWdlcj4yPC9pbnRlZ2Vy 42 PgoJCQkJPHN0cmluZz50ZXh0PC9zdHJpbmc+CgkJCTwvYXJyYXk+CgkJPC9k 43 aWN0PgoJPC9kaWN0PgoJPGtleT5hRmxvYXQ8L2tleT4KCTxyZWFsPjAuNTwv 44 cmVhbD4KCTxrZXk+YUxpc3Q8L2tleT4KCTxhcnJheT4KCQk8c3RyaW5nPkE8 45 L3N0cmluZz4KCQk8c3RyaW5nPkI8L3N0cmluZz4KCQk8aW50ZWdlcj4xMjwv 46 aW50ZWdlcj4KCQk8cmVhbD4zMi41PC9yZWFsPgoJCTxhcnJheT4KCQkJPGlu 47 dGVnZXI+MTwvaW50ZWdlcj4KCQkJPGludGVnZXI+MjwvaW50ZWdlcj4KCQkJ 48 PGludGVnZXI+MzwvaW50ZWdlcj4KCQk8L2FycmF5PgoJPC9hcnJheT4KCTxr 49 ZXk+YU5lZ2F0aXZlQmlnSW50PC9rZXk+Cgk8aW50ZWdlcj4tODAwMDAwMDAw 50 MDA8L2ludGVnZXI+Cgk8a2V5PmFOZWdhdGl2ZUludDwva2V5PgoJPGludGVn 51 ZXI+LTU8L2ludGVnZXI+Cgk8a2V5PmFTdHJpbmc8L2tleT4KCTxzdHJpbmc+ 52 RG9vZGFoPC9zdHJpbmc+Cgk8a2V5PmFuRW1wdHlEaWN0PC9rZXk+Cgk8ZGlj 53 dC8+Cgk8a2V5PmFuRW1wdHlMaXN0PC9rZXk+Cgk8YXJyYXkvPgoJPGtleT5h 54 bkludDwva2V5PgoJPGludGVnZXI+NzI4PC9pbnRlZ2VyPgoJPGtleT5uZXN0 55 ZWREYXRhPC9rZXk+Cgk8YXJyYXk+CgkJPGRhdGE+CgkJUEd4dmRITWdiMlln 56 WW1sdVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5k 57 VzVyCgkJUGdBQkFnTThiRzkwY3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJ 58 RFBHeHZkSE1nYjJZZ1ltbHVZWEo1CgkJSUdkMWJtcytBQUVDQXp4c2IzUnpJ 59 RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004Ykc5MGN5QnZaaUJpCgkJYVc1 60 aGNua2daM1Z1YXo0QUFRSURQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMr 61 QUFFQ0F6eHNiM1J6CgkJSUc5bUlHSnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJH 62 OTBjeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlECgkJUEd4dmRITWdiMlln 63 WW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09CgkJPC9kYXRhPgoJPC9hcnJheT4K 64 CTxrZXk+c29tZURhdGE8L2tleT4KCTxkYXRhPgoJUEdKcGJtRnllU0JuZFc1 65 clBnPT0KCTwvZGF0YT4KCTxrZXk+c29tZU1vcmVEYXRhPC9rZXk+Cgk8ZGF0 66 YT4KCVBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytBQUVDQXp4c2IzUnpJ 67 RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004CgliRzkwY3lCdlppQmlhVzVo 68 Y25rZ1ozVnVhejRBQVFJRFBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytB 69 QUVDQXp4cwoJYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVyUGdBQkFnTThiRzkw 70 Y3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJRFBHeHYKCWRITWdiMllnWW1s 71 dVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVy 72 UGdBQkFnTThiRzkwCgljeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlEUEd4 73 dmRITWdiMllnWW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09Cgk8L2RhdGE+Cgk8 74 a2V5PsOFYmVucmFhPC9rZXk+Cgk8c3RyaW5nPlRoYXQgd2FzIGEgdW5pY29k 75 ZSBrZXkuPC9zdHJpbmc+CjwvZGljdD4KPC9wbGlzdD4K'''), 76 plistlib.FMT_BINARY: binascii.a2b_base64(b''' 77 YnBsaXN0MDDfEBABAgMEBQYHCAkKCwwNDg8QERITFCgpLzAxMjM0NTc2OFdh 78 QmlnSW50WGFCaWdJbnQyVWFEYXRlVWFEaWN0VmFGbG9hdFVhTGlzdF8QD2FO 79 ZWdhdGl2ZUJpZ0ludFxhTmVnYXRpdmVJbnRXYVN0cmluZ1thbkVtcHR5RGlj 80 dFthbkVtcHR5TGlzdFVhbkludFpuZXN0ZWREYXRhWHNvbWVEYXRhXHNvbWVN 81 b3JlRGF0YWcAxQBiAGUAbgByAGEAYRN/////////1BQAAAAAAAAAAIAAAAAA 82 AAAsM0GcuX30AAAA1RUWFxgZGhscHR5bYUZhbHNlVmFsdWVaYVRydWVWYWx1 83 ZV1hVW5pY29kZVZhbHVlXWFub3RoZXJTdHJpbmdaZGVlcGVyRGljdAgJawBN 84 AOQAcwBzAGkAZwAsACAATQBhAN9fEBU8aGVsbG8gJiAnaGknIHRoZXJlIT7T 85 HyAhIiMkUWFRYlFjEBEjQEBAAAAAAACjJSYnEAEQAlR0ZXh0Iz/gAAAAAAAA 86 pSorLCMtUUFRQhAMoyUmLhADE////+1foOAAE//////////7VkRvb2RhaNCg 87 EQLYoTZPEPo8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmlu 88 YXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBv 89 ZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs 90 b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4A 91 AQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBn 92 dW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDTTxiaW5hcnkgZ3Vu 93 az5fEBdUaGF0IHdhcyBhIHVuaWNvZGUga2V5LgAIACsAMwA8AEIASABPAFUA 94 ZwB0AHwAiACUAJoApQCuALsAygDTAOQA7QD4AQQBDwEdASsBNgE3ATgBTwFn 95 AW4BcAFyAXQBdgF/AYMBhQGHAYwBlQGbAZ0BnwGhAaUBpwGwAbkBwAHBAcIB 96 xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''), 97 'KEYED_ARCHIVE': binascii.a2b_base64(b''' 98 YnBsaXN0MDDUAQIDBAUGHB1YJHZlcnNpb25YJG9iamVjdHNZJGFyY2hpdmVy 99 VCR0b3ASAAGGoKMHCA9VJG51bGzTCQoLDA0OVnB5dHlwZVYkY2xhc3NZTlMu 100 c3RyaW5nEAGAAl8QE0tleUFyY2hpdmUgVUlEIFRlc3TTEBESExQZWiRjbGFz 101 c25hbWVYJGNsYXNzZXNbJGNsYXNzaGludHNfEBdPQ19CdWlsdGluUHl0aG9u 102 VW5pY29kZaQVFhcYXxAXT0NfQnVpbHRpblB5dGhvblVuaWNvZGVfEBBPQ19Q 103 eXRob25Vbmljb2RlWE5TU3RyaW5nWE5TT2JqZWN0ohobXxAPT0NfUHl0aG9u 104 U3RyaW5nWE5TU3RyaW5nXxAPTlNLZXllZEFyY2hpdmVy0R4fVHJvb3SAAQAI 105 ABEAGgAjAC0AMgA3ADsAQQBIAE8AVgBgAGIAZAB6AIEAjACVAKEAuwDAANoA 106 7QD2AP8BAgEUAR0BLwEyATcAAAAAAAACAQAAAAAAAAAgAAAAAAAAAAAAAAAA 107 AAABOQ=='''), 108} 109 110XML_PLIST_WITH_ENTITY=b'''\ 111<?xml version="1.0" encoding="UTF-8"?> 112<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd" [ 113 <!ENTITY entity "replacement text"> 114 ]> 115<plist version="1.0"> 116 <dict> 117 <key>A</key> 118 <string>&entity;</string> 119 </dict> 120</plist> 121''' 122 123INVALID_BINARY_PLISTS = [ 124 ('too short data', 125 b'' 126 ), 127 ('too large offset_table_offset and offset_size = 1', 128 b'\x00\x08' 129 b'\x00\x00\x00\x00\x00\x00\x01\x01' 130 b'\x00\x00\x00\x00\x00\x00\x00\x01' 131 b'\x00\x00\x00\x00\x00\x00\x00\x00' 132 b'\x00\x00\x00\x00\x00\x00\x00\x2a' 133 ), 134 ('too large offset_table_offset and nonstandard offset_size', 135 b'\x00\x00\x00\x08' 136 b'\x00\x00\x00\x00\x00\x00\x03\x01' 137 b'\x00\x00\x00\x00\x00\x00\x00\x01' 138 b'\x00\x00\x00\x00\x00\x00\x00\x00' 139 b'\x00\x00\x00\x00\x00\x00\x00\x2c' 140 ), 141 ('integer overflow in offset_table_offset', 142 b'\x00\x08' 143 b'\x00\x00\x00\x00\x00\x00\x01\x01' 144 b'\x00\x00\x00\x00\x00\x00\x00\x01' 145 b'\x00\x00\x00\x00\x00\x00\x00\x00' 146 b'\xff\xff\xff\xff\xff\xff\xff\xff' 147 ), 148 ('too large top_object', 149 b'\x00\x08' 150 b'\x00\x00\x00\x00\x00\x00\x01\x01' 151 b'\x00\x00\x00\x00\x00\x00\x00\x01' 152 b'\x00\x00\x00\x00\x00\x00\x00\x01' 153 b'\x00\x00\x00\x00\x00\x00\x00\x09' 154 ), 155 ('integer overflow in top_object', 156 b'\x00\x08' 157 b'\x00\x00\x00\x00\x00\x00\x01\x01' 158 b'\x00\x00\x00\x00\x00\x00\x00\x01' 159 b'\xff\xff\xff\xff\xff\xff\xff\xff' 160 b'\x00\x00\x00\x00\x00\x00\x00\x09' 161 ), 162 ('too large num_objects and offset_size = 1', 163 b'\x00\x08' 164 b'\x00\x00\x00\x00\x00\x00\x01\x01' 165 b'\x00\x00\x00\x00\x00\x00\x00\xff' 166 b'\x00\x00\x00\x00\x00\x00\x00\x00' 167 b'\x00\x00\x00\x00\x00\x00\x00\x09' 168 ), 169 ('too large num_objects and nonstandard offset_size', 170 b'\x00\x00\x00\x08' 171 b'\x00\x00\x00\x00\x00\x00\x03\x01' 172 b'\x00\x00\x00\x00\x00\x00\x00\xff' 173 b'\x00\x00\x00\x00\x00\x00\x00\x00' 174 b'\x00\x00\x00\x00\x00\x00\x00\x09' 175 ), 176 ('extremally large num_objects (32 bit)', 177 b'\x00\x08' 178 b'\x00\x00\x00\x00\x00\x00\x01\x01' 179 b'\x00\x00\x00\x00\x7f\xff\xff\xff' 180 b'\x00\x00\x00\x00\x00\x00\x00\x00' 181 b'\x00\x00\x00\x00\x00\x00\x00\x09' 182 ), 183 ('extremally large num_objects (64 bit)', 184 b'\x00\x08' 185 b'\x00\x00\x00\x00\x00\x00\x01\x01' 186 b'\x00\x00\x00\xff\xff\xff\xff\xff' 187 b'\x00\x00\x00\x00\x00\x00\x00\x00' 188 b'\x00\x00\x00\x00\x00\x00\x00\x09' 189 ), 190 ('integer overflow in num_objects', 191 b'\x00\x08' 192 b'\x00\x00\x00\x00\x00\x00\x01\x01' 193 b'\xff\xff\xff\xff\xff\xff\xff\xff' 194 b'\x00\x00\x00\x00\x00\x00\x00\x00' 195 b'\x00\x00\x00\x00\x00\x00\x00\x09' 196 ), 197 ('offset_size = 0', 198 b'\x00\x08' 199 b'\x00\x00\x00\x00\x00\x00\x00\x01' 200 b'\x00\x00\x00\x00\x00\x00\x00\x01' 201 b'\x00\x00\x00\x00\x00\x00\x00\x00' 202 b'\x00\x00\x00\x00\x00\x00\x00\x09' 203 ), 204 ('ref_size = 0', 205 b'\xa1\x01\x00\x08\x0a' 206 b'\x00\x00\x00\x00\x00\x00\x01\x00' 207 b'\x00\x00\x00\x00\x00\x00\x00\x02' 208 b'\x00\x00\x00\x00\x00\x00\x00\x00' 209 b'\x00\x00\x00\x00\x00\x00\x00\x0b' 210 ), 211 ('too large offset', 212 b'\x00\x2a' 213 b'\x00\x00\x00\x00\x00\x00\x01\x01' 214 b'\x00\x00\x00\x00\x00\x00\x00\x01' 215 b'\x00\x00\x00\x00\x00\x00\x00\x00' 216 b'\x00\x00\x00\x00\x00\x00\x00\x09' 217 ), 218 ('integer overflow in offset', 219 b'\x00\xff\xff\xff\xff\xff\xff\xff\xff' 220 b'\x00\x00\x00\x00\x00\x00\x08\x01' 221 b'\x00\x00\x00\x00\x00\x00\x00\x01' 222 b'\x00\x00\x00\x00\x00\x00\x00\x00' 223 b'\x00\x00\x00\x00\x00\x00\x00\x09' 224 ), 225 ('too large array size', 226 b'\xaf\x00\x01\xff\x00\x08\x0c' 227 b'\x00\x00\x00\x00\x00\x00\x01\x01' 228 b'\x00\x00\x00\x00\x00\x00\x00\x02' 229 b'\x00\x00\x00\x00\x00\x00\x00\x00' 230 b'\x00\x00\x00\x00\x00\x00\x00\x0d' 231 ), 232 ('extremally large array size (32-bit)', 233 b'\xaf\x02\x7f\xff\xff\xff\x01\x00\x08\x0f' 234 b'\x00\x00\x00\x00\x00\x00\x01\x01' 235 b'\x00\x00\x00\x00\x00\x00\x00\x02' 236 b'\x00\x00\x00\x00\x00\x00\x00\x00' 237 b'\x00\x00\x00\x00\x00\x00\x00\x10' 238 ), 239 ('extremally large array size (64-bit)', 240 b'\xaf\x03\x00\x00\x00\xff\xff\xff\xff\xff\x01\x00\x08\x13' 241 b'\x00\x00\x00\x00\x00\x00\x01\x01' 242 b'\x00\x00\x00\x00\x00\x00\x00\x02' 243 b'\x00\x00\x00\x00\x00\x00\x00\x00' 244 b'\x00\x00\x00\x00\x00\x00\x00\x14' 245 ), 246 ('integer overflow in array size', 247 b'\xaf\x03\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x08\x13' 248 b'\x00\x00\x00\x00\x00\x00\x01\x01' 249 b'\x00\x00\x00\x00\x00\x00\x00\x02' 250 b'\x00\x00\x00\x00\x00\x00\x00\x00' 251 b'\x00\x00\x00\x00\x00\x00\x00\x14' 252 ), 253 ('too large reference index', 254 b'\xa1\x02\x00\x08\x0a' 255 b'\x00\x00\x00\x00\x00\x00\x01\x01' 256 b'\x00\x00\x00\x00\x00\x00\x00\x02' 257 b'\x00\x00\x00\x00\x00\x00\x00\x00' 258 b'\x00\x00\x00\x00\x00\x00\x00\x0b' 259 ), 260 ('integer overflow in reference index', 261 b'\xa1\xff\xff\xff\xff\xff\xff\xff\xff\x00\x08\x11' 262 b'\x00\x00\x00\x00\x00\x00\x01\x08' 263 b'\x00\x00\x00\x00\x00\x00\x00\x02' 264 b'\x00\x00\x00\x00\x00\x00\x00\x00' 265 b'\x00\x00\x00\x00\x00\x00\x00\x12' 266 ), 267 ('too large bytes size', 268 b'\x4f\x00\x23\x41\x08' 269 b'\x00\x00\x00\x00\x00\x00\x01\x01' 270 b'\x00\x00\x00\x00\x00\x00\x00\x01' 271 b'\x00\x00\x00\x00\x00\x00\x00\x00' 272 b'\x00\x00\x00\x00\x00\x00\x00\x0c' 273 ), 274 ('extremally large bytes size (32-bit)', 275 b'\x4f\x02\x7f\xff\xff\xff\x41\x08' 276 b'\x00\x00\x00\x00\x00\x00\x01\x01' 277 b'\x00\x00\x00\x00\x00\x00\x00\x01' 278 b'\x00\x00\x00\x00\x00\x00\x00\x00' 279 b'\x00\x00\x00\x00\x00\x00\x00\x0f' 280 ), 281 ('extremally large bytes size (64-bit)', 282 b'\x4f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08' 283 b'\x00\x00\x00\x00\x00\x00\x01\x01' 284 b'\x00\x00\x00\x00\x00\x00\x00\x01' 285 b'\x00\x00\x00\x00\x00\x00\x00\x00' 286 b'\x00\x00\x00\x00\x00\x00\x00\x13' 287 ), 288 ('integer overflow in bytes size', 289 b'\x4f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08' 290 b'\x00\x00\x00\x00\x00\x00\x01\x01' 291 b'\x00\x00\x00\x00\x00\x00\x00\x01' 292 b'\x00\x00\x00\x00\x00\x00\x00\x00' 293 b'\x00\x00\x00\x00\x00\x00\x00\x13' 294 ), 295 ('too large ASCII size', 296 b'\x5f\x00\x23\x41\x08' 297 b'\x00\x00\x00\x00\x00\x00\x01\x01' 298 b'\x00\x00\x00\x00\x00\x00\x00\x01' 299 b'\x00\x00\x00\x00\x00\x00\x00\x00' 300 b'\x00\x00\x00\x00\x00\x00\x00\x0c' 301 ), 302 ('extremally large ASCII size (32-bit)', 303 b'\x5f\x02\x7f\xff\xff\xff\x41\x08' 304 b'\x00\x00\x00\x00\x00\x00\x01\x01' 305 b'\x00\x00\x00\x00\x00\x00\x00\x01' 306 b'\x00\x00\x00\x00\x00\x00\x00\x00' 307 b'\x00\x00\x00\x00\x00\x00\x00\x0f' 308 ), 309 ('extremally large ASCII size (64-bit)', 310 b'\x5f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08' 311 b'\x00\x00\x00\x00\x00\x00\x01\x01' 312 b'\x00\x00\x00\x00\x00\x00\x00\x01' 313 b'\x00\x00\x00\x00\x00\x00\x00\x00' 314 b'\x00\x00\x00\x00\x00\x00\x00\x13' 315 ), 316 ('integer overflow in ASCII size', 317 b'\x5f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08' 318 b'\x00\x00\x00\x00\x00\x00\x01\x01' 319 b'\x00\x00\x00\x00\x00\x00\x00\x01' 320 b'\x00\x00\x00\x00\x00\x00\x00\x00' 321 b'\x00\x00\x00\x00\x00\x00\x00\x13' 322 ), 323 ('invalid ASCII', 324 b'\x51\xff\x08' 325 b'\x00\x00\x00\x00\x00\x00\x01\x01' 326 b'\x00\x00\x00\x00\x00\x00\x00\x01' 327 b'\x00\x00\x00\x00\x00\x00\x00\x00' 328 b'\x00\x00\x00\x00\x00\x00\x00\x0a' 329 ), 330 ('too large UTF-16 size', 331 b'\x6f\x00\x13\x20\xac\x00\x08' 332 b'\x00\x00\x00\x00\x00\x00\x01\x01' 333 b'\x00\x00\x00\x00\x00\x00\x00\x01' 334 b'\x00\x00\x00\x00\x00\x00\x00\x00' 335 b'\x00\x00\x00\x00\x00\x00\x00\x0e' 336 ), 337 ('extremally large UTF-16 size (32-bit)', 338 b'\x6f\x02\x4f\xff\xff\xff\x20\xac\x00\x08' 339 b'\x00\x00\x00\x00\x00\x00\x01\x01' 340 b'\x00\x00\x00\x00\x00\x00\x00\x01' 341 b'\x00\x00\x00\x00\x00\x00\x00\x00' 342 b'\x00\x00\x00\x00\x00\x00\x00\x11' 343 ), 344 ('extremally large UTF-16 size (64-bit)', 345 b'\x6f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x20\xac\x00\x08' 346 b'\x00\x00\x00\x00\x00\x00\x01\x01' 347 b'\x00\x00\x00\x00\x00\x00\x00\x01' 348 b'\x00\x00\x00\x00\x00\x00\x00\x00' 349 b'\x00\x00\x00\x00\x00\x00\x00\x15' 350 ), 351 ('integer overflow in UTF-16 size', 352 b'\x6f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x20\xac\x00\x08' 353 b'\x00\x00\x00\x00\x00\x00\x01\x01' 354 b'\x00\x00\x00\x00\x00\x00\x00\x01' 355 b'\x00\x00\x00\x00\x00\x00\x00\x00' 356 b'\x00\x00\x00\x00\x00\x00\x00\x15' 357 ), 358 ('invalid UTF-16', 359 b'\x61\xd8\x00\x08' 360 b'\x00\x00\x00\x00\x00\x00\x01\x01' 361 b'\x00\x00\x00\x00\x00\x00\x00\x01' 362 b'\x00\x00\x00\x00\x00\x00\x00\x00' 363 b'\x00\x00\x00\x00\x00\x00\x00\x0b' 364 ), 365 ('non-hashable key', 366 b'\xd1\x01\x01\xa0\x08\x0b' 367 b'\x00\x00\x00\x00\x00\x00\x01\x01' 368 b'\x00\x00\x00\x00\x00\x00\x00\x02' 369 b'\x00\x00\x00\x00\x00\x00\x00\x00' 370 b'\x00\x00\x00\x00\x00\x00\x00\x0c' 371 ), 372 ('too large datetime (datetime overflow)', 373 b'\x33\x42\x50\x00\x00\x00\x00\x00\x00\x08' 374 b'\x00\x00\x00\x00\x00\x00\x01\x01' 375 b'\x00\x00\x00\x00\x00\x00\x00\x01' 376 b'\x00\x00\x00\x00\x00\x00\x00\x00' 377 b'\x00\x00\x00\x00\x00\x00\x00\x11' 378 ), 379 ('too large datetime (timedelta overflow)', 380 b'\x33\x42\xe0\x00\x00\x00\x00\x00\x00\x08' 381 b'\x00\x00\x00\x00\x00\x00\x01\x01' 382 b'\x00\x00\x00\x00\x00\x00\x00\x01' 383 b'\x00\x00\x00\x00\x00\x00\x00\x00' 384 b'\x00\x00\x00\x00\x00\x00\x00\x11' 385 ), 386 ('invalid datetime (Infinity)', 387 b'\x33\x7f\xf0\x00\x00\x00\x00\x00\x00\x08' 388 b'\x00\x00\x00\x00\x00\x00\x01\x01' 389 b'\x00\x00\x00\x00\x00\x00\x00\x01' 390 b'\x00\x00\x00\x00\x00\x00\x00\x00' 391 b'\x00\x00\x00\x00\x00\x00\x00\x11' 392 ), 393 ('invalid datetime (NaN)', 394 b'\x33\x7f\xf8\x00\x00\x00\x00\x00\x00\x08' 395 b'\x00\x00\x00\x00\x00\x00\x01\x01' 396 b'\x00\x00\x00\x00\x00\x00\x00\x01' 397 b'\x00\x00\x00\x00\x00\x00\x00\x00' 398 b'\x00\x00\x00\x00\x00\x00\x00\x11' 399 ), 400] 401 402 403class TestPlistlib(unittest.TestCase): 404 405 def tearDown(self): 406 try: 407 os.unlink(os_helper.TESTFN) 408 except: 409 pass 410 411 def _create(self, fmt=None): 412 pl = dict( 413 aString="Doodah", 414 aList=["A", "B", 12, 32.5, [1, 2, 3]], 415 aFloat = 0.5, 416 anInt = 728, 417 aBigInt = 2 ** 63 - 44, 418 aBigInt2 = 2 ** 63 + 44, 419 aNegativeInt = -5, 420 aNegativeBigInt = -80000000000, 421 aDict=dict( 422 anotherString="<hello & 'hi' there!>", 423 aUnicodeValue='M\xe4ssig, Ma\xdf', 424 aTrueValue=True, 425 aFalseValue=False, 426 deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]), 427 ), 428 someData = b"<binary gunk>", 429 someMoreData = b"<lots of binary gunk>\0\1\2\3" * 10, 430 nestedData = [b"<lots of binary gunk>\0\1\2\3" * 10], 431 aDate = datetime.datetime(2004, 10, 26, 10, 33, 33), 432 anEmptyDict = dict(), 433 anEmptyList = list() 434 ) 435 pl['\xc5benraa'] = "That was a unicode key." 436 return pl 437 438 def test_create(self): 439 pl = self._create() 440 self.assertEqual(pl["aString"], "Doodah") 441 self.assertEqual(pl["aDict"]["aFalseValue"], False) 442 443 def test_io(self): 444 pl = self._create() 445 with open(os_helper.TESTFN, 'wb') as fp: 446 plistlib.dump(pl, fp) 447 448 with open(os_helper.TESTFN, 'rb') as fp: 449 pl2 = plistlib.load(fp) 450 451 self.assertEqual(dict(pl), dict(pl2)) 452 453 self.assertRaises(AttributeError, plistlib.dump, pl, 'filename') 454 self.assertRaises(AttributeError, plistlib.load, 'filename') 455 456 def test_invalid_type(self): 457 pl = [ object() ] 458 459 for fmt in ALL_FORMATS: 460 with self.subTest(fmt=fmt): 461 self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt) 462 463 def test_invalid_uid(self): 464 with self.assertRaises(TypeError): 465 UID("not an int") 466 with self.assertRaises(ValueError): 467 UID(2 ** 64) 468 with self.assertRaises(ValueError): 469 UID(-19) 470 471 def test_int(self): 472 for pl in [0, 2**8-1, 2**8, 2**16-1, 2**16, 2**32-1, 2**32, 473 2**63-1, 2**64-1, 1, -2**63]: 474 for fmt in ALL_FORMATS: 475 with self.subTest(pl=pl, fmt=fmt): 476 data = plistlib.dumps(pl, fmt=fmt) 477 pl2 = plistlib.loads(data) 478 self.assertIsInstance(pl2, int) 479 self.assertEqual(pl, pl2) 480 data2 = plistlib.dumps(pl2, fmt=fmt) 481 self.assertEqual(data, data2) 482 483 for fmt in ALL_FORMATS: 484 for pl in (2 ** 64 + 1, 2 ** 127-1, -2**64, -2 ** 127): 485 with self.subTest(pl=pl, fmt=fmt): 486 self.assertRaises(OverflowError, plistlib.dumps, 487 pl, fmt=fmt) 488 489 def test_bytearray(self): 490 for pl in (b'<binary gunk>', b"<lots of binary gunk>\0\1\2\3" * 10): 491 for fmt in ALL_FORMATS: 492 with self.subTest(pl=pl, fmt=fmt): 493 data = plistlib.dumps(bytearray(pl), fmt=fmt) 494 pl2 = plistlib.loads(data) 495 self.assertIsInstance(pl2, bytes) 496 self.assertEqual(pl2, pl) 497 data2 = plistlib.dumps(pl2, fmt=fmt) 498 self.assertEqual(data, data2) 499 500 def test_bytes(self): 501 pl = self._create() 502 data = plistlib.dumps(pl) 503 pl2 = plistlib.loads(data) 504 self.assertEqual(dict(pl), dict(pl2)) 505 data2 = plistlib.dumps(pl2) 506 self.assertEqual(data, data2) 507 508 def test_indentation_array(self): 509 data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]] 510 self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) 511 512 def test_indentation_dict(self): 513 data = {'1': {'2': {'3': {'4': {'5': {'6': {'7': {'8': {'9': b'aaaaaa'}}}}}}}}} 514 self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) 515 516 def test_indentation_dict_mix(self): 517 data = {'1': {'2': [{'3': [[[[[{'test': b'aaaaaa'}]]]]]}]}} 518 self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) 519 520 def test_uid(self): 521 data = UID(1) 522 self.assertEqual(plistlib.loads(plistlib.dumps(data, fmt=plistlib.FMT_BINARY)), data) 523 dict_data = { 524 'uid0': UID(0), 525 'uid2': UID(2), 526 'uid8': UID(2 ** 8), 527 'uid16': UID(2 ** 16), 528 'uid32': UID(2 ** 32), 529 'uid63': UID(2 ** 63) 530 } 531 self.assertEqual(plistlib.loads(plistlib.dumps(dict_data, fmt=plistlib.FMT_BINARY)), dict_data) 532 533 def test_uid_data(self): 534 uid = UID(1) 535 self.assertEqual(uid.data, 1) 536 537 def test_uid_eq(self): 538 self.assertEqual(UID(1), UID(1)) 539 self.assertNotEqual(UID(1), UID(2)) 540 self.assertNotEqual(UID(1), "not uid") 541 542 def test_uid_hash(self): 543 self.assertEqual(hash(UID(1)), hash(UID(1))) 544 545 def test_uid_repr(self): 546 self.assertEqual(repr(UID(1)), "UID(1)") 547 548 def test_uid_index(self): 549 self.assertEqual(operator.index(UID(1)), 1) 550 551 def test_uid_pickle(self): 552 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 553 self.assertEqual(pickle.loads(pickle.dumps(UID(19), protocol=proto)), UID(19)) 554 555 def test_uid_copy(self): 556 self.assertEqual(copy.copy(UID(1)), UID(1)) 557 self.assertEqual(copy.deepcopy(UID(1)), UID(1)) 558 559 def test_appleformatting(self): 560 for fmt in ALL_FORMATS: 561 with self.subTest(fmt=fmt): 562 pl = plistlib.loads(TESTDATA[fmt]) 563 data = plistlib.dumps(pl, fmt=fmt) 564 self.assertEqual(data, TESTDATA[fmt], 565 "generated data was not identical to Apple's output") 566 567 568 def test_appleformattingfromliteral(self): 569 self.maxDiff = None 570 for fmt in ALL_FORMATS: 571 with self.subTest(fmt=fmt): 572 pl = self._create(fmt=fmt) 573 pl2 = plistlib.loads(TESTDATA[fmt], fmt=fmt) 574 self.assertEqual(dict(pl), dict(pl2), 575 "generated data was not identical to Apple's output") 576 pl2 = plistlib.loads(TESTDATA[fmt]) 577 self.assertEqual(dict(pl), dict(pl2), 578 "generated data was not identical to Apple's output") 579 580 def test_bytesio(self): 581 for fmt in ALL_FORMATS: 582 with self.subTest(fmt=fmt): 583 b = BytesIO() 584 pl = self._create(fmt=fmt) 585 plistlib.dump(pl, b, fmt=fmt) 586 pl2 = plistlib.load(BytesIO(b.getvalue()), fmt=fmt) 587 self.assertEqual(dict(pl), dict(pl2)) 588 pl2 = plistlib.load(BytesIO(b.getvalue())) 589 self.assertEqual(dict(pl), dict(pl2)) 590 591 def test_keysort_bytesio(self): 592 pl = collections.OrderedDict() 593 pl['b'] = 1 594 pl['a'] = 2 595 pl['c'] = 3 596 597 for fmt in ALL_FORMATS: 598 for sort_keys in (False, True): 599 with self.subTest(fmt=fmt, sort_keys=sort_keys): 600 b = BytesIO() 601 602 plistlib.dump(pl, b, fmt=fmt, sort_keys=sort_keys) 603 pl2 = plistlib.load(BytesIO(b.getvalue()), 604 dict_type=collections.OrderedDict) 605 606 self.assertEqual(dict(pl), dict(pl2)) 607 if sort_keys: 608 self.assertEqual(list(pl2.keys()), ['a', 'b', 'c']) 609 else: 610 self.assertEqual(list(pl2.keys()), ['b', 'a', 'c']) 611 612 def test_keysort(self): 613 pl = collections.OrderedDict() 614 pl['b'] = 1 615 pl['a'] = 2 616 pl['c'] = 3 617 618 for fmt in ALL_FORMATS: 619 for sort_keys in (False, True): 620 with self.subTest(fmt=fmt, sort_keys=sort_keys): 621 data = plistlib.dumps(pl, fmt=fmt, sort_keys=sort_keys) 622 pl2 = plistlib.loads(data, dict_type=collections.OrderedDict) 623 624 self.assertEqual(dict(pl), dict(pl2)) 625 if sort_keys: 626 self.assertEqual(list(pl2.keys()), ['a', 'b', 'c']) 627 else: 628 self.assertEqual(list(pl2.keys()), ['b', 'a', 'c']) 629 630 def test_keys_no_string(self): 631 pl = { 42: 'aNumber' } 632 633 for fmt in ALL_FORMATS: 634 with self.subTest(fmt=fmt): 635 self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt) 636 637 b = BytesIO() 638 self.assertRaises(TypeError, plistlib.dump, pl, b, fmt=fmt) 639 640 def test_skipkeys(self): 641 pl = { 642 42: 'aNumber', 643 'snake': 'aWord', 644 } 645 646 for fmt in ALL_FORMATS: 647 with self.subTest(fmt=fmt): 648 data = plistlib.dumps( 649 pl, fmt=fmt, skipkeys=True, sort_keys=False) 650 651 pl2 = plistlib.loads(data) 652 self.assertEqual(pl2, {'snake': 'aWord'}) 653 654 fp = BytesIO() 655 plistlib.dump( 656 pl, fp, fmt=fmt, skipkeys=True, sort_keys=False) 657 data = fp.getvalue() 658 pl2 = plistlib.loads(fp.getvalue()) 659 self.assertEqual(pl2, {'snake': 'aWord'}) 660 661 def test_tuple_members(self): 662 pl = { 663 'first': (1, 2), 664 'second': (1, 2), 665 'third': (3, 4), 666 } 667 668 for fmt in ALL_FORMATS: 669 with self.subTest(fmt=fmt): 670 data = plistlib.dumps(pl, fmt=fmt) 671 pl2 = plistlib.loads(data) 672 self.assertEqual(pl2, { 673 'first': [1, 2], 674 'second': [1, 2], 675 'third': [3, 4], 676 }) 677 if fmt != plistlib.FMT_BINARY: 678 self.assertIsNot(pl2['first'], pl2['second']) 679 680 def test_list_members(self): 681 pl = { 682 'first': [1, 2], 683 'second': [1, 2], 684 'third': [3, 4], 685 } 686 687 for fmt in ALL_FORMATS: 688 with self.subTest(fmt=fmt): 689 data = plistlib.dumps(pl, fmt=fmt) 690 pl2 = plistlib.loads(data) 691 self.assertEqual(pl2, { 692 'first': [1, 2], 693 'second': [1, 2], 694 'third': [3, 4], 695 }) 696 self.assertIsNot(pl2['first'], pl2['second']) 697 698 def test_dict_members(self): 699 pl = { 700 'first': {'a': 1}, 701 'second': {'a': 1}, 702 'third': {'b': 2 }, 703 } 704 705 for fmt in ALL_FORMATS: 706 with self.subTest(fmt=fmt): 707 data = plistlib.dumps(pl, fmt=fmt) 708 pl2 = plistlib.loads(data) 709 self.assertEqual(pl2, { 710 'first': {'a': 1}, 711 'second': {'a': 1}, 712 'third': {'b': 2 }, 713 }) 714 self.assertIsNot(pl2['first'], pl2['second']) 715 716 def test_controlcharacters(self): 717 for i in range(128): 718 c = chr(i) 719 testString = "string containing %s" % c 720 if i >= 32 or c in "\r\n\t": 721 # \r, \n and \t are the only legal control chars in XML 722 data = plistlib.dumps(testString, fmt=plistlib.FMT_XML) 723 if c != "\r": 724 self.assertEqual(plistlib.loads(data), testString) 725 else: 726 with self.assertRaises(ValueError): 727 plistlib.dumps(testString, fmt=plistlib.FMT_XML) 728 plistlib.dumps(testString, fmt=plistlib.FMT_BINARY) 729 730 def test_non_bmp_characters(self): 731 pl = {'python': '\U0001f40d'} 732 for fmt in ALL_FORMATS: 733 with self.subTest(fmt=fmt): 734 data = plistlib.dumps(pl, fmt=fmt) 735 self.assertEqual(plistlib.loads(data), pl) 736 737 def test_lone_surrogates(self): 738 for fmt in ALL_FORMATS: 739 with self.subTest(fmt=fmt): 740 with self.assertRaises(UnicodeEncodeError): 741 plistlib.dumps('\ud8ff', fmt=fmt) 742 with self.assertRaises(UnicodeEncodeError): 743 plistlib.dumps('\udcff', fmt=fmt) 744 745 def test_nondictroot(self): 746 for fmt in ALL_FORMATS: 747 with self.subTest(fmt=fmt): 748 test1 = "abc" 749 test2 = [1, 2, 3, "abc"] 750 result1 = plistlib.loads(plistlib.dumps(test1, fmt=fmt)) 751 result2 = plistlib.loads(plistlib.dumps(test2, fmt=fmt)) 752 self.assertEqual(test1, result1) 753 self.assertEqual(test2, result2) 754 755 def test_invalidarray(self): 756 for i in ["<key>key inside an array</key>", 757 "<key>key inside an array2</key><real>3</real>", 758 "<true/><key>key inside an array3</key>"]: 759 self.assertRaises(ValueError, plistlib.loads, 760 ("<plist><array>%s</array></plist>"%i).encode()) 761 762 def test_invaliddict(self): 763 for i in ["<key><true/>k</key><string>compound key</string>", 764 "<key>single key</key>", 765 "<string>missing key</string>", 766 "<key>k1</key><string>v1</string><real>5.3</real>" 767 "<key>k1</key><key>k2</key><string>double key</string>"]: 768 self.assertRaises(ValueError, plistlib.loads, 769 ("<plist><dict>%s</dict></plist>"%i).encode()) 770 self.assertRaises(ValueError, plistlib.loads, 771 ("<plist><array><dict>%s</dict></array></plist>"%i).encode()) 772 773 def test_invalidinteger(self): 774 self.assertRaises(ValueError, plistlib.loads, 775 b"<plist><integer>not integer</integer></plist>") 776 777 def test_invalidreal(self): 778 self.assertRaises(ValueError, plistlib.loads, 779 b"<plist><integer>not real</integer></plist>") 780 781 def test_integer_notations(self): 782 pl = b"<plist><integer>456</integer></plist>" 783 value = plistlib.loads(pl) 784 self.assertEqual(value, 456) 785 786 pl = b"<plist><integer>0xa</integer></plist>" 787 value = plistlib.loads(pl) 788 self.assertEqual(value, 10) 789 790 pl = b"<plist><integer>0123</integer></plist>" 791 value = plistlib.loads(pl) 792 self.assertEqual(value, 123) 793 794 def test_xml_encodings(self): 795 base = TESTDATA[plistlib.FMT_XML] 796 797 for xml_encoding, encoding, bom in [ 798 (b'utf-8', 'utf-8', codecs.BOM_UTF8), 799 (b'utf-16', 'utf-16-le', codecs.BOM_UTF16_LE), 800 (b'utf-16', 'utf-16-be', codecs.BOM_UTF16_BE), 801 # Expat does not support UTF-32 802 #(b'utf-32', 'utf-32-le', codecs.BOM_UTF32_LE), 803 #(b'utf-32', 'utf-32-be', codecs.BOM_UTF32_BE), 804 ]: 805 806 pl = self._create(fmt=plistlib.FMT_XML) 807 with self.subTest(encoding=encoding): 808 data = base.replace(b'UTF-8', xml_encoding) 809 data = bom + data.decode('utf-8').encode(encoding) 810 pl2 = plistlib.loads(data) 811 self.assertEqual(dict(pl), dict(pl2)) 812 813 def test_dump_invalid_format(self): 814 with self.assertRaises(ValueError): 815 plistlib.dumps({}, fmt="blah") 816 817 def test_load_invalid_file(self): 818 with self.assertRaises(plistlib.InvalidFileException): 819 plistlib.loads(b"these are not plist file contents") 820 821 def test_modified_uid_negative(self): 822 neg_uid = UID(1) 823 neg_uid.data = -1 # dodge the negative check in the constructor 824 with self.assertRaises(ValueError): 825 plistlib.dumps(neg_uid, fmt=plistlib.FMT_BINARY) 826 827 def test_modified_uid_huge(self): 828 huge_uid = UID(1) 829 huge_uid.data = 2 ** 64 # dodge the size check in the constructor 830 with self.assertRaises(OverflowError): 831 plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY) 832 833 def test_xml_plist_with_entity_decl(self): 834 with self.assertRaisesRegex(plistlib.InvalidFileException, 835 "XML entity declarations are not supported"): 836 plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML) 837 838 839class TestBinaryPlistlib(unittest.TestCase): 840 841 @staticmethod 842 def decode(*objects, offset_size=1, ref_size=1): 843 data = [b'bplist00'] 844 offset = 8 845 offsets = [] 846 for x in objects: 847 offsets.append(offset.to_bytes(offset_size, 'big')) 848 data.append(x) 849 offset += len(x) 850 tail = struct.pack('>6xBBQQQ', offset_size, ref_size, 851 len(objects), 0, offset) 852 data.extend(offsets) 853 data.append(tail) 854 return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY) 855 856 def test_nonstandard_refs_size(self): 857 # Issue #21538: Refs and offsets are 24-bit integers 858 data = (b'bplist00' 859 b'\xd1\x00\x00\x01\x00\x00\x02QaQb' 860 b'\x00\x00\x08\x00\x00\x0f\x00\x00\x11' 861 b'\x00\x00\x00\x00\x00\x00' 862 b'\x03\x03' 863 b'\x00\x00\x00\x00\x00\x00\x00\x03' 864 b'\x00\x00\x00\x00\x00\x00\x00\x00' 865 b'\x00\x00\x00\x00\x00\x00\x00\x13') 866 self.assertEqual(plistlib.loads(data), {'a': 'b'}) 867 868 def test_dump_duplicates(self): 869 # Test effectiveness of saving duplicated objects 870 for x in (None, False, True, 12345, 123.45, 'abcde', 'абвгд', b'abcde', 871 datetime.datetime(2004, 10, 26, 10, 33, 33), 872 bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}): 873 with self.subTest(x=x): 874 data = plistlib.dumps([x]*1000, fmt=plistlib.FMT_BINARY) 875 self.assertLess(len(data), 1100, repr(data)) 876 877 def test_identity(self): 878 for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde', 879 datetime.datetime(2004, 10, 26, 10, 33, 33), 880 bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}): 881 with self.subTest(x=x): 882 data = plistlib.dumps([x]*2, fmt=plistlib.FMT_BINARY) 883 a, b = plistlib.loads(data) 884 if isinstance(x, tuple): 885 x = list(x) 886 self.assertEqual(a, x) 887 self.assertEqual(b, x) 888 self.assertIs(a, b) 889 890 def test_cycles(self): 891 # recursive list 892 a = [] 893 a.append(a) 894 b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY)) 895 self.assertIs(b[0], b) 896 # recursive tuple 897 a = ([],) 898 a[0].append(a) 899 b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY)) 900 self.assertIs(b[0][0], b) 901 # recursive dict 902 a = {} 903 a['x'] = a 904 b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY)) 905 self.assertIs(b['x'], b) 906 907 def test_deep_nesting(self): 908 for N in [300, 100000]: 909 chunks = [b'\xa1' + (i + 1).to_bytes(4, 'big') for i in range(N)] 910 try: 911 result = self.decode(*chunks, b'\x54seed', offset_size=4, ref_size=4) 912 except RecursionError: 913 pass 914 else: 915 for i in range(N): 916 self.assertIsInstance(result, list) 917 self.assertEqual(len(result), 1) 918 result = result[0] 919 self.assertEqual(result, 'seed') 920 921 def test_large_timestamp(self): 922 # Issue #26709: 32-bit timestamp out of range 923 for ts in -2**31-1, 2**31: 924 with self.subTest(ts=ts): 925 d = (datetime.datetime.utcfromtimestamp(0) + 926 datetime.timedelta(seconds=ts)) 927 data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY) 928 self.assertEqual(plistlib.loads(data), d) 929 930 def test_load_singletons(self): 931 self.assertIs(self.decode(b'\x00'), None) 932 self.assertIs(self.decode(b'\x08'), False) 933 self.assertIs(self.decode(b'\x09'), True) 934 self.assertEqual(self.decode(b'\x0f'), b'') 935 936 def test_load_int(self): 937 self.assertEqual(self.decode(b'\x10\x00'), 0) 938 self.assertEqual(self.decode(b'\x10\xfe'), 0xfe) 939 self.assertEqual(self.decode(b'\x11\xfe\xdc'), 0xfedc) 940 self.assertEqual(self.decode(b'\x12\xfe\xdc\xba\x98'), 0xfedcba98) 941 self.assertEqual(self.decode(b'\x13\x01\x23\x45\x67\x89\xab\xcd\xef'), 942 0x0123456789abcdef) 943 self.assertEqual(self.decode(b'\x13\xfe\xdc\xba\x98\x76\x54\x32\x10'), 944 -0x123456789abcdf0) 945 946 def test_unsupported(self): 947 unsupported = [*range(1, 8), *range(10, 15), 948 0x20, 0x21, *range(0x24, 0x33), *range(0x34, 0x40)] 949 for i in [0x70, 0x90, 0xb0, 0xc0, 0xe0, 0xf0]: 950 unsupported.extend(i + j for j in range(16)) 951 for token in unsupported: 952 with self.subTest(f'token {token:02x}'): 953 with self.assertRaises(plistlib.InvalidFileException): 954 self.decode(bytes([token]) + b'\x00'*16) 955 956 def test_invalid_binary(self): 957 for name, data in INVALID_BINARY_PLISTS: 958 with self.subTest(name): 959 with self.assertRaises(plistlib.InvalidFileException): 960 plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY) 961 962 963class TestKeyedArchive(unittest.TestCase): 964 def test_keyed_archive_data(self): 965 # This is the structure of a NSKeyedArchive packed plist 966 data = { 967 '$version': 100000, 968 '$objects': [ 969 '$null', { 970 'pytype': 1, 971 '$class': UID(2), 972 'NS.string': 'KeyArchive UID Test' 973 }, 974 { 975 '$classname': 'OC_BuiltinPythonUnicode', 976 '$classes': [ 977 'OC_BuiltinPythonUnicode', 978 'OC_PythonUnicode', 979 'NSString', 980 'NSObject' 981 ], 982 '$classhints': [ 983 'OC_PythonString', 'NSString' 984 ] 985 } 986 ], 987 '$archiver': 'NSKeyedArchiver', 988 '$top': { 989 'root': UID(1) 990 } 991 } 992 self.assertEqual(plistlib.loads(TESTDATA["KEYED_ARCHIVE"]), data) 993 994 995class MiscTestCase(unittest.TestCase): 996 def test__all__(self): 997 not_exported = {"PlistFormat", "PLISTHEADER"} 998 support.check__all__(self, plistlib, not_exported=not_exported) 999 1000 1001if __name__ == '__main__': 1002 unittest.main() 1003