blob: c77a6bf9d03fd385baa58885f2d23255a6c72883 [file] [log] [blame]
Ronald Oussorenc5cf7972013-11-21 15:46:49 +01001# Copyright (C) 2003-2013 Python Software Foundation
Jack Jansen838e76a2003-02-25 12:58:58 +00002
3import unittest
4import plistlib
5import os
Just van Rossumfc93e172004-10-26 11:02:08 +00006import datetime
Ronald Oussorenc5cf7972013-11-21 15:46:49 +01007import codecs
8import binascii
9import collections
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Ronald Oussorenc5cf7972013-11-21 15:46:49 +010011from io import BytesIO
Jack Jansen838e76a2003-02-25 12:58:58 +000012
Ronald Oussorenc5cf7972013-11-21 15:46:49 +010013ALL_FORMATS=(plistlib.FMT_XML, plistlib.FMT_BINARY)
Just van Rossumfc93e172004-10-26 11:02:08 +000014
Ronald Oussorenc5cf7972013-11-21 15:46:49 +010015# The testdata is generated using Mac/Tools/plistlib_generate_testdata.py
16# (which using PyObjC to control the Cocoa classes for generating plists)
17TESTDATA={
18 plistlib.FMT_XML: binascii.a2b_base64(b'''
19 PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU
20 WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO
21 IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w
Ronald Oussoren6db66532014-01-15 11:32:35 +010022 LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+YUJp
23 Z0ludDwva2V5PgoJPGludGVnZXI+OTIyMzM3MjAzNjg1NDc3NTc2NDwvaW50
Ronald Oussoren94e44a92014-02-06 11:19:18 +010024 ZWdlcj4KCTxrZXk+YUJpZ0ludDI8L2tleT4KCTxpbnRlZ2VyPjkyMjMzNzIw
25 MzY4NTQ3NzU4NTI8L2ludGVnZXI+Cgk8a2V5PmFEYXRlPC9rZXk+Cgk8ZGF0
26 ZT4yMDA0LTEwLTI2VDEwOjMzOjMzWjwvZGF0ZT4KCTxrZXk+YURpY3Q8L2tl
27 eT4KCTxkaWN0PgoJCTxrZXk+YUZhbHNlVmFsdWU8L2tleT4KCQk8ZmFsc2Uv
28 PgoJCTxrZXk+YVRydWVWYWx1ZTwva2V5PgoJCTx0cnVlLz4KCQk8a2V5PmFV
29 bmljb2RlVmFsdWU8L2tleT4KCQk8c3RyaW5nPk3DpHNzaWcsIE1hw588L3N0
30 cmluZz4KCQk8a2V5PmFub3RoZXJTdHJpbmc8L2tleT4KCQk8c3RyaW5nPiZs
31 dDtoZWxsbyAmYW1wOyAnaGknIHRoZXJlISZndDs8L3N0cmluZz4KCQk8a2V5
32 PmRlZXBlckRpY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5hPC9rZXk+CgkJ
33 CTxpbnRlZ2VyPjE3PC9pbnRlZ2VyPgoJCQk8a2V5PmI8L2tleT4KCQkJPHJl
34 YWw+MzIuNTwvcmVhbD4KCQkJPGtleT5jPC9rZXk+CgkJCTxhcnJheT4KCQkJ
35 CTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8aW50ZWdlcj4yPC9pbnRlZ2Vy
36 PgoJCQkJPHN0cmluZz50ZXh0PC9zdHJpbmc+CgkJCTwvYXJyYXk+CgkJPC9k
37 aWN0PgoJPC9kaWN0PgoJPGtleT5hRmxvYXQ8L2tleT4KCTxyZWFsPjAuNTwv
38 cmVhbD4KCTxrZXk+YUxpc3Q8L2tleT4KCTxhcnJheT4KCQk8c3RyaW5nPkE8
39 L3N0cmluZz4KCQk8c3RyaW5nPkI8L3N0cmluZz4KCQk8aW50ZWdlcj4xMjwv
40 aW50ZWdlcj4KCQk8cmVhbD4zMi41PC9yZWFsPgoJCTxhcnJheT4KCQkJPGlu
41 dGVnZXI+MTwvaW50ZWdlcj4KCQkJPGludGVnZXI+MjwvaW50ZWdlcj4KCQkJ
42 PGludGVnZXI+MzwvaW50ZWdlcj4KCQk8L2FycmF5PgoJPC9hcnJheT4KCTxr
43 ZXk+YU5lZ2F0aXZlQmlnSW50PC9rZXk+Cgk8aW50ZWdlcj4tODAwMDAwMDAw
44 MDA8L2ludGVnZXI+Cgk8a2V5PmFOZWdhdGl2ZUludDwva2V5PgoJPGludGVn
45 ZXI+LTU8L2ludGVnZXI+Cgk8a2V5PmFTdHJpbmc8L2tleT4KCTxzdHJpbmc+
46 RG9vZGFoPC9zdHJpbmc+Cgk8a2V5PmFuRW1wdHlEaWN0PC9rZXk+Cgk8ZGlj
47 dC8+Cgk8a2V5PmFuRW1wdHlMaXN0PC9rZXk+Cgk8YXJyYXkvPgoJPGtleT5h
48 bkludDwva2V5PgoJPGludGVnZXI+NzI4PC9pbnRlZ2VyPgoJPGtleT5uZXN0
49 ZWREYXRhPC9rZXk+Cgk8YXJyYXk+CgkJPGRhdGE+CgkJUEd4dmRITWdiMlln
Ronald Oussoren6db66532014-01-15 11:32:35 +010050 WW1sdVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5k
Ronald Oussoren94e44a92014-02-06 11:19:18 +010051 VzVyCgkJUGdBQkFnTThiRzkwY3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJ
52 RFBHeHZkSE1nYjJZZ1ltbHVZWEo1CgkJSUdkMWJtcytBQUVDQXp4c2IzUnpJ
53 RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004Ykc5MGN5QnZaaUJpCgkJYVc1
54 aGNua2daM1Z1YXo0QUFRSURQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMr
55 QUFFQ0F6eHNiM1J6CgkJSUc5bUlHSnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJH
56 OTBjeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlECgkJUEd4dmRITWdiMlln
57 WW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09CgkJPC9kYXRhPgoJPC9hcnJheT4K
58 CTxrZXk+c29tZURhdGE8L2tleT4KCTxkYXRhPgoJUEdKcGJtRnllU0JuZFc1
59 clBnPT0KCTwvZGF0YT4KCTxrZXk+c29tZU1vcmVEYXRhPC9rZXk+Cgk8ZGF0
60 YT4KCVBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytBQUVDQXp4c2IzUnpJ
61 RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004CgliRzkwY3lCdlppQmlhVzVo
62 Y25rZ1ozVnVhejRBQVFJRFBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytB
63 QUVDQXp4cwoJYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVyUGdBQkFnTThiRzkw
64 Y3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJRFBHeHYKCWRITWdiMllnWW1s
65 dVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVy
66 UGdBQkFnTThiRzkwCgljeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlEUEd4
67 dmRITWdiMllnWW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09Cgk8L2RhdGE+Cgk8
68 a2V5PsOFYmVucmFhPC9rZXk+Cgk8c3RyaW5nPlRoYXQgd2FzIGEgdW5pY29k
69 ZSBrZXkuPC9zdHJpbmc+CjwvZGljdD4KPC9wbGlzdD4K'''),
Ronald Oussorenc5cf7972013-11-21 15:46:49 +010070 plistlib.FMT_BINARY: binascii.a2b_base64(b'''
Ronald Oussoren94e44a92014-02-06 11:19:18 +010071 YnBsaXN0MDDfEBABAgMEBQYHCAkKCwwNDg8QERITFCgpLzAxMjM0NTc2OFdh
72 QmlnSW50WGFCaWdJbnQyVWFEYXRlVWFEaWN0VmFGbG9hdFVhTGlzdF8QD2FO
73 ZWdhdGl2ZUJpZ0ludFxhTmVnYXRpdmVJbnRXYVN0cmluZ1thbkVtcHR5RGlj
74 dFthbkVtcHR5TGlzdFVhbkludFpuZXN0ZWREYXRhWHNvbWVEYXRhXHNvbWVN
75 b3JlRGF0YWcAxQBiAGUAbgByAGEAYRN/////////1BQAAAAAAAAAAIAAAAAA
76 AAAsM0GcuX30AAAA1RUWFxgZGhscHR5bYUZhbHNlVmFsdWVaYVRydWVWYWx1
77 ZV1hVW5pY29kZVZhbHVlXWFub3RoZXJTdHJpbmdaZGVlcGVyRGljdAgJawBN
78 AOQAcwBzAGkAZwAsACAATQBhAN9fEBU8aGVsbG8gJiAnaGknIHRoZXJlIT7T
79 HyAhIiMkUWFRYlFjEBEjQEBAAAAAAACjJSYnEAEQAlR0ZXh0Iz/gAAAAAAAA
80 pSorLCMtUUFRQhAMoyUmLhADE////+1foOAAE//////////7VkRvb2RhaNCg
81 EQLYoTZPEPo8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmlu
82 YXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBv
83 ZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs
84 b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4A
85 AQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBn
86 dW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDTTxiaW5hcnkgZ3Vu
87 az5fEBdUaGF0IHdhcyBhIHVuaWNvZGUga2V5LgAIACsAMwA8AEIASABPAFUA
88 ZwB0AHwAiACUAJoApQCuALsAygDTAOQA7QD4AQQBDwEdASsBNgE3ATgBTwFn
89 AW4BcAFyAXQBdgF/AYMBhQGHAYwBlQGbAZ0BnwGhAaUBpwGwAbkBwAHBAcIB
90 xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''),
Ronald Oussorenc5cf7972013-11-21 15:46:49 +010091}
Just van Rossumfc93e172004-10-26 11:02:08 +000092
93
Jack Jansen838e76a2003-02-25 12:58:58 +000094class TestPlistlib(unittest.TestCase):
95
96 def tearDown(self):
97 try:
Benjamin Petersonee8712c2008-05-20 21:35:26 +000098 os.unlink(support.TESTFN)
Jack Jansen838e76a2003-02-25 12:58:58 +000099 except:
100 pass
101
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100102 def _create(self, fmt=None):
Just van Rossumfc93e172004-10-26 11:02:08 +0000103 pl = dict(
Jack Jansen838e76a2003-02-25 12:58:58 +0000104 aString="Doodah",
Just van Rossumfc93e172004-10-26 11:02:08 +0000105 aList=["A", "B", 12, 32.5, [1, 2, 3]],
106 aFloat = 0.5,
Jack Jansen838e76a2003-02-25 12:58:58 +0000107 anInt = 728,
Ronald Oussoren6db66532014-01-15 11:32:35 +0100108 aBigInt = 2 ** 63 - 44,
Ronald Oussoren94e44a92014-02-06 11:19:18 +0100109 aBigInt2 = 2 ** 63 + 44,
Ronald Oussoren6db66532014-01-15 11:32:35 +0100110 aNegativeInt = -5,
111 aNegativeBigInt = -80000000000,
Just van Rossumfc93e172004-10-26 11:02:08 +0000112 aDict=dict(
113 anotherString="<hello & 'hi' there!>",
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000114 aUnicodeValue='M\xe4ssig, Ma\xdf',
Jack Jansen838e76a2003-02-25 12:58:58 +0000115 aTrueValue=True,
116 aFalseValue=False,
Just van Rossumfc93e172004-10-26 11:02:08 +0000117 deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
Jack Jansen838e76a2003-02-25 12:58:58 +0000118 ),
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100119 someData = b"<binary gunk>",
120 someMoreData = b"<lots of binary gunk>\0\1\2\3" * 10,
121 nestedData = [b"<lots of binary gunk>\0\1\2\3" * 10],
Just van Rossumfc93e172004-10-26 11:02:08 +0000122 aDate = datetime.datetime(2004, 10, 26, 10, 33, 33),
Hynek Schlawack52209d32012-05-29 12:04:54 +0200123 anEmptyDict = dict(),
124 anEmptyList = list()
Jack Jansen838e76a2003-02-25 12:58:58 +0000125 )
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000126 pl['\xc5benraa'] = "That was a unicode key."
Jack Jansen838e76a2003-02-25 12:58:58 +0000127 return pl
Tim Peters669454e2003-03-07 17:30:48 +0000128
Jack Jansen838e76a2003-02-25 12:58:58 +0000129 def test_create(self):
130 pl = self._create()
131 self.assertEqual(pl["aString"], "Doodah")
132 self.assertEqual(pl["aDict"]["aFalseValue"], False)
133
134 def test_io(self):
135 pl = self._create()
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100136 with open(support.TESTFN, 'wb') as fp:
137 plistlib.dump(pl, fp)
138
139 with open(support.TESTFN, 'rb') as fp:
140 pl2 = plistlib.load(fp)
141
Tim Peters669454e2003-03-07 17:30:48 +0000142 self.assertEqual(dict(pl), dict(pl2))
Jack Jansen838e76a2003-02-25 12:58:58 +0000143
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100144 self.assertRaises(AttributeError, plistlib.dump, pl, 'filename')
145 self.assertRaises(AttributeError, plistlib.load, 'filename')
146
Ronald Oussoren6db66532014-01-15 11:32:35 +0100147 def test_invalid_type(self):
148 pl = [ object() ]
149
150 for fmt in ALL_FORMATS:
151 with self.subTest(fmt=fmt):
152 self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt)
153
154 def test_int(self):
155 for pl in [0, 2**8-1, 2**8, 2**16-1, 2**16, 2**32-1, 2**32,
Ronald Oussoren94e44a92014-02-06 11:19:18 +0100156 2**63-1, 2**64-1, 1, -2**63]:
Ronald Oussoren6db66532014-01-15 11:32:35 +0100157 for fmt in ALL_FORMATS:
158 with self.subTest(pl=pl, fmt=fmt):
159 data = plistlib.dumps(pl, fmt=fmt)
160 pl2 = plistlib.loads(data)
161 self.assertIsInstance(pl2, int)
162 self.assertEqual(pl, pl2)
163 data2 = plistlib.dumps(pl2, fmt=fmt)
164 self.assertEqual(data, data2)
165
166 for fmt in ALL_FORMATS:
167 for pl in (2 ** 64 + 1, 2 ** 127-1, -2**64, -2 ** 127):
168 with self.subTest(pl=pl, fmt=fmt):
169 self.assertRaises(OverflowError, plistlib.dumps,
170 pl, fmt=fmt)
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100171
Guido van Rossumcd869d82007-08-07 14:26:40 +0000172 def test_bytes(self):
Just van Rossumfc93e172004-10-26 11:02:08 +0000173 pl = self._create()
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100174 data = plistlib.dumps(pl)
175 pl2 = plistlib.loads(data)
176 self.assertNotIsInstance(pl, plistlib._InternalDict)
Just van Rossumfc93e172004-10-26 11:02:08 +0000177 self.assertEqual(dict(pl), dict(pl2))
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100178 data2 = plistlib.dumps(pl2)
Just van Rossumfc93e172004-10-26 11:02:08 +0000179 self.assertEqual(data, data2)
180
Ronald Oussoren326edfd2013-04-23 13:47:22 +0200181 def test_indentation_array(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100182 data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]]
183 self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
Ronald Oussoren326edfd2013-04-23 13:47:22 +0200184
185 def test_indentation_dict(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100186 data = {'1': {'2': {'3': {'4': {'5': {'6': {'7': {'8': {'9': b'aaaaaa'}}}}}}}}}
187 self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
Ronald Oussoren326edfd2013-04-23 13:47:22 +0200188
189 def test_indentation_dict_mix(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100190 data = {'1': {'2': [{'3': [[[[[{'test': b'aaaaaa'}]]]]]}]}}
191 self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)
Ronald Oussoren326edfd2013-04-23 13:47:22 +0200192
Just van Rossumfc93e172004-10-26 11:02:08 +0000193 def test_appleformatting(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100194 for use_builtin_types in (True, False):
195 for fmt in ALL_FORMATS:
196 with self.subTest(fmt=fmt, use_builtin_types=use_builtin_types):
197 pl = plistlib.loads(TESTDATA[fmt],
198 use_builtin_types=use_builtin_types)
199 data = plistlib.dumps(pl, fmt=fmt)
200 self.assertEqual(data, TESTDATA[fmt],
201 "generated data was not identical to Apple's output")
202
Just van Rossumfc93e172004-10-26 11:02:08 +0000203
204 def test_appleformattingfromliteral(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100205 self.maxDiff = None
206 for fmt in ALL_FORMATS:
207 with self.subTest(fmt=fmt):
208 pl = self._create(fmt=fmt)
Serhiy Storchaka89667592014-07-23 18:49:31 +0300209 pl2 = plistlib.loads(TESTDATA[fmt], fmt=fmt)
210 self.assertEqual(dict(pl), dict(pl2),
211 "generated data was not identical to Apple's output")
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100212 pl2 = plistlib.loads(TESTDATA[fmt])
213 self.assertEqual(dict(pl), dict(pl2),
214 "generated data was not identical to Apple's output")
Just van Rossumfc93e172004-10-26 11:02:08 +0000215
Guido van Rossumcd869d82007-08-07 14:26:40 +0000216 def test_bytesio(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100217 for fmt in ALL_FORMATS:
218 with self.subTest(fmt=fmt):
219 b = BytesIO()
220 pl = self._create(fmt=fmt)
221 plistlib.dump(pl, b, fmt=fmt)
Serhiy Storchaka89667592014-07-23 18:49:31 +0300222 pl2 = plistlib.load(BytesIO(b.getvalue()), fmt=fmt)
223 self.assertEqual(dict(pl), dict(pl2))
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100224 pl2 = plistlib.load(BytesIO(b.getvalue()))
225 self.assertEqual(dict(pl), dict(pl2))
226
227 def test_keysort_bytesio(self):
228 pl = collections.OrderedDict()
229 pl['b'] = 1
230 pl['a'] = 2
231 pl['c'] = 3
232
233 for fmt in ALL_FORMATS:
234 for sort_keys in (False, True):
235 with self.subTest(fmt=fmt, sort_keys=sort_keys):
236 b = BytesIO()
237
238 plistlib.dump(pl, b, fmt=fmt, sort_keys=sort_keys)
239 pl2 = plistlib.load(BytesIO(b.getvalue()),
240 dict_type=collections.OrderedDict)
241
242 self.assertEqual(dict(pl), dict(pl2))
243 if sort_keys:
244 self.assertEqual(list(pl2.keys()), ['a', 'b', 'c'])
245 else:
246 self.assertEqual(list(pl2.keys()), ['b', 'a', 'c'])
247
248 def test_keysort(self):
249 pl = collections.OrderedDict()
250 pl['b'] = 1
251 pl['a'] = 2
252 pl['c'] = 3
253
254 for fmt in ALL_FORMATS:
255 for sort_keys in (False, True):
256 with self.subTest(fmt=fmt, sort_keys=sort_keys):
257 data = plistlib.dumps(pl, fmt=fmt, sort_keys=sort_keys)
258 pl2 = plistlib.loads(data, dict_type=collections.OrderedDict)
259
260 self.assertEqual(dict(pl), dict(pl2))
261 if sort_keys:
262 self.assertEqual(list(pl2.keys()), ['a', 'b', 'c'])
263 else:
264 self.assertEqual(list(pl2.keys()), ['b', 'a', 'c'])
265
266 def test_keys_no_string(self):
267 pl = { 42: 'aNumber' }
268
269 for fmt in ALL_FORMATS:
270 with self.subTest(fmt=fmt):
271 self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt)
272
273 b = BytesIO()
274 self.assertRaises(TypeError, plistlib.dump, pl, b, fmt=fmt)
275
276 def test_skipkeys(self):
277 pl = {
278 42: 'aNumber',
279 'snake': 'aWord',
280 }
281
282 for fmt in ALL_FORMATS:
283 with self.subTest(fmt=fmt):
284 data = plistlib.dumps(
285 pl, fmt=fmt, skipkeys=True, sort_keys=False)
286
287 pl2 = plistlib.loads(data)
288 self.assertEqual(pl2, {'snake': 'aWord'})
289
290 fp = BytesIO()
291 plistlib.dump(
292 pl, fp, fmt=fmt, skipkeys=True, sort_keys=False)
293 data = fp.getvalue()
294 pl2 = plistlib.loads(fp.getvalue())
295 self.assertEqual(pl2, {'snake': 'aWord'})
296
297 def test_tuple_members(self):
298 pl = {
299 'first': (1, 2),
300 'second': (1, 2),
301 'third': (3, 4),
302 }
303
304 for fmt in ALL_FORMATS:
305 with self.subTest(fmt=fmt):
306 data = plistlib.dumps(pl, fmt=fmt)
307 pl2 = plistlib.loads(data)
308 self.assertEqual(pl2, {
309 'first': [1, 2],
310 'second': [1, 2],
311 'third': [3, 4],
312 })
313 self.assertIsNot(pl2['first'], pl2['second'])
314
315 def test_list_members(self):
316 pl = {
317 'first': [1, 2],
318 'second': [1, 2],
319 'third': [3, 4],
320 }
321
322 for fmt in ALL_FORMATS:
323 with self.subTest(fmt=fmt):
324 data = plistlib.dumps(pl, fmt=fmt)
325 pl2 = plistlib.loads(data)
326 self.assertEqual(pl2, {
327 'first': [1, 2],
328 'second': [1, 2],
329 'third': [3, 4],
330 })
331 self.assertIsNot(pl2['first'], pl2['second'])
332
333 def test_dict_members(self):
334 pl = {
335 'first': {'a': 1},
336 'second': {'a': 1},
337 'third': {'b': 2 },
338 }
339
340 for fmt in ALL_FORMATS:
341 with self.subTest(fmt=fmt):
342 data = plistlib.dumps(pl, fmt=fmt)
343 pl2 = plistlib.loads(data)
344 self.assertEqual(pl2, {
345 'first': {'a': 1},
346 'second': {'a': 1},
347 'third': {'b': 2 },
348 })
349 self.assertIsNot(pl2['first'], pl2['second'])
Just van Rossumbcc58e82003-07-01 20:22:30 +0000350
Just van Rossum48ecacc2004-11-12 08:34:32 +0000351 def test_controlcharacters(self):
Just van Rossum2dae7642004-11-12 09:36:12 +0000352 for i in range(128):
353 c = chr(i)
354 testString = "string containing %s" % c
355 if i >= 32 or c in "\r\n\t":
356 # \r, \n and \t are the only legal control chars in XML
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100357 plistlib.dumps(testString, fmt=plistlib.FMT_XML)
Just van Rossum2dae7642004-11-12 09:36:12 +0000358 else:
359 self.assertRaises(ValueError,
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100360 plistlib.dumps,
Just van Rossum2dae7642004-11-12 09:36:12 +0000361 testString)
Just van Rossum48ecacc2004-11-12 08:34:32 +0000362
Serhiy Storchaka7338ebc2016-10-04 20:04:30 +0300363 def test_non_bmp_characters(self):
364 pl = {'python': '\U0001f40d'}
365 for fmt in ALL_FORMATS:
366 with self.subTest(fmt=fmt):
367 data = plistlib.dumps(pl, fmt=fmt)
368 self.assertEqual(plistlib.loads(data), pl)
369
Just van Rossum48ecacc2004-11-12 08:34:32 +0000370 def test_nondictroot(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100371 for fmt in ALL_FORMATS:
372 with self.subTest(fmt=fmt):
373 test1 = "abc"
374 test2 = [1, 2, 3, "abc"]
375 result1 = plistlib.loads(plistlib.dumps(test1, fmt=fmt))
376 result2 = plistlib.loads(plistlib.dumps(test2, fmt=fmt))
377 self.assertEqual(test1, result1)
378 self.assertEqual(test2, result2)
Jack Jansen838e76a2003-02-25 12:58:58 +0000379
Ned Deilyb8e59f72011-05-28 02:19:19 -0700380 def test_invalidarray(self):
381 for i in ["<key>key inside an array</key>",
382 "<key>key inside an array2</key><real>3</real>",
383 "<true/><key>key inside an array3</key>"]:
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100384 self.assertRaises(ValueError, plistlib.loads,
Ned Deilyb8e59f72011-05-28 02:19:19 -0700385 ("<plist><array>%s</array></plist>"%i).encode())
386
387 def test_invaliddict(self):
388 for i in ["<key><true/>k</key><string>compound key</string>",
389 "<key>single key</key>",
390 "<string>missing key</string>",
391 "<key>k1</key><string>v1</string><real>5.3</real>"
392 "<key>k1</key><key>k2</key><string>double key</string>"]:
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100393 self.assertRaises(ValueError, plistlib.loads,
Ned Deilyb8e59f72011-05-28 02:19:19 -0700394 ("<plist><dict>%s</dict></plist>"%i).encode())
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100395 self.assertRaises(ValueError, plistlib.loads,
Ned Deilyb8e59f72011-05-28 02:19:19 -0700396 ("<plist><array><dict>%s</dict></array></plist>"%i).encode())
397
398 def test_invalidinteger(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100399 self.assertRaises(ValueError, plistlib.loads,
Ned Deilyb8e59f72011-05-28 02:19:19 -0700400 b"<plist><integer>not integer</integer></plist>")
401
402 def test_invalidreal(self):
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100403 self.assertRaises(ValueError, plistlib.loads,
Ned Deilyb8e59f72011-05-28 02:19:19 -0700404 b"<plist><integer>not real</integer></plist>")
405
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100406 def test_xml_encodings(self):
407 base = TESTDATA[plistlib.FMT_XML]
408
409 for xml_encoding, encoding, bom in [
410 (b'utf-8', 'utf-8', codecs.BOM_UTF8),
411 (b'utf-16', 'utf-16-le', codecs.BOM_UTF16_LE),
412 (b'utf-16', 'utf-16-be', codecs.BOM_UTF16_BE),
413 # Expat does not support UTF-32
414 #(b'utf-32', 'utf-32-le', codecs.BOM_UTF32_LE),
415 #(b'utf-32', 'utf-32-be', codecs.BOM_UTF32_BE),
416 ]:
417
418 pl = self._create(fmt=plistlib.FMT_XML)
419 with self.subTest(encoding=encoding):
420 data = base.replace(b'UTF-8', xml_encoding)
421 data = bom + data.decode('utf-8').encode(encoding)
422 pl2 = plistlib.loads(data)
423 self.assertEqual(dict(pl), dict(pl2))
424
Serhiy Storchaka06526642014-05-23 16:13:33 +0300425 def test_nonstandard_refs_size(self):
426 # Issue #21538: Refs and offsets are 24-bit integers
427 data = (b'bplist00'
428 b'\xd1\x00\x00\x01\x00\x00\x02QaQb'
429 b'\x00\x00\x08\x00\x00\x0f\x00\x00\x11'
430 b'\x00\x00\x00\x00\x00\x00'
431 b'\x03\x03'
432 b'\x00\x00\x00\x00\x00\x00\x00\x03'
433 b'\x00\x00\x00\x00\x00\x00\x00\x00'
434 b'\x00\x00\x00\x00\x00\x00\x00\x13')
435 self.assertEqual(plistlib.loads(data), {'a': 'b'})
436
Serhiy Storchaka94ad49f2016-04-08 15:00:02 +0300437 def test_large_timestamp(self):
438 # Issue #26709: 32-bit timestamp out of range
439 for ts in -2**31-1, 2**31:
440 with self.subTest(ts=ts):
441 d = (datetime.datetime.utcfromtimestamp(0) +
442 datetime.timedelta(seconds=ts))
443 data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
444 self.assertEqual(plistlib.loads(data), d)
445
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100446
447class TestPlistlibDeprecated(unittest.TestCase):
448 def test_io_deprecated(self):
449 pl_in = {
450 'key': 42,
451 'sub': {
452 'key': 9,
453 'alt': 'value',
454 'data': b'buffer',
455 }
456 }
457 pl_out = plistlib._InternalDict({
458 'key': 42,
459 'sub': plistlib._InternalDict({
460 'key': 9,
461 'alt': 'value',
462 'data': plistlib.Data(b'buffer'),
463 })
464 })
465
466 self.addCleanup(support.unlink, support.TESTFN)
467 with self.assertWarns(DeprecationWarning):
468 plistlib.writePlist(pl_in, support.TESTFN)
469
470 with self.assertWarns(DeprecationWarning):
471 pl2 = plistlib.readPlist(support.TESTFN)
472
473 self.assertEqual(pl_out, pl2)
474
475 os.unlink(support.TESTFN)
476
477 with open(support.TESTFN, 'wb') as fp:
478 with self.assertWarns(DeprecationWarning):
479 plistlib.writePlist(pl_in, fp)
480
481 with open(support.TESTFN, 'rb') as fp:
482 with self.assertWarns(DeprecationWarning):
483 pl2 = plistlib.readPlist(fp)
484
485 self.assertEqual(pl_out, pl2)
486
487 def test_bytes_deprecated(self):
488 pl = {
489 'key': 42,
490 'sub': {
491 'key': 9,
492 'alt': 'value',
493 'data': b'buffer',
494 }
495 }
496 with self.assertWarns(DeprecationWarning):
497 data = plistlib.writePlistToBytes(pl)
498
499 with self.assertWarns(DeprecationWarning):
500 pl2 = plistlib.readPlistFromBytes(data)
501
502 self.assertIsInstance(pl2, plistlib._InternalDict)
503 self.assertEqual(pl2, plistlib._InternalDict(
504 key=42,
505 sub=plistlib._InternalDict(
506 key=9,
507 alt='value',
508 data=plistlib.Data(b'buffer'),
509 )
510 ))
511
512 with self.assertWarns(DeprecationWarning):
513 data2 = plistlib.writePlistToBytes(pl2)
514 self.assertEqual(data, data2)
515
516 def test_dataobject_deprecated(self):
517 in_data = { 'key': plistlib.Data(b'hello') }
518 out_data = { 'key': b'hello' }
519
520 buf = plistlib.dumps(in_data)
521
522 cur = plistlib.loads(buf)
523 self.assertEqual(cur, out_data)
Serhiy Storchakadd1bcdf2016-05-01 13:36:16 +0300524 self.assertEqual(cur, in_data)
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100525
526 cur = plistlib.loads(buf, use_builtin_types=False)
Serhiy Storchakadd1bcdf2016-05-01 13:36:16 +0300527 self.assertEqual(cur, out_data)
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100528 self.assertEqual(cur, in_data)
529
530 with self.assertWarns(DeprecationWarning):
531 cur = plistlib.readPlistFromBytes(buf)
Serhiy Storchakadd1bcdf2016-05-01 13:36:16 +0300532 self.assertEqual(cur, out_data)
Ronald Oussorenc5cf7972013-11-21 15:46:49 +0100533 self.assertEqual(cur, in_data)
534
Jack Jansen838e76a2003-02-25 12:58:58 +0000535
Martin Panterd04d2132016-06-06 02:00:50 +0000536class MiscTestCase(unittest.TestCase):
537 def test__all__(self):
538 blacklist = {"PlistFormat", "PLISTHEADER"}
539 support.check__all__(self, plistlib, blacklist=blacklist)
540
541
Jack Jansen838e76a2003-02-25 12:58:58 +0000542def test_main():
Martin Panterd04d2132016-06-06 02:00:50 +0000543 support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase)
Jack Jansen838e76a2003-02-25 12:58:58 +0000544
545
546if __name__ == '__main__':
547 test_main()