1 | n/a | # Copyright (C) 2003-2013 Python Software Foundation |
---|
2 | n/a | |
---|
3 | n/a | import unittest |
---|
4 | n/a | import plistlib |
---|
5 | n/a | import os |
---|
6 | n/a | import datetime |
---|
7 | n/a | import codecs |
---|
8 | n/a | import binascii |
---|
9 | n/a | import collections |
---|
10 | n/a | from test import support |
---|
11 | n/a | from io import BytesIO |
---|
12 | n/a | |
---|
13 | n/a | ALL_FORMATS=(plistlib.FMT_XML, plistlib.FMT_BINARY) |
---|
14 | n/a | |
---|
15 | n/a | # The testdata is generated using Mac/Tools/plistlib_generate_testdata.py |
---|
16 | n/a | # (which using PyObjC to control the Cocoa classes for generating plists) |
---|
17 | n/a | TESTDATA={ |
---|
18 | n/a | plistlib.FMT_XML: binascii.a2b_base64(b''' |
---|
19 | n/a | PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NU |
---|
20 | n/a | WVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VO |
---|
21 | n/a | IiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4w |
---|
22 | n/a | LmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+YUJp |
---|
23 | n/a | Z0ludDwva2V5PgoJPGludGVnZXI+OTIyMzM3MjAzNjg1NDc3NTc2NDwvaW50 |
---|
24 | n/a | ZWdlcj4KCTxrZXk+YUJpZ0ludDI8L2tleT4KCTxpbnRlZ2VyPjkyMjMzNzIw |
---|
25 | n/a | MzY4NTQ3NzU4NTI8L2ludGVnZXI+Cgk8a2V5PmFEYXRlPC9rZXk+Cgk8ZGF0 |
---|
26 | n/a | ZT4yMDA0LTEwLTI2VDEwOjMzOjMzWjwvZGF0ZT4KCTxrZXk+YURpY3Q8L2tl |
---|
27 | n/a | eT4KCTxkaWN0PgoJCTxrZXk+YUZhbHNlVmFsdWU8L2tleT4KCQk8ZmFsc2Uv |
---|
28 | n/a | PgoJCTxrZXk+YVRydWVWYWx1ZTwva2V5PgoJCTx0cnVlLz4KCQk8a2V5PmFV |
---|
29 | n/a | bmljb2RlVmFsdWU8L2tleT4KCQk8c3RyaW5nPk3DpHNzaWcsIE1hw588L3N0 |
---|
30 | n/a | cmluZz4KCQk8a2V5PmFub3RoZXJTdHJpbmc8L2tleT4KCQk8c3RyaW5nPiZs |
---|
31 | n/a | dDtoZWxsbyAmYW1wOyAnaGknIHRoZXJlISZndDs8L3N0cmluZz4KCQk8a2V5 |
---|
32 | n/a | PmRlZXBlckRpY3Q8L2tleT4KCQk8ZGljdD4KCQkJPGtleT5hPC9rZXk+CgkJ |
---|
33 | n/a | CTxpbnRlZ2VyPjE3PC9pbnRlZ2VyPgoJCQk8a2V5PmI8L2tleT4KCQkJPHJl |
---|
34 | n/a | YWw+MzIuNTwvcmVhbD4KCQkJPGtleT5jPC9rZXk+CgkJCTxhcnJheT4KCQkJ |
---|
35 | n/a | CTxpbnRlZ2VyPjE8L2ludGVnZXI+CgkJCQk8aW50ZWdlcj4yPC9pbnRlZ2Vy |
---|
36 | n/a | PgoJCQkJPHN0cmluZz50ZXh0PC9zdHJpbmc+CgkJCTwvYXJyYXk+CgkJPC9k |
---|
37 | n/a | aWN0PgoJPC9kaWN0PgoJPGtleT5hRmxvYXQ8L2tleT4KCTxyZWFsPjAuNTwv |
---|
38 | n/a | cmVhbD4KCTxrZXk+YUxpc3Q8L2tleT4KCTxhcnJheT4KCQk8c3RyaW5nPkE8 |
---|
39 | n/a | L3N0cmluZz4KCQk8c3RyaW5nPkI8L3N0cmluZz4KCQk8aW50ZWdlcj4xMjwv |
---|
40 | n/a | aW50ZWdlcj4KCQk8cmVhbD4zMi41PC9yZWFsPgoJCTxhcnJheT4KCQkJPGlu |
---|
41 | n/a | dGVnZXI+MTwvaW50ZWdlcj4KCQkJPGludGVnZXI+MjwvaW50ZWdlcj4KCQkJ |
---|
42 | n/a | PGludGVnZXI+MzwvaW50ZWdlcj4KCQk8L2FycmF5PgoJPC9hcnJheT4KCTxr |
---|
43 | n/a | ZXk+YU5lZ2F0aXZlQmlnSW50PC9rZXk+Cgk8aW50ZWdlcj4tODAwMDAwMDAw |
---|
44 | n/a | MDA8L2ludGVnZXI+Cgk8a2V5PmFOZWdhdGl2ZUludDwva2V5PgoJPGludGVn |
---|
45 | n/a | ZXI+LTU8L2ludGVnZXI+Cgk8a2V5PmFTdHJpbmc8L2tleT4KCTxzdHJpbmc+ |
---|
46 | n/a | RG9vZGFoPC9zdHJpbmc+Cgk8a2V5PmFuRW1wdHlEaWN0PC9rZXk+Cgk8ZGlj |
---|
47 | n/a | dC8+Cgk8a2V5PmFuRW1wdHlMaXN0PC9rZXk+Cgk8YXJyYXkvPgoJPGtleT5h |
---|
48 | n/a | bkludDwva2V5PgoJPGludGVnZXI+NzI4PC9pbnRlZ2VyPgoJPGtleT5uZXN0 |
---|
49 | n/a | ZWREYXRhPC9rZXk+Cgk8YXJyYXk+CgkJPGRhdGE+CgkJUEd4dmRITWdiMlln |
---|
50 | n/a | WW1sdVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5k |
---|
51 | n/a | VzVyCgkJUGdBQkFnTThiRzkwY3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJ |
---|
52 | n/a | RFBHeHZkSE1nYjJZZ1ltbHVZWEo1CgkJSUdkMWJtcytBQUVDQXp4c2IzUnpJ |
---|
53 | n/a | RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004Ykc5MGN5QnZaaUJpCgkJYVc1 |
---|
54 | n/a | aGNua2daM1Z1YXo0QUFRSURQR3h2ZEhNZ2IyWWdZbWx1WVhKNUlHZDFibXMr |
---|
55 | n/a | QUFFQ0F6eHNiM1J6CgkJSUc5bUlHSnBibUZ5ZVNCbmRXNXJQZ0FCQWdNOGJH |
---|
56 | n/a | OTBjeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlECgkJUEd4dmRITWdiMlln |
---|
57 | n/a | WW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09CgkJPC9kYXRhPgoJPC9hcnJheT4K |
---|
58 | n/a | CTxrZXk+c29tZURhdGE8L2tleT4KCTxkYXRhPgoJUEdKcGJtRnllU0JuZFc1 |
---|
59 | n/a | clBnPT0KCTwvZGF0YT4KCTxrZXk+c29tZU1vcmVEYXRhPC9rZXk+Cgk8ZGF0 |
---|
60 | n/a | YT4KCVBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytBQUVDQXp4c2IzUnpJ |
---|
61 | n/a | RzltSUdKcGJtRnllU0JuZFc1clBnQUJBZ004CgliRzkwY3lCdlppQmlhVzVo |
---|
62 | n/a | Y25rZ1ozVnVhejRBQVFJRFBHeHZkSE1nYjJZZ1ltbHVZWEo1SUdkMWJtcytB |
---|
63 | n/a | QUVDQXp4cwoJYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVyUGdBQkFnTThiRzkw |
---|
64 | n/a | Y3lCdlppQmlhVzVoY25rZ1ozVnVhejRBQVFJRFBHeHYKCWRITWdiMllnWW1s |
---|
65 | n/a | dVlYSjVJR2QxYm1zK0FBRUNBenhzYjNSeklHOW1JR0pwYm1GeWVTQm5kVzVy |
---|
66 | n/a | UGdBQkFnTThiRzkwCgljeUJ2WmlCaWFXNWhjbmtnWjNWdWF6NEFBUUlEUEd4 |
---|
67 | n/a | dmRITWdiMllnWW1sdVlYSjVJR2QxYm1zK0FBRUNBdz09Cgk8L2RhdGE+Cgk8 |
---|
68 | n/a | a2V5PsOFYmVucmFhPC9rZXk+Cgk8c3RyaW5nPlRoYXQgd2FzIGEgdW5pY29k |
---|
69 | n/a | ZSBrZXkuPC9zdHJpbmc+CjwvZGljdD4KPC9wbGlzdD4K'''), |
---|
70 | n/a | plistlib.FMT_BINARY: binascii.a2b_base64(b''' |
---|
71 | n/a | YnBsaXN0MDDfEBABAgMEBQYHCAkKCwwNDg8QERITFCgpLzAxMjM0NTc2OFdh |
---|
72 | n/a | QmlnSW50WGFCaWdJbnQyVWFEYXRlVWFEaWN0VmFGbG9hdFVhTGlzdF8QD2FO |
---|
73 | n/a | ZWdhdGl2ZUJpZ0ludFxhTmVnYXRpdmVJbnRXYVN0cmluZ1thbkVtcHR5RGlj |
---|
74 | n/a | dFthbkVtcHR5TGlzdFVhbkludFpuZXN0ZWREYXRhWHNvbWVEYXRhXHNvbWVN |
---|
75 | n/a | b3JlRGF0YWcAxQBiAGUAbgByAGEAYRN/////////1BQAAAAAAAAAAIAAAAAA |
---|
76 | n/a | AAAsM0GcuX30AAAA1RUWFxgZGhscHR5bYUZhbHNlVmFsdWVaYVRydWVWYWx1 |
---|
77 | n/a | ZV1hVW5pY29kZVZhbHVlXWFub3RoZXJTdHJpbmdaZGVlcGVyRGljdAgJawBN |
---|
78 | n/a | AOQAcwBzAGkAZwAsACAATQBhAN9fEBU8aGVsbG8gJiAnaGknIHRoZXJlIT7T |
---|
79 | n/a | HyAhIiMkUWFRYlFjEBEjQEBAAAAAAACjJSYnEAEQAlR0ZXh0Iz/gAAAAAAAA |
---|
80 | n/a | pSorLCMtUUFRQhAMoyUmLhADE////+1foOAAE//////////7VkRvb2RhaNCg |
---|
81 | n/a | EQLYoTZPEPo8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmlu |
---|
82 | n/a | YXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBv |
---|
83 | n/a | ZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs |
---|
84 | n/a | b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4A |
---|
85 | n/a | AQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBn |
---|
86 | n/a | dW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDTTxiaW5hcnkgZ3Vu |
---|
87 | n/a | az5fEBdUaGF0IHdhcyBhIHVuaWNvZGUga2V5LgAIACsAMwA8AEIASABPAFUA |
---|
88 | n/a | ZwB0AHwAiACUAJoApQCuALsAygDTAOQA7QD4AQQBDwEdASsBNgE3ATgBTwFn |
---|
89 | n/a | AW4BcAFyAXQBdgF/AYMBhQGHAYwBlQGbAZ0BnwGhAaUBpwGwAbkBwAHBAcIB |
---|
90 | n/a | xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''), |
---|
91 | n/a | } |
---|
92 | n/a | |
---|
93 | n/a | |
---|
94 | n/a | class TestPlistlib(unittest.TestCase): |
---|
95 | n/a | |
---|
96 | n/a | def tearDown(self): |
---|
97 | n/a | try: |
---|
98 | n/a | os.unlink(support.TESTFN) |
---|
99 | n/a | except: |
---|
100 | n/a | pass |
---|
101 | n/a | |
---|
102 | n/a | def _create(self, fmt=None): |
---|
103 | n/a | pl = dict( |
---|
104 | n/a | aString="Doodah", |
---|
105 | n/a | aList=["A", "B", 12, 32.5, [1, 2, 3]], |
---|
106 | n/a | aFloat = 0.5, |
---|
107 | n/a | anInt = 728, |
---|
108 | n/a | aBigInt = 2 ** 63 - 44, |
---|
109 | n/a | aBigInt2 = 2 ** 63 + 44, |
---|
110 | n/a | aNegativeInt = -5, |
---|
111 | n/a | aNegativeBigInt = -80000000000, |
---|
112 | n/a | aDict=dict( |
---|
113 | n/a | anotherString="<hello & 'hi' there!>", |
---|
114 | n/a | aUnicodeValue='M\xe4ssig, Ma\xdf', |
---|
115 | n/a | aTrueValue=True, |
---|
116 | n/a | aFalseValue=False, |
---|
117 | n/a | deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]), |
---|
118 | n/a | ), |
---|
119 | n/a | someData = b"<binary gunk>", |
---|
120 | n/a | someMoreData = b"<lots of binary gunk>\0\1\2\3" * 10, |
---|
121 | n/a | nestedData = [b"<lots of binary gunk>\0\1\2\3" * 10], |
---|
122 | n/a | aDate = datetime.datetime(2004, 10, 26, 10, 33, 33), |
---|
123 | n/a | anEmptyDict = dict(), |
---|
124 | n/a | anEmptyList = list() |
---|
125 | n/a | ) |
---|
126 | n/a | pl['\xc5benraa'] = "That was a unicode key." |
---|
127 | n/a | return pl |
---|
128 | n/a | |
---|
129 | n/a | def test_create(self): |
---|
130 | n/a | pl = self._create() |
---|
131 | n/a | self.assertEqual(pl["aString"], "Doodah") |
---|
132 | n/a | self.assertEqual(pl["aDict"]["aFalseValue"], False) |
---|
133 | n/a | |
---|
134 | n/a | def test_io(self): |
---|
135 | n/a | pl = self._create() |
---|
136 | n/a | with open(support.TESTFN, 'wb') as fp: |
---|
137 | n/a | plistlib.dump(pl, fp) |
---|
138 | n/a | |
---|
139 | n/a | with open(support.TESTFN, 'rb') as fp: |
---|
140 | n/a | pl2 = plistlib.load(fp) |
---|
141 | n/a | |
---|
142 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
143 | n/a | |
---|
144 | n/a | self.assertRaises(AttributeError, plistlib.dump, pl, 'filename') |
---|
145 | n/a | self.assertRaises(AttributeError, plistlib.load, 'filename') |
---|
146 | n/a | |
---|
147 | n/a | def test_invalid_type(self): |
---|
148 | n/a | pl = [ object() ] |
---|
149 | n/a | |
---|
150 | n/a | for fmt in ALL_FORMATS: |
---|
151 | n/a | with self.subTest(fmt=fmt): |
---|
152 | n/a | self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt) |
---|
153 | n/a | |
---|
154 | n/a | def test_int(self): |
---|
155 | n/a | for pl in [0, 2**8-1, 2**8, 2**16-1, 2**16, 2**32-1, 2**32, |
---|
156 | n/a | 2**63-1, 2**64-1, 1, -2**63]: |
---|
157 | n/a | for fmt in ALL_FORMATS: |
---|
158 | n/a | with self.subTest(pl=pl, fmt=fmt): |
---|
159 | n/a | data = plistlib.dumps(pl, fmt=fmt) |
---|
160 | n/a | pl2 = plistlib.loads(data) |
---|
161 | n/a | self.assertIsInstance(pl2, int) |
---|
162 | n/a | self.assertEqual(pl, pl2) |
---|
163 | n/a | data2 = plistlib.dumps(pl2, fmt=fmt) |
---|
164 | n/a | self.assertEqual(data, data2) |
---|
165 | n/a | |
---|
166 | n/a | for fmt in ALL_FORMATS: |
---|
167 | n/a | for pl in (2 ** 64 + 1, 2 ** 127-1, -2**64, -2 ** 127): |
---|
168 | n/a | with self.subTest(pl=pl, fmt=fmt): |
---|
169 | n/a | self.assertRaises(OverflowError, plistlib.dumps, |
---|
170 | n/a | pl, fmt=fmt) |
---|
171 | n/a | |
---|
172 | n/a | def test_bytes(self): |
---|
173 | n/a | pl = self._create() |
---|
174 | n/a | data = plistlib.dumps(pl) |
---|
175 | n/a | pl2 = plistlib.loads(data) |
---|
176 | n/a | self.assertNotIsInstance(pl, plistlib._InternalDict) |
---|
177 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
178 | n/a | data2 = plistlib.dumps(pl2) |
---|
179 | n/a | self.assertEqual(data, data2) |
---|
180 | n/a | |
---|
181 | n/a | def test_indentation_array(self): |
---|
182 | n/a | data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]] |
---|
183 | n/a | self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) |
---|
184 | n/a | |
---|
185 | n/a | def test_indentation_dict(self): |
---|
186 | n/a | data = {'1': {'2': {'3': {'4': {'5': {'6': {'7': {'8': {'9': b'aaaaaa'}}}}}}}}} |
---|
187 | n/a | self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) |
---|
188 | n/a | |
---|
189 | n/a | def test_indentation_dict_mix(self): |
---|
190 | n/a | data = {'1': {'2': [{'3': [[[[[{'test': b'aaaaaa'}]]]]]}]}} |
---|
191 | n/a | self.assertEqual(plistlib.loads(plistlib.dumps(data)), data) |
---|
192 | n/a | |
---|
193 | n/a | def test_appleformatting(self): |
---|
194 | n/a | for use_builtin_types in (True, False): |
---|
195 | n/a | for fmt in ALL_FORMATS: |
---|
196 | n/a | with self.subTest(fmt=fmt, use_builtin_types=use_builtin_types): |
---|
197 | n/a | pl = plistlib.loads(TESTDATA[fmt], |
---|
198 | n/a | use_builtin_types=use_builtin_types) |
---|
199 | n/a | data = plistlib.dumps(pl, fmt=fmt) |
---|
200 | n/a | self.assertEqual(data, TESTDATA[fmt], |
---|
201 | n/a | "generated data was not identical to Apple's output") |
---|
202 | n/a | |
---|
203 | n/a | |
---|
204 | n/a | def test_appleformattingfromliteral(self): |
---|
205 | n/a | self.maxDiff = None |
---|
206 | n/a | for fmt in ALL_FORMATS: |
---|
207 | n/a | with self.subTest(fmt=fmt): |
---|
208 | n/a | pl = self._create(fmt=fmt) |
---|
209 | n/a | pl2 = plistlib.loads(TESTDATA[fmt], fmt=fmt) |
---|
210 | n/a | self.assertEqual(dict(pl), dict(pl2), |
---|
211 | n/a | "generated data was not identical to Apple's output") |
---|
212 | n/a | pl2 = plistlib.loads(TESTDATA[fmt]) |
---|
213 | n/a | self.assertEqual(dict(pl), dict(pl2), |
---|
214 | n/a | "generated data was not identical to Apple's output") |
---|
215 | n/a | |
---|
216 | n/a | def test_bytesio(self): |
---|
217 | n/a | for fmt in ALL_FORMATS: |
---|
218 | n/a | with self.subTest(fmt=fmt): |
---|
219 | n/a | b = BytesIO() |
---|
220 | n/a | pl = self._create(fmt=fmt) |
---|
221 | n/a | plistlib.dump(pl, b, fmt=fmt) |
---|
222 | n/a | pl2 = plistlib.load(BytesIO(b.getvalue()), fmt=fmt) |
---|
223 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
224 | n/a | pl2 = plistlib.load(BytesIO(b.getvalue())) |
---|
225 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
226 | n/a | |
---|
227 | n/a | def test_keysort_bytesio(self): |
---|
228 | n/a | pl = collections.OrderedDict() |
---|
229 | n/a | pl['b'] = 1 |
---|
230 | n/a | pl['a'] = 2 |
---|
231 | n/a | pl['c'] = 3 |
---|
232 | n/a | |
---|
233 | n/a | for fmt in ALL_FORMATS: |
---|
234 | n/a | for sort_keys in (False, True): |
---|
235 | n/a | with self.subTest(fmt=fmt, sort_keys=sort_keys): |
---|
236 | n/a | b = BytesIO() |
---|
237 | n/a | |
---|
238 | n/a | plistlib.dump(pl, b, fmt=fmt, sort_keys=sort_keys) |
---|
239 | n/a | pl2 = plistlib.load(BytesIO(b.getvalue()), |
---|
240 | n/a | dict_type=collections.OrderedDict) |
---|
241 | n/a | |
---|
242 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
243 | n/a | if sort_keys: |
---|
244 | n/a | self.assertEqual(list(pl2.keys()), ['a', 'b', 'c']) |
---|
245 | n/a | else: |
---|
246 | n/a | self.assertEqual(list(pl2.keys()), ['b', 'a', 'c']) |
---|
247 | n/a | |
---|
248 | n/a | def test_keysort(self): |
---|
249 | n/a | pl = collections.OrderedDict() |
---|
250 | n/a | pl['b'] = 1 |
---|
251 | n/a | pl['a'] = 2 |
---|
252 | n/a | pl['c'] = 3 |
---|
253 | n/a | |
---|
254 | n/a | for fmt in ALL_FORMATS: |
---|
255 | n/a | for sort_keys in (False, True): |
---|
256 | n/a | with self.subTest(fmt=fmt, sort_keys=sort_keys): |
---|
257 | n/a | data = plistlib.dumps(pl, fmt=fmt, sort_keys=sort_keys) |
---|
258 | n/a | pl2 = plistlib.loads(data, dict_type=collections.OrderedDict) |
---|
259 | n/a | |
---|
260 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
261 | n/a | if sort_keys: |
---|
262 | n/a | self.assertEqual(list(pl2.keys()), ['a', 'b', 'c']) |
---|
263 | n/a | else: |
---|
264 | n/a | self.assertEqual(list(pl2.keys()), ['b', 'a', 'c']) |
---|
265 | n/a | |
---|
266 | n/a | def test_keys_no_string(self): |
---|
267 | n/a | pl = { 42: 'aNumber' } |
---|
268 | n/a | |
---|
269 | n/a | for fmt in ALL_FORMATS: |
---|
270 | n/a | with self.subTest(fmt=fmt): |
---|
271 | n/a | self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt) |
---|
272 | n/a | |
---|
273 | n/a | b = BytesIO() |
---|
274 | n/a | self.assertRaises(TypeError, plistlib.dump, pl, b, fmt=fmt) |
---|
275 | n/a | |
---|
276 | n/a | def test_skipkeys(self): |
---|
277 | n/a | pl = { |
---|
278 | n/a | 42: 'aNumber', |
---|
279 | n/a | 'snake': 'aWord', |
---|
280 | n/a | } |
---|
281 | n/a | |
---|
282 | n/a | for fmt in ALL_FORMATS: |
---|
283 | n/a | with self.subTest(fmt=fmt): |
---|
284 | n/a | data = plistlib.dumps( |
---|
285 | n/a | pl, fmt=fmt, skipkeys=True, sort_keys=False) |
---|
286 | n/a | |
---|
287 | n/a | pl2 = plistlib.loads(data) |
---|
288 | n/a | self.assertEqual(pl2, {'snake': 'aWord'}) |
---|
289 | n/a | |
---|
290 | n/a | fp = BytesIO() |
---|
291 | n/a | plistlib.dump( |
---|
292 | n/a | pl, fp, fmt=fmt, skipkeys=True, sort_keys=False) |
---|
293 | n/a | data = fp.getvalue() |
---|
294 | n/a | pl2 = plistlib.loads(fp.getvalue()) |
---|
295 | n/a | self.assertEqual(pl2, {'snake': 'aWord'}) |
---|
296 | n/a | |
---|
297 | n/a | def test_tuple_members(self): |
---|
298 | n/a | pl = { |
---|
299 | n/a | 'first': (1, 2), |
---|
300 | n/a | 'second': (1, 2), |
---|
301 | n/a | 'third': (3, 4), |
---|
302 | n/a | } |
---|
303 | n/a | |
---|
304 | n/a | for fmt in ALL_FORMATS: |
---|
305 | n/a | with self.subTest(fmt=fmt): |
---|
306 | n/a | data = plistlib.dumps(pl, fmt=fmt) |
---|
307 | n/a | pl2 = plistlib.loads(data) |
---|
308 | n/a | self.assertEqual(pl2, { |
---|
309 | n/a | 'first': [1, 2], |
---|
310 | n/a | 'second': [1, 2], |
---|
311 | n/a | 'third': [3, 4], |
---|
312 | n/a | }) |
---|
313 | n/a | self.assertIsNot(pl2['first'], pl2['second']) |
---|
314 | n/a | |
---|
315 | n/a | def test_list_members(self): |
---|
316 | n/a | pl = { |
---|
317 | n/a | 'first': [1, 2], |
---|
318 | n/a | 'second': [1, 2], |
---|
319 | n/a | 'third': [3, 4], |
---|
320 | n/a | } |
---|
321 | n/a | |
---|
322 | n/a | for fmt in ALL_FORMATS: |
---|
323 | n/a | with self.subTest(fmt=fmt): |
---|
324 | n/a | data = plistlib.dumps(pl, fmt=fmt) |
---|
325 | n/a | pl2 = plistlib.loads(data) |
---|
326 | n/a | self.assertEqual(pl2, { |
---|
327 | n/a | 'first': [1, 2], |
---|
328 | n/a | 'second': [1, 2], |
---|
329 | n/a | 'third': [3, 4], |
---|
330 | n/a | }) |
---|
331 | n/a | self.assertIsNot(pl2['first'], pl2['second']) |
---|
332 | n/a | |
---|
333 | n/a | def test_dict_members(self): |
---|
334 | n/a | pl = { |
---|
335 | n/a | 'first': {'a': 1}, |
---|
336 | n/a | 'second': {'a': 1}, |
---|
337 | n/a | 'third': {'b': 2 }, |
---|
338 | n/a | } |
---|
339 | n/a | |
---|
340 | n/a | for fmt in ALL_FORMATS: |
---|
341 | n/a | with self.subTest(fmt=fmt): |
---|
342 | n/a | data = plistlib.dumps(pl, fmt=fmt) |
---|
343 | n/a | pl2 = plistlib.loads(data) |
---|
344 | n/a | self.assertEqual(pl2, { |
---|
345 | n/a | 'first': {'a': 1}, |
---|
346 | n/a | 'second': {'a': 1}, |
---|
347 | n/a | 'third': {'b': 2 }, |
---|
348 | n/a | }) |
---|
349 | n/a | self.assertIsNot(pl2['first'], pl2['second']) |
---|
350 | n/a | |
---|
351 | n/a | def test_controlcharacters(self): |
---|
352 | n/a | for i in range(128): |
---|
353 | n/a | c = chr(i) |
---|
354 | n/a | testString = "string containing %s" % c |
---|
355 | n/a | if i >= 32 or c in "\r\n\t": |
---|
356 | n/a | # \r, \n and \t are the only legal control chars in XML |
---|
357 | n/a | plistlib.dumps(testString, fmt=plistlib.FMT_XML) |
---|
358 | n/a | else: |
---|
359 | n/a | self.assertRaises(ValueError, |
---|
360 | n/a | plistlib.dumps, |
---|
361 | n/a | testString) |
---|
362 | n/a | |
---|
363 | n/a | def test_non_bmp_characters(self): |
---|
364 | n/a | pl = {'python': '\U0001f40d'} |
---|
365 | n/a | for fmt in ALL_FORMATS: |
---|
366 | n/a | with self.subTest(fmt=fmt): |
---|
367 | n/a | data = plistlib.dumps(pl, fmt=fmt) |
---|
368 | n/a | self.assertEqual(plistlib.loads(data), pl) |
---|
369 | n/a | |
---|
370 | n/a | def test_nondictroot(self): |
---|
371 | n/a | for fmt in ALL_FORMATS: |
---|
372 | n/a | with self.subTest(fmt=fmt): |
---|
373 | n/a | test1 = "abc" |
---|
374 | n/a | test2 = [1, 2, 3, "abc"] |
---|
375 | n/a | result1 = plistlib.loads(plistlib.dumps(test1, fmt=fmt)) |
---|
376 | n/a | result2 = plistlib.loads(plistlib.dumps(test2, fmt=fmt)) |
---|
377 | n/a | self.assertEqual(test1, result1) |
---|
378 | n/a | self.assertEqual(test2, result2) |
---|
379 | n/a | |
---|
380 | n/a | def test_invalidarray(self): |
---|
381 | n/a | for i in ["<key>key inside an array</key>", |
---|
382 | n/a | "<key>key inside an array2</key><real>3</real>", |
---|
383 | n/a | "<true/><key>key inside an array3</key>"]: |
---|
384 | n/a | self.assertRaises(ValueError, plistlib.loads, |
---|
385 | n/a | ("<plist><array>%s</array></plist>"%i).encode()) |
---|
386 | n/a | |
---|
387 | n/a | def test_invaliddict(self): |
---|
388 | n/a | for i in ["<key><true/>k</key><string>compound key</string>", |
---|
389 | n/a | "<key>single key</key>", |
---|
390 | n/a | "<string>missing key</string>", |
---|
391 | n/a | "<key>k1</key><string>v1</string><real>5.3</real>" |
---|
392 | n/a | "<key>k1</key><key>k2</key><string>double key</string>"]: |
---|
393 | n/a | self.assertRaises(ValueError, plistlib.loads, |
---|
394 | n/a | ("<plist><dict>%s</dict></plist>"%i).encode()) |
---|
395 | n/a | self.assertRaises(ValueError, plistlib.loads, |
---|
396 | n/a | ("<plist><array><dict>%s</dict></array></plist>"%i).encode()) |
---|
397 | n/a | |
---|
398 | n/a | def test_invalidinteger(self): |
---|
399 | n/a | self.assertRaises(ValueError, plistlib.loads, |
---|
400 | n/a | b"<plist><integer>not integer</integer></plist>") |
---|
401 | n/a | |
---|
402 | n/a | def test_invalidreal(self): |
---|
403 | n/a | self.assertRaises(ValueError, plistlib.loads, |
---|
404 | n/a | b"<plist><integer>not real</integer></plist>") |
---|
405 | n/a | |
---|
406 | n/a | def test_xml_encodings(self): |
---|
407 | n/a | base = TESTDATA[plistlib.FMT_XML] |
---|
408 | n/a | |
---|
409 | n/a | for xml_encoding, encoding, bom in [ |
---|
410 | n/a | (b'utf-8', 'utf-8', codecs.BOM_UTF8), |
---|
411 | n/a | (b'utf-16', 'utf-16-le', codecs.BOM_UTF16_LE), |
---|
412 | n/a | (b'utf-16', 'utf-16-be', codecs.BOM_UTF16_BE), |
---|
413 | n/a | # Expat does not support UTF-32 |
---|
414 | n/a | #(b'utf-32', 'utf-32-le', codecs.BOM_UTF32_LE), |
---|
415 | n/a | #(b'utf-32', 'utf-32-be', codecs.BOM_UTF32_BE), |
---|
416 | n/a | ]: |
---|
417 | n/a | |
---|
418 | n/a | pl = self._create(fmt=plistlib.FMT_XML) |
---|
419 | n/a | with self.subTest(encoding=encoding): |
---|
420 | n/a | data = base.replace(b'UTF-8', xml_encoding) |
---|
421 | n/a | data = bom + data.decode('utf-8').encode(encoding) |
---|
422 | n/a | pl2 = plistlib.loads(data) |
---|
423 | n/a | self.assertEqual(dict(pl), dict(pl2)) |
---|
424 | n/a | |
---|
425 | n/a | def test_nonstandard_refs_size(self): |
---|
426 | n/a | # Issue #21538: Refs and offsets are 24-bit integers |
---|
427 | n/a | data = (b'bplist00' |
---|
428 | n/a | b'\xd1\x00\x00\x01\x00\x00\x02QaQb' |
---|
429 | n/a | b'\x00\x00\x08\x00\x00\x0f\x00\x00\x11' |
---|
430 | n/a | b'\x00\x00\x00\x00\x00\x00' |
---|
431 | n/a | b'\x03\x03' |
---|
432 | n/a | b'\x00\x00\x00\x00\x00\x00\x00\x03' |
---|
433 | n/a | b'\x00\x00\x00\x00\x00\x00\x00\x00' |
---|
434 | n/a | b'\x00\x00\x00\x00\x00\x00\x00\x13') |
---|
435 | n/a | self.assertEqual(plistlib.loads(data), {'a': 'b'}) |
---|
436 | n/a | |
---|
437 | n/a | def test_large_timestamp(self): |
---|
438 | n/a | # Issue #26709: 32-bit timestamp out of range |
---|
439 | n/a | for ts in -2**31-1, 2**31: |
---|
440 | n/a | with self.subTest(ts=ts): |
---|
441 | n/a | d = (datetime.datetime.utcfromtimestamp(0) + |
---|
442 | n/a | datetime.timedelta(seconds=ts)) |
---|
443 | n/a | data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY) |
---|
444 | n/a | self.assertEqual(plistlib.loads(data), d) |
---|
445 | n/a | |
---|
446 | n/a | |
---|
447 | n/a | class TestPlistlibDeprecated(unittest.TestCase): |
---|
448 | n/a | def test_io_deprecated(self): |
---|
449 | n/a | pl_in = { |
---|
450 | n/a | 'key': 42, |
---|
451 | n/a | 'sub': { |
---|
452 | n/a | 'key': 9, |
---|
453 | n/a | 'alt': 'value', |
---|
454 | n/a | 'data': b'buffer', |
---|
455 | n/a | } |
---|
456 | n/a | } |
---|
457 | n/a | pl_out = plistlib._InternalDict({ |
---|
458 | n/a | 'key': 42, |
---|
459 | n/a | 'sub': plistlib._InternalDict({ |
---|
460 | n/a | 'key': 9, |
---|
461 | n/a | 'alt': 'value', |
---|
462 | n/a | 'data': plistlib.Data(b'buffer'), |
---|
463 | n/a | }) |
---|
464 | n/a | }) |
---|
465 | n/a | |
---|
466 | n/a | self.addCleanup(support.unlink, support.TESTFN) |
---|
467 | n/a | with self.assertWarns(DeprecationWarning): |
---|
468 | n/a | plistlib.writePlist(pl_in, support.TESTFN) |
---|
469 | n/a | |
---|
470 | n/a | with self.assertWarns(DeprecationWarning): |
---|
471 | n/a | pl2 = plistlib.readPlist(support.TESTFN) |
---|
472 | n/a | |
---|
473 | n/a | self.assertEqual(pl_out, pl2) |
---|
474 | n/a | |
---|
475 | n/a | os.unlink(support.TESTFN) |
---|
476 | n/a | |
---|
477 | n/a | with open(support.TESTFN, 'wb') as fp: |
---|
478 | n/a | with self.assertWarns(DeprecationWarning): |
---|
479 | n/a | plistlib.writePlist(pl_in, fp) |
---|
480 | n/a | |
---|
481 | n/a | with open(support.TESTFN, 'rb') as fp: |
---|
482 | n/a | with self.assertWarns(DeprecationWarning): |
---|
483 | n/a | pl2 = plistlib.readPlist(fp) |
---|
484 | n/a | |
---|
485 | n/a | self.assertEqual(pl_out, pl2) |
---|
486 | n/a | |
---|
487 | n/a | def test_bytes_deprecated(self): |
---|
488 | n/a | pl = { |
---|
489 | n/a | 'key': 42, |
---|
490 | n/a | 'sub': { |
---|
491 | n/a | 'key': 9, |
---|
492 | n/a | 'alt': 'value', |
---|
493 | n/a | 'data': b'buffer', |
---|
494 | n/a | } |
---|
495 | n/a | } |
---|
496 | n/a | with self.assertWarns(DeprecationWarning): |
---|
497 | n/a | data = plistlib.writePlistToBytes(pl) |
---|
498 | n/a | |
---|
499 | n/a | with self.assertWarns(DeprecationWarning): |
---|
500 | n/a | pl2 = plistlib.readPlistFromBytes(data) |
---|
501 | n/a | |
---|
502 | n/a | self.assertIsInstance(pl2, plistlib._InternalDict) |
---|
503 | n/a | self.assertEqual(pl2, plistlib._InternalDict( |
---|
504 | n/a | key=42, |
---|
505 | n/a | sub=plistlib._InternalDict( |
---|
506 | n/a | key=9, |
---|
507 | n/a | alt='value', |
---|
508 | n/a | data=plistlib.Data(b'buffer'), |
---|
509 | n/a | ) |
---|
510 | n/a | )) |
---|
511 | n/a | |
---|
512 | n/a | with self.assertWarns(DeprecationWarning): |
---|
513 | n/a | data2 = plistlib.writePlistToBytes(pl2) |
---|
514 | n/a | self.assertEqual(data, data2) |
---|
515 | n/a | |
---|
516 | n/a | def test_dataobject_deprecated(self): |
---|
517 | n/a | in_data = { 'key': plistlib.Data(b'hello') } |
---|
518 | n/a | out_data = { 'key': b'hello' } |
---|
519 | n/a | |
---|
520 | n/a | buf = plistlib.dumps(in_data) |
---|
521 | n/a | |
---|
522 | n/a | cur = plistlib.loads(buf) |
---|
523 | n/a | self.assertEqual(cur, out_data) |
---|
524 | n/a | self.assertEqual(cur, in_data) |
---|
525 | n/a | |
---|
526 | n/a | cur = plistlib.loads(buf, use_builtin_types=False) |
---|
527 | n/a | self.assertEqual(cur, out_data) |
---|
528 | n/a | self.assertEqual(cur, in_data) |
---|
529 | n/a | |
---|
530 | n/a | with self.assertWarns(DeprecationWarning): |
---|
531 | n/a | cur = plistlib.readPlistFromBytes(buf) |
---|
532 | n/a | self.assertEqual(cur, out_data) |
---|
533 | n/a | self.assertEqual(cur, in_data) |
---|
534 | n/a | |
---|
535 | n/a | |
---|
536 | n/a | class MiscTestCase(unittest.TestCase): |
---|
537 | n/a | def test__all__(self): |
---|
538 | n/a | blacklist = {"PlistFormat", "PLISTHEADER"} |
---|
539 | n/a | support.check__all__(self, plistlib, blacklist=blacklist) |
---|
540 | n/a | |
---|
541 | n/a | |
---|
542 | n/a | def test_main(): |
---|
543 | n/a | support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase) |
---|
544 | n/a | |
---|
545 | n/a | |
---|
546 | n/a | if __name__ == '__main__': |
---|
547 | n/a | test_main() |
---|