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