c 20032013 python software foundation the testdata is generated using mactoolsplistlibgeneratetestdata py which using pyobjc to control the cocoa classes for generating plists invalidbinaryplists too short data b too large offsettableoffset and offsetsize 1 b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x2a too large offsettableoffset and nonstandard offsetsize b x00x00x00x08 b x00x00x00x00x00x00x03x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x2c integer overflow in offsettableoffset b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b xffxffxffxffxffxffxffxff too large topobject b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x09 integer overflow in topobject b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b xffxffxffxffxffxffxffxff b x00x00x00x00x00x00x00x09 too large numobjects and offsetsize 1 b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00xff b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 too large numobjects and nonstandard offsetsize b x00x00x00x08 b x00x00x00x00x00x00x03x01 b x00x00x00x00x00x00x00xff b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 extremally large numobjects 32 bit b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x7fxffxffxff b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 extremally large numobjects 64 bit b x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00xffxffxffxffxff b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 integer overflow in numobjects b x00x08 b x00x00x00x00x00x00x01x01 b xffxffxffxffxffxffxffxff b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 offsetsize 0 b x00x08 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 refsize 0 b xa1x01x00x08x0a b x00x00x00x00x00x00x01x00 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0b too large offset b x00x2a b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 integer overflow in offset b x00xffxffxffxffxffxffxffxff b x00x00x00x00x00x00x08x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x09 too large array size b xafx00x01xffx00x08x0c b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0d extremally large array size 32bit b xafx02x7fxffxffxffx01x00x08x0f b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x10 extremally large array size 64bit b xafx03x00x00x00xffxffxffxffxffx01x00x08x13 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x14 integer overflow in array size b xafx03xffxffxffxffxffxffxffxffx01x00x08x13 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x14 too large reference index b xa1x02x00x08x0a b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0b integer overflow in reference index b xa1xffxffxffxffxffxffxffxffx00x08x11 b x00x00x00x00x00x00x01x08 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x12 too large bytes size b x4fx00x23x41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0c extremally large bytes size 32bit b x4fx02x7fxffxffxffx41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0f extremally large bytes size 64bit b x4fx03x00x00x00xffxffxffxffxffx41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x13 integer overflow in bytes size b x4fx03xffxffxffxffxffxffxffxffx41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x13 too large ascii size b x5fx00x23x41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0c extremally large ascii size 32bit b x5fx02x7fxffxffxffx41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0f extremally large ascii size 64bit b x5fx03x00x00x00xffxffxffxffxffx41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x13 integer overflow in ascii size b x5fx03xffxffxffxffxffxffxffxffx41x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x13 invalid ascii b x51xffx08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0a too large utf16 size b x6fx00x13x20xacx00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0e extremally large utf16 size 32bit b x6fx02x4fxffxffxffx20xacx00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x11 extremally large utf16 size 64bit b x6fx03x00x00x00xffxffxffxffxffx20xacx00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x15 integer overflow in utf16 size b x6fx03xffxffxffxffxffxffxffxffx20xacx00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x15 invalid utf16 b x61xd8x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0b nonhashable key b xd1x01x01xa0x08x0b b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x02 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x0c too large datetime datetime overflow b x33x42x50x00x00x00x00x00x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x11 too large datetime timedelta overflow b x33x42xe0x00x00x00x00x00x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x11 invalid datetime infinity b x33x7fxf0x00x00x00x00x00x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x11 invalid datetime nan b x33x7fxf8x00x00x00x00x00x00x08 b x00x00x00x00x00x00x01x01 b x00x00x00x00x00x00x00x01 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x11 class testplistlibunittest testcase def teardownself try os unlinkoshelper testfn except pass def createself fmtnone pl dict astringdoodah alista b 12 32 5 1 2 3 afloat 0 5 anint 728 abigint 2 63 44 abigint2 2 63 44 anegativeint 5 anegativebigint 80000000000 adictdict anotherstringhello hi there aunicodevalue mxe4ssig maxdf atruevaluetrue afalsevaluefalse deeperdictdicta17 b32 5 c1 2 text somedata bbinary gunk somemoredata blots of binary gunk0123 10 nesteddata blots of binary gunk0123 10 adate datetime datetime2004 10 26 10 33 33 anemptydict dict anemptylist list pl xc5benraa that was a unicode key return pl def testcreateself pl self create self assertequalplastring doodah self assertequalpladictafalsevalue false def testioself pl self create with openoshelper testfn wb as fp plistlib dumppl fp with openoshelper testfn rb as fp pl2 plistlib loadfp self assertequaldictpl dictpl2 self assertraisesattributeerror plistlib dump pl filename self assertraisesattributeerror plistlib load filename def testinvalidtypeself pl object for fmt in allformats with self subtestfmtfmt self assertraisestypeerror plistlib dumps pl fmtfmt def testinvaliduidself with self assertraisestypeerror uidnot an int with self assertraisesvalueerror uid2 64 with self assertraisesvalueerror uid19 def testintself for pl in 0 281 28 2161 216 2321 232 2631 2641 1 263 for fmt in allformats with self subtestplpl fmtfmt data plistlib dumpspl fmtfmt pl2 plistlib loadsdata self assertisinstancepl2 int self assertequalpl pl2 data2 plistlib dumpspl2 fmtfmt self assertequaldata data2 for fmt in allformats for pl in 2 64 1 2 1271 264 2 127 with self subtestplpl fmtfmt self assertraisesoverflowerror plistlib dumps pl fmtfmt def testbytearrayself for pl in b binary gunk blots of binary gunk0123 10 for fmt in allformats with self subtestplpl fmtfmt data plistlib dumpsbytearraypl fmtfmt pl2 plistlib loadsdata self assertisinstancepl2 bytes self assertequalpl2 pl data2 plistlib dumpspl2 fmtfmt self assertequaldata data2 def testbytesself pl self create data plistlib dumpspl pl2 plistlib loadsdata self assertequaldictpl dictpl2 data2 plistlib dumpspl2 self assertequaldata data2 def testindentationarrayself data test b aaaaaa self assertequalplistlib loadsplistlib dumpsdata data def testindentationdictself data 1 2 3 4 5 6 7 8 9 b aaaaaa self assertequalplistlib loadsplistlib dumpsdata data def testindentationdictmixself data 1 2 3 test b aaaaaa self assertequalplistlib loadsplistlib dumpsdata data def testuidself data uid1 self assertequalplistlib loadsplistlib dumpsdata fmtplistlib fmtbinary data dictdata uid0 uid0 uid2 uid2 uid8 uid2 8 uid16 uid2 16 uid32 uid2 32 uid63 uid2 63 self assertequalplistlib loadsplistlib dumpsdictdata fmtplistlib fmtbinary dictdata def testuiddataself uid uid1 self assertequaluid data 1 def testuideqself self assertequaluid1 uid1 self assertnotequaluid1 uid2 self assertnotequaluid1 not uid def testuidhashself self assertequalhashuid1 hashuid1 def testuidreprself self assertequalrepruid1 uid1 def testuidindexself self assertequaloperator indexuid1 1 def testuidpickleself for proto in rangepickle highestprotocol 1 self assertequalpickle loadspickle dumpsuid19 protocolproto uid19 def testuidcopyself self assertequalcopy copyuid1 uid1 self assertequalcopy deepcopyuid1 uid1 def testappleformattingself for fmt in allformats with self subtestfmtfmt pl plistlib loadstestdatafmt data plistlib dumpspl fmtfmt self assertequaldata testdatafmt generated data was not identical to apple s output def testappleformattingfromliteralself self maxdiff none for fmt in allformats with self subtestfmtfmt pl self createfmtfmt pl2 plistlib loadstestdatafmt fmtfmt self assertequaldictpl dictpl2 generated data was not identical to apple s output pl2 plistlib loadstestdatafmt self assertequaldictpl dictpl2 generated data was not identical to apple s output def testbytesioself for fmt in allformats with self subtestfmtfmt b bytesio pl self createfmtfmt plistlib dumppl b fmtfmt pl2 plistlib loadbytesiob getvalue fmtfmt self assertequaldictpl dictpl2 pl2 plistlib loadbytesiob getvalue self assertequaldictpl dictpl2 def testkeysortbytesioself pl collections ordereddict pl b 1 pl a 2 pl c 3 for fmt in allformats for sortkeys in false true with self subtestfmtfmt sortkeyssortkeys b bytesio plistlib dumppl b fmtfmt sortkeyssortkeys pl2 plistlib loadbytesiob getvalue dicttypecollections ordereddict self assertequaldictpl dictpl2 if sortkeys self assertequallistpl2 keys a b c else self assertequallistpl2 keys b a c def testkeysortself pl collections ordereddict pl b 1 pl a 2 pl c 3 for fmt in allformats for sortkeys in false true with self subtestfmtfmt sortkeyssortkeys data plistlib dumpspl fmtfmt sortkeyssortkeys pl2 plistlib loadsdata dicttypecollections ordereddict self assertequaldictpl dictpl2 if sortkeys self assertequallistpl2 keys a b c else self assertequallistpl2 keys b a c def testkeysnostringself pl 42 anumber for fmt in allformats with self subtestfmtfmt self assertraisestypeerror plistlib dumps pl fmtfmt b bytesio self assertraisestypeerror plistlib dump pl b fmtfmt def testskipkeysself pl 42 anumber snake aword for fmt in allformats with self subtestfmtfmt data plistlib dumps pl fmtfmt skipkeystrue sortkeysfalse pl2 plistlib loadsdata self assertequalpl2 snake aword fp bytesio plistlib dump pl fp fmtfmt skipkeystrue sortkeysfalse data fp getvalue pl2 plistlib loadsfp getvalue self assertequalpl2 snake aword def testtuplemembersself pl first 1 2 second 1 2 third 3 4 for fmt in allformats with self subtestfmtfmt data plistlib dumpspl fmtfmt pl2 plistlib loadsdata self assertequalpl2 first 1 2 second 1 2 third 3 4 if fmt plistlib fmtbinary self assertisnotpl2 first pl2 second def testlistmembersself pl first 1 2 second 1 2 third 3 4 for fmt in allformats with self subtestfmtfmt data plistlib dumpspl fmtfmt pl2 plistlib loadsdata self assertequalpl2 first 1 2 second 1 2 third 3 4 self assertisnotpl2 first pl2 second def testdictmembersself pl first a 1 second a 1 third b 2 for fmt in allformats with self subtestfmtfmt data plistlib dumpspl fmtfmt pl2 plistlib loadsdata self assertequalpl2 first a 1 second a 1 third b 2 self assertisnotpl2 first pl2 second def testcontrolcharactersself for i in range128 c chri teststring string containing s c if i 32 or c in rnt r n and t are the only legal control chars in xml data plistlib dumpsteststring fmtplistlib fmtxml if c r self assertequalplistlib loadsdata teststring else with self assertraisesvalueerror plistlib dumpsteststring fmtplistlib fmtxml plistlib dumpsteststring fmtplistlib fmtbinary def testnonbmpcharactersself pl python u0001f40d for fmt in allformats with self subtestfmtfmt data plistlib dumpspl fmtfmt self assertequalplistlib loadsdata pl def testlonesurrogatesself for fmt in allformats with self subtestfmtfmt with self assertraisesunicodeencodeerror plistlib dumps ud8ff fmtfmt with self assertraisesunicodeencodeerror plistlib dumps udcff fmtfmt def testnondictrootself for fmt in allformats with self subtestfmtfmt test1 abc test2 1 2 3 abc result1 plistlib loadsplistlib dumpstest1 fmtfmt result2 plistlib loadsplistlib dumpstest2 fmtfmt self assertequaltest1 result1 self assertequaltest2 result2 def testinvalidarrayself for i in keykey inside an arraykey keykey inside an array2keyreal3real truekeykey inside an array3key self assertraisesvalueerror plistlib loads plistarraysarrayplisti encode def testinvaliddictself for i in keytruekkeystringcompound keystring keysingle keykey stringmissing keystring keyk1keystringv1stringreal5 3real keyk1keykeyk2keystringdouble keystring self assertraisesvalueerror plistlib loads plistdictsdictplisti encode self assertraisesvalueerror plistlib loads plistarraydictsdictarrayplisti encode def testinvalidintegerself self assertraisesvalueerror plistlib loads bplistintegernot integerintegerplist def testinvalidrealself self assertraisesvalueerror plistlib loads bplistintegernot realintegerplist def testintegernotationsself pl bplistinteger456integerplist value plistlib loadspl self assertequalvalue 456 pl bplistinteger0xaintegerplist value plistlib loadspl self assertequalvalue 10 pl bplistinteger0123integerplist value plistlib loadspl self assertequalvalue 123 def testxmlencodingsself base testdataplistlib fmtxml for xmlencoding encoding bom in b utf8 utf8 codecs bomutf8 b utf16 utf16le codecs bomutf16le b utf16 utf16be codecs bomutf16be expat does not support utf32 b utf32 utf32le codecs bomutf32le b utf32 utf32be codecs bomutf32be pl self createfmtplistlib fmtxml with self subtestencodingencoding data base replaceb utf8 xmlencoding data bom data decode utf8 encodeencoding pl2 plistlib loadsdata self assertequaldictpl dictpl2 def testdumpinvalidformatself with self assertraisesvalueerror plistlib dumps fmtblah def testloadinvalidfileself with self assertraisesplistlib invalidfileexception plistlib loadsbthese are not plist file contents def testmodifieduidnegativeself neguid uid1 neguid data 1 dodge the negative check in the constructor with self assertraisesvalueerror plistlib dumpsneguid fmtplistlib fmtbinary def testmodifieduidhugeself hugeuid uid1 hugeuid data 2 64 dodge the size check in the constructor with self assertraisesoverflowerror plistlib dumpshugeuid fmtplistlib fmtbinary def testxmlplistwithentitydeclself with self assertraisesregexplistlib invalidfileexception xml entity declarations are not supported plistlib loadsxmlplistwithentity fmtplistlib fmtxml class testbinaryplistlibunittest testcase staticmethod def decodeobjects offsetsize1 refsize1 data b bplist00 offset 8 offsets for x in objects offsets appendoffset tobytesoffsetsize big data appendx offset lenx tail struct pack 6xbbqqq offsetsize refsize lenobjects 0 offset data extendoffsets data appendtail return plistlib loadsb joindata fmtplistlib fmtbinary def testnonstandardrefssizeself issue 21538 refs and offsets are 24bit integers data b bplist00 b xd1x00x00x01x00x00x02qaqb b x00x00x08x00x00x0fx00x00x11 b x00x00x00x00x00x00 b x03x03 b x00x00x00x00x00x00x00x03 b x00x00x00x00x00x00x00x00 b x00x00x00x00x00x00x00x13 self assertequalplistlib loadsdata a b def testdumpduplicatesself test effectiveness of saving duplicated objects for x in none false true 12345 123 45 abcde b abcde datetime datetime2004 10 26 10 33 33 bytearrayb abcde 12 345 12 345 12 345 with self subtestxx data plistlib dumpsx1000 fmtplistlib fmtbinary self assertlesslendata 1100 reprdata def testidentityself for x in none false true 12345 123 45 abcde b abcde datetime datetime2004 10 26 10 33 33 bytearrayb abcde 12 345 12 345 12 345 with self subtestxx data plistlib dumpsx2 fmtplistlib fmtbinary a b plistlib loadsdata if isinstancex tuple x listx self assertequala x self assertequalb x self assertisa b def testcyclesself recursive list a a appenda b plistlib loadsplistlib dumpsa fmtplistlib fmtbinary self assertisb0 b recursive tuple a a0 appenda b plistlib loadsplistlib dumpsa fmtplistlib fmtbinary self assertisb00 b recursive dict a a x a b plistlib loadsplistlib dumpsa fmtplistlib fmtbinary self assertisb x b def testdeepnestingself for n in 300 100000 chunks b xa1 i 1 tobytes4 big for i in rangen try result self decodechunks b x54seed offsetsize4 refsize4 except recursionerror pass else for i in rangen self assertisinstanceresult list self assertequallenresult 1 result result0 self assertequalresult seed def testlargetimestampself issue 26709 32bit timestamp out of range for ts in 2311 231 with self subtesttsts d datetime datetime1970 1 1 0 0 datetime timedeltasecondsts data plistlib dumpsd fmtplistlib fmtbinary self assertequalplistlib loadsdata d def testloadsingletonsself self assertisself decodeb x00 none self assertisself decodeb x08 false self assertisself decodeb x09 true self assertequalself decodeb x0f b def testloadintself self assertequalself decodeb x10x00 0 self assertequalself decodeb x10xfe 0xfe self assertequalself decodeb x11xfexdc 0xfedc self assertequalself decodeb x12xfexdcxbax98 0xfedcba98 self assertequalself decodeb x13x01x23x45x67x89xabxcdxef 0x0123456789abcdef self assertequalself decodeb x13xfexdcxbax98x76x54x32x10 0x123456789abcdf0 def testunsupportedself unsupported range1 8 range10 15 0x20 0x21 range0x24 0x33 range0x34 0x40 for i in 0x70 0x90 0xb0 0xc0 0xe0 0xf0 unsupported extendi j for j in range16 for token in unsupported with self subtestf token token 02x with self assertraisesplistlib invalidfileexception self decodebytestoken b x00 16 def testinvalidbinaryself for name data in invalidbinaryplists with self subtestname with self assertraisesplistlib invalidfileexception plistlib loadsb bplist00 data fmtplistlib fmtbinary class testkeyedarchiveunittest testcase def testkeyedarchivedataself this is the structure of a nskeyedarchive packed plist data version 100000 objects null pytype 1 class uid2 ns string keyarchive uid test classname ocbuiltinpythonunicode classes ocbuiltinpythonunicode ocpythonunicode nsstring nsobject classhints ocpythonstring nsstring archiver nskeyedarchiver top root uid1 self assertequalplistlib loadstestdatakeyedarchive data class misctestcaseunittest testcase def testallself notexported plistformat plistheader support checkallself plistlib notexportednotexported unittest skipunlesssys platform darwin plutil utility is for mac os class testplutilunittest testcase filename plutiltest plist properties fname h lname a marks a 100 b 0x10 exptectedproperties fname h lname a marks a 100 b 16 pl hextype 0x0100000c inttype 0o123 classmethod def setupclasscls none generate plist file with plistlib and parse with plutil with opencls filename wb as f plistlib dumpcls properties f fmtplistlib fmtbinary classmethod def teardownclasscls none os removecls filename def getlintstatusself return subprocess run plutil lint self filename captureoutputtrue texttrue stdout def converttojsonself subprocess run plutil convert json self filename def converttobinself subprocess run plutil convert binary1 self filename def writeplself with openself filename wb as f plistlib dumpself pl f fmtplistlib fmtbinary def testlintstatusself check lint status of file using plutil self assertequalfself filename okn self getlintstatus def checkcontentself check file content with plutil converting binary to json self converttojson with openself filename as f ff json loadsf read self assertequalff self exptectedproperties def checkplistlibparseself generate plist files with plutil and parse with plistlib self converttobin with openself filename rb as f self assertequalplistlib loadf self exptectedproperties def testoctalandhexself self writepl self converttojson with openself filename r as f p json loadsf read self assertequalp gethextype 16777228 self assertequalp getinttype 83 if name main unittest main c 2003 2013 python software foundation the testdata is generated using mac tools plistlib_generate_testdata py which using pyobjc to control the cocoa classes for generating plists pd94bwwgdmvyc2lvbj0ims4wiiblbmnvzgluzz0ivvrgltgipz4kpcfet0nu wvbfihbsaxn0ifbvqkxjqyails8vqxbwbguvl0rurcbqteltvcaxljavl0vo iiaiahr0cdovl3d3dy5hchbszs5jb20vrfrecy9qcm9wzxj0euxpc3qtms4w lmr0zci cjxwbglzdcb2zxjzaw9upsixljaipgo8zgljdd4kctxrzxk yujp z0luddwva2v5pgojpgludgvnzxi otiymzm3mjaznjg1ndc3ntc2ndwvaw50 zwdlcj4kctxrzxk yujpz0luddi8l2tlet4kctxpbnrlz2vypjkymjmznziw mzy4ntq3nzu4nti8l2ludgvnzxi cgk8a2v5pmfeyxrlpc9rzxk cgk8zgf0 zt4ymda0ltewlti2vdewojmzojmzwjwvzgf0zt4kctxrzxk yurpy3q8l2tl et4kctxkawn0pgojctxrzxk yuzhbhnlvmfsdwu8l2tlet4kcqk8zmfsc2uv pgojctxrzxk yvrydwvwywx1ztwva2v5pgojctx0cnvllz4kcqk8a2v5pmfv bmljb2rlvmfsdwu8l2tlet4kcqk8c3ryaw5npk3dphnzawcsie1hw588l3n0 cmluzz4kcqk8a2v5pmfub3rozxjtdhjpbmc8l2tlet4kcqk8c3ryaw5npizs ddtozwxsbyamyw1woyanagknihrozxjliszndds8l3n0cmluzz4kcqk8a2v5 pmrlzxblckrpy3q8l2tlet4kcqk8zgljdd4kcqkjpgtlet5hpc9rzxk cgkj ctxpbnrlz2vypje3pc9pbnrlz2vypgojcqk8a2v5pmi8l2tlet4kcqkjphjl yww mziuntwvcmvhbd4kcqkjpgtlet5jpc9rzxk cgkjctxhcnjhet4kcqkj ctxpbnrlz2vypje8l2ludgvnzxi cgkjcqk8aw50zwdlcj4ypc9pbnrlz2vy pgojcqkjphn0cmluzz50zxh0pc9zdhjpbmc cgkjctwvyxjyyxk cgkjpc9k awn0pgojpc9kawn0pgojpgtlet5hrmxvyxq8l2tlet4kctxyzwfspjauntwv cmvhbd4kctxrzxk yuxpc3q8l2tlet4kctxhcnjhet4kcqk8c3ryaw5npke8 l3n0cmluzz4kcqk8c3ryaw5npki8l3n0cmluzz4kcqk8aw50zwdlcj4xmjwv aw50zwdlcj4kcqk8cmvhbd4zmi41pc9yzwfspgojctxhcnjhet4kcqkjpglu dgvnzxi mtwvaw50zwdlcj4kcqkjpgludgvnzxi mjwvaw50zwdlcj4kcqkj pgludgvnzxi mzwvaw50zwdlcj4kcqk8l2fycmf5pgojpc9hcnjhet4kctxr zxk yu5lz2f0axzlqmlnsw50pc9rzxk cgk8aw50zwdlcj4todawmdawmdaw mda8l2ludgvnzxi cgk8a2v5pmfozwdhdgl2zuluddwva2v5pgojpgludgvn zxi ltu8l2ludgvnzxi cgk8a2v5pmftdhjpbmc8l2tlet4kctxzdhjpbmc rg9vzgfopc9zdhjpbmc cgk8a2v5pmfurw1wdhleawn0pc9rzxk cgk8zglj dc8 cgk8a2v5pmfurw1wdhlmaxn0pc9rzxk cgk8yxjyyxkvpgojpgtlet5h bkluddwva2v5pgojpgludgvnzxi nzi4pc9pbnrlz2vypgojpgtlet5uzxn0 zwreyxrhpc9rzxk cgk8yxjyyxk cgkjpgrhdge cgkjued4dmritwdimlln ww1sdvlysjvjr2qxym1zk0fbrunbenhzyjnseklhow1jr0pwym1gewvtqm5k vzvycgkjugdbqkfntthirzkwy3lcdlppqmlhvzvoy25rz1ozvnvhejrbqvfj rfbhehzkse1nyjjzz1ltbhvzweo1cgkjsudkmwjtcytbquvdqxp4c2izunpj rzltsudkcgjtrnllu0juzfc1clbnqujbz004ykc5mgn5qnzaaujpcgkjyvc1 agnua2dam1z1yxo0qufrsurqr3h2zehnz2iywwdzbwx1wvhknulhzdfibxmr quffq0f6ehnim1j6cgkjsuc5bulhsnbibuz5zvncbmrxnxjqz0fcqwdnogjh otbjeuj2wmlcawfxnwhjbmtnwjnwdwf6nefbuulecgkjued4dmritwdimlln ww1sdvlysjvjr2qxym1zk0fbrunbdz09cgkjpc9kyxrhpgojpc9hcnjhet4k ctxrzxk c29tzurhdge8l2tlet4kctxkyxrhpgojuedkcgjtrnllu0juzfc1 clbnpt0kctwvzgf0yt4kctxrzxk c29tzu1vcmveyxrhpc9rzxk cgk8zgf0 yt4kcvbhehzkse1nyjjzz1ltbhvzweo1sudkmwjtcytbquvdqxp4c2izunpj rzltsudkcgjtrnllu0juzfc1clbnqujbz004cglirzkwy3lcdlppqmlhvzvo y25rz1ozvnvhejrbqvfjrfbhehzkse1nyjjzz1ltbhvzweo1sudkmwjtcytb quvdqxp4cwojyjnseklhow1jr0pwym1gewvtqm5kvzvyugdbqkfntthirzkw y3lcdlppqmlhvzvoy25rz1ozvnvhejrbqvfjrfbhehykcwritwdimllnww1s dvlysjvjr2qxym1zk0fbrunbenhzyjnseklhow1jr0pwym1gewvtqm5kvzvy ugdbqkfntthirzkwcgljeuj2wmlcawfxnwhjbmtnwjnwdwf6nefbuuleued4 dmritwdimllnww1sdvlysjvjr2qxym1zk0fbrunbdz09cgk8l2rhdge cgk8 a2v5psofymvucmfhpc9rzxk cgk8c3ryaw5nplroyxqgd2fzigegdw5py29k zsbrzxkupc9zdhjpbmc cjwvzgljdd4kpc9wbglzdd4k ynbsaxn0mddfebabagmebqyhcakkcwwndg8qeritfcgplzaxmjm0ntc2ofdh qmlnsw50wgfcawdjbnqyvwfeyxrlvwfeawn0vmfgbg9hdfvhtglzdf8qd2fo zwdhdgl2zujpz0ludfxhtmvnyxrpdmvjbnrxyvn0cmluz1thbkvtchr5rglj dfthbkvtchr5tglzdfvhbkludfpuzxn0zwreyxrhwhnvbwveyxrhxhnvbwvn b3jlrgf0ywcaxqbiaguabgbyageayrn 1bqaaaaaaaaaaiaaaaaa aaasm0gcux30aaaa1ruwfxgzghschr5byuzhbhnlvmfsdwvayvrydwvwywx1 zv1hvw5py29kzvzhbhvlxwfub3rozxjtdhjpbmdazgvlcgvyrgljdagjawbn aoqacwbzagkazwasacaatqbhan9febu8agvsbg8gjianagknihrozxjlit7t hyahiimkuwfrylfjebejqebaaaaaaacjjsyneaeqalr0zxh0iz gaaaaaaaa psorlcmtuufrqhamoyumlhade 1fooaae 7vkrvb2rhancg eqlyotzpepo8bg90cybvzibiaw5hcnkgz3vuaz4aaqidpgxvdhmgb2ygymlu yxj5igd1bms aaecazxsb3rzig9migjpbmfyesbndw5rpgabagm8bg90cybv zibiaw5hcnkgz3vuaz4aaqidpgxvdhmgb2ygymluyxj5igd1bms aaecazxs b3rzig9migjpbmfyesbndw5rpgabagm8bg90cybvzibiaw5hcnkgz3vuaz4a aqidpgxvdhmgb2ygymluyxj5igd1bms aaecazxsb3rzig9migjpbmfyesbn dw5rpgabagm8bg90cybvzibiaw5hcnkgz3vuaz4aaqidttxiaw5hcnkgz3vu az5febduagf0ihdhcybhihvuawnvzguga2v5lgaiacsamwa8aeiasabpafua zwb0ahwaiacuajoapqcualsaygdtaoqa7qd4aqqbdwedassbnge3atgbtwfn aw4bcafyaxqbdgf aymbhqghaywblqgbaz0bnwghaaubpwgwabkbwahbacib xqhhasqc0gaaaaaaaaibaaaaaaaaadkaaaaaaaaaaaaaaaaaaals ynbsaxn0mdduaqidbaughb1yjhzlcnnpb25yjg9iamvjdhnzjgfyy2hpdmvy vcr0b3asaaggokmhca9vjg51bgztcqolda0ovnb5dhlwzvyky2xhc3nztlmu c3ryaw5neagaal8qe0tleufyy2hpdmugvuleifrlc3ttebesexqzwirjbgfz c25hbwvyjgnsyxnzzxnbjgnsyxnzagludhnfebdpq19cdwlsdgluuhl0ag9u vw5py29kzaqvfhcyxxaxt0nfqnvpbhrpblb5dghvblvuawnvzgvfebbpq19q exrob25vbmljb2rlwe5tu3ryaw5nwe5tt2jqzwn0ohobxxapt0nfuhl0ag9u u3ryaw5nwe5tu3ryaw5nxxaptlnlzxllzefyy2hpdmvy0r4fvhjvb3saaqai abeaggajac0amga3adsaqqbiae8avgbgagiazab6aieajacvakeauwdaanoa 7qd2ap8bageuar0blweyatcaaaaaaaacaqaaaaaaaaagaaaaaaaaaaaaaaaa aaaboq xml version 1 0 encoding utf 8 doctype plist public apple dtd plist 1 0 en http www apple com dtds propertylist 1 0 dtd entity entity replacement text plist version 1 0 dict key a key string entity string dict plist r n and t are the only legal control chars in xml expat does not support utf 32 b utf 32 utf 32 le codecs bom_utf32_le b utf 32 utf 32 be codecs bom_utf32_be dodge the negative check in the constructor dodge the size check in the constructor issue 21538 refs and offsets are 24 bit integers test effectiveness of saving duplicated objects recursive list recursive tuple recursive dict issue 26709 32 bit timestamp out of range this is the structure of a nskeyedarchive packed plist generate plist file with plistlib and parse with plutil convert binary file to json using plutil convert file to binary using plutil write hex properties to file using writeplist check lint status of file using plutil check file content with plutil converting binary to json generate plist files with plutil and parse with plistlib
import copy
import operator
import pickle
import struct
import unittest
import plistlib
import os
import sys
import json
import datetime
import codecs
import subprocess
import binascii
import collections
from test import support
from test.support import os_helper
from io import BytesIO

from plistlib import UID

ALL_FORMATS=(plistlib.FMT_XML, plistlib.FMT_BINARY)



TESTDATA={
    plistlib.FMT_XML: binascii.a2b_base64(b),
    plistlib.FMT_BINARY: binascii.a2b_base64(b),
    'KEYED_ARCHIVE': binascii.a2b_base64(b),
}

XML_PLIST_WITH_ENTITY=b

INVALID_BINARY_PLISTS = [
    ('too short data',
        b''
    ),
    ('too large offset_table_offset and offset_size = 1',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x2a'
    ),
    ('too large offset_table_offset and nonstandard offset_size',
        b'\x00\x00\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x03\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x2c'
    ),
    ('integer overflow in offset_table_offset',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\xff\xff\xff\xff\xff\xff\xff\xff'
    ),
    ('too large top_object',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('integer overflow in top_object',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\xff\xff\xff\xff\xff\xff\xff\xff'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('too large num_objects and offset_size = 1',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\xff'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('too large num_objects and nonstandard offset_size',
        b'\x00\x00\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x03\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\xff'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('extremally large num_objects (32 bit)',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x7f\xff\xff\xff'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('extremally large num_objects (64 bit)',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\xff\xff\xff\xff\xff'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('integer overflow in num_objects',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\xff\xff\xff\xff\xff\xff\xff\xff'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('offset_size = 0',
        b'\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('ref_size = 0',
        b'\xa1\x01\x00\x08\x0a'
        b'\x00\x00\x00\x00\x00\x00\x01\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0b'
    ),
    ('too large offset',
        b'\x00\x2a'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('integer overflow in offset',
        b'\x00\xff\xff\xff\xff\xff\xff\xff\xff'
        b'\x00\x00\x00\x00\x00\x00\x08\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x09'
    ),
    ('too large array size',
        b'\xaf\x00\x01\xff\x00\x08\x0c'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0d'
    ),
    ('extremally large array size (32-bit)',
        b'\xaf\x02\x7f\xff\xff\xff\x01\x00\x08\x0f'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x10'
    ),
    ('extremally large array size (64-bit)',
        b'\xaf\x03\x00\x00\x00\xff\xff\xff\xff\xff\x01\x00\x08\x13'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x14'
    ),
    ('integer overflow in array size',
        b'\xaf\x03\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x08\x13'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x14'
    ),
    ('too large reference index',
        b'\xa1\x02\x00\x08\x0a'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0b'
    ),
    ('integer overflow in reference index',
        b'\xa1\xff\xff\xff\xff\xff\xff\xff\xff\x00\x08\x11'
        b'\x00\x00\x00\x00\x00\x00\x01\x08'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x12'
    ),
    ('too large bytes size',
        b'\x4f\x00\x23\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0c'
    ),
    ('extremally large bytes size (32-bit)',
        b'\x4f\x02\x7f\xff\xff\xff\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0f'
    ),
    ('extremally large bytes size (64-bit)',
        b'\x4f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x13'
    ),
    ('integer overflow in bytes size',
        b'\x4f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x13'
    ),
    ('too large ASCII size',
        b'\x5f\x00\x23\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0c'
    ),
    ('extremally large ASCII size (32-bit)',
        b'\x5f\x02\x7f\xff\xff\xff\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0f'
    ),
    ('extremally large ASCII size (64-bit)',
        b'\x5f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x13'
    ),
    ('integer overflow in ASCII size',
        b'\x5f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x13'
    ),
    ('invalid ASCII',
        b'\x51\xff\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0a'
    ),
    ('too large UTF-16 size',
        b'\x6f\x00\x13\x20\xac\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0e'
    ),
    ('extremally large UTF-16 size (32-bit)',
        b'\x6f\x02\x4f\xff\xff\xff\x20\xac\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x11'
    ),
    ('extremally large UTF-16 size (64-bit)',
        b'\x6f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x20\xac\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x15'
    ),
    ('integer overflow in UTF-16 size',
        b'\x6f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x20\xac\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x15'
    ),
    ('invalid UTF-16',
        b'\x61\xd8\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0b'
    ),
    ('non-hashable key',
        b'\xd1\x01\x01\xa0\x08\x0b'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x02'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x0c'
    ),
    ('too large datetime (datetime overflow)',
        b'\x33\x42\x50\x00\x00\x00\x00\x00\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x11'
    ),
    ('too large datetime (timedelta overflow)',
        b'\x33\x42\xe0\x00\x00\x00\x00\x00\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x11'
    ),
    ('invalid datetime (Infinity)',
        b'\x33\x7f\xf0\x00\x00\x00\x00\x00\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x11'
    ),
    ('invalid datetime (NaN)',
        b'\x33\x7f\xf8\x00\x00\x00\x00\x00\x00\x08'
        b'\x00\x00\x00\x00\x00\x00\x01\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x01'
        b'\x00\x00\x00\x00\x00\x00\x00\x00'
        b'\x00\x00\x00\x00\x00\x00\x00\x11'
    ),
]


class TestPlistlib(unittest.TestCase):

    def tearDown(self):
        try:
            os.unlink(os_helper.TESTFN)
        except:
            pass

    def _create(self, fmt=None):
        pl = dict(
            aString="Doodah",
            aList=["A", "B", 12, 32.5, [1, 2, 3]],
            aFloat = 0.5,
            anInt = 728,
            aBigInt = 2 ** 63 - 44,
            aBigInt2 = 2 ** 63 + 44,
            aNegativeInt = -5,
            aNegativeBigInt = -80000000000,
            aDict=dict(
                anotherString="<hello & 'hi' there!>",
                aUnicodeValue='M\xe4ssig, Ma\xdf',
                aTrueValue=True,
                aFalseValue=False,
                deeperDict=dict(a=17, b=32.5, c=[1, 2, "text"]),
            ),
            someData = b"<binary gunk>",
            someMoreData = b"<lots of binary gunk>\0\1\2\3" * 10,
            nestedData = [b"<lots of binary gunk>\0\1\2\3" * 10],
            aDate = datetime.datetime(2004, 10, 26, 10, 33, 33),
            anEmptyDict = dict(),
            anEmptyList = list()
        )
        pl['\xc5benraa'] = "That was a unicode key."
        return pl

    def test_create(self):
        pl = self._create()
        self.assertEqual(pl["aString"], "Doodah")
        self.assertEqual(pl["aDict"]["aFalseValue"], False)

    def test_io(self):
        pl = self._create()
        with open(os_helper.TESTFN, 'wb') as fp:
            plistlib.dump(pl, fp)

        with open(os_helper.TESTFN, 'rb') as fp:
            pl2 = plistlib.load(fp)

        self.assertEqual(dict(pl), dict(pl2))

        self.assertRaises(AttributeError, plistlib.dump, pl, 'filename')
        self.assertRaises(AttributeError, plistlib.load, 'filename')

    def test_invalid_type(self):
        pl = [ object() ]

        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt)

    def test_invalid_uid(self):
        with self.assertRaises(TypeError):
            UID("not an int")
        with self.assertRaises(ValueError):
            UID(2 ** 64)
        with self.assertRaises(ValueError):
            UID(-19)

    def test_int(self):
        for pl in [0, 2**8-1, 2**8, 2**16-1, 2**16, 2**32-1, 2**32,
                   2**63-1, 2**64-1, 1, -2**63]:
            for fmt in ALL_FORMATS:
                with self.subTest(pl=pl, fmt=fmt):
                    data = plistlib.dumps(pl, fmt=fmt)
                    pl2 = plistlib.loads(data)
                    self.assertIsInstance(pl2, int)
                    self.assertEqual(pl, pl2)
                    data2 = plistlib.dumps(pl2, fmt=fmt)
                    self.assertEqual(data, data2)

        for fmt in ALL_FORMATS:
            for pl in (2 ** 64 + 1, 2 ** 127-1, -2**64, -2 ** 127):
                with self.subTest(pl=pl, fmt=fmt):
                    self.assertRaises(OverflowError, plistlib.dumps,
                                      pl, fmt=fmt)

    def test_bytearray(self):
        for pl in (b'<binary gunk>', b"<lots of binary gunk>\0\1\2\3" * 10):
            for fmt in ALL_FORMATS:
                with self.subTest(pl=pl, fmt=fmt):
                    data = plistlib.dumps(bytearray(pl), fmt=fmt)
                    pl2 = plistlib.loads(data)
                    self.assertIsInstance(pl2, bytes)
                    self.assertEqual(pl2, pl)
                    data2 = plistlib.dumps(pl2, fmt=fmt)
                    self.assertEqual(data, data2)

    def test_bytes(self):
        pl = self._create()
        data = plistlib.dumps(pl)
        pl2 = plistlib.loads(data)
        self.assertEqual(dict(pl), dict(pl2))
        data2 = plistlib.dumps(pl2)
        self.assertEqual(data, data2)

    def test_indentation_array(self):
        data = [[[[[[[[{'test': b'aaaaaa'}]]]]]]]]
        self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)

    def test_indentation_dict(self):
        data = {'1': {'2': {'3': {'4': {'5': {'6': {'7': {'8': {'9': b'aaaaaa'}}}}}}}}}
        self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)

    def test_indentation_dict_mix(self):
        data = {'1': {'2': [{'3': [[[[[{'test': b'aaaaaa'}]]]]]}]}}
        self.assertEqual(plistlib.loads(plistlib.dumps(data)), data)

    def test_uid(self):
        data = UID(1)
        self.assertEqual(plistlib.loads(plistlib.dumps(data, fmt=plistlib.FMT_BINARY)), data)
        dict_data = {
            'uid0': UID(0),
            'uid2': UID(2),
            'uid8': UID(2 ** 8),
            'uid16': UID(2 ** 16),
            'uid32': UID(2 ** 32),
            'uid63': UID(2 ** 63)
        }
        self.assertEqual(plistlib.loads(plistlib.dumps(dict_data, fmt=plistlib.FMT_BINARY)), dict_data)

    def test_uid_data(self):
        uid = UID(1)
        self.assertEqual(uid.data, 1)

    def test_uid_eq(self):
        self.assertEqual(UID(1), UID(1))
        self.assertNotEqual(UID(1), UID(2))
        self.assertNotEqual(UID(1), "not uid")

    def test_uid_hash(self):
        self.assertEqual(hash(UID(1)), hash(UID(1)))

    def test_uid_repr(self):
        self.assertEqual(repr(UID(1)), "UID(1)")

    def test_uid_index(self):
        self.assertEqual(operator.index(UID(1)), 1)

    def test_uid_pickle(self):
        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
            self.assertEqual(pickle.loads(pickle.dumps(UID(19), protocol=proto)), UID(19))

    def test_uid_copy(self):
        self.assertEqual(copy.copy(UID(1)), UID(1))
        self.assertEqual(copy.deepcopy(UID(1)), UID(1))

    def test_appleformatting(self):
        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                pl = plistlib.loads(TESTDATA[fmt])
                data = plistlib.dumps(pl, fmt=fmt)
                self.assertEqual(data, TESTDATA[fmt],
                    "generated data was not identical to Apple's output")


    def test_appleformattingfromliteral(self):
        self.maxDiff = None
        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                pl = self._create(fmt=fmt)
                pl2 = plistlib.loads(TESTDATA[fmt], fmt=fmt)
                self.assertEqual(dict(pl), dict(pl2),
                    "generated data was not identical to Apple's output")
                pl2 = plistlib.loads(TESTDATA[fmt])
                self.assertEqual(dict(pl), dict(pl2),
                    "generated data was not identical to Apple's output")

    def test_bytesio(self):
        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                b = BytesIO()
                pl = self._create(fmt=fmt)
                plistlib.dump(pl, b, fmt=fmt)
                pl2 = plistlib.load(BytesIO(b.getvalue()), fmt=fmt)
                self.assertEqual(dict(pl), dict(pl2))
                pl2 = plistlib.load(BytesIO(b.getvalue()))
                self.assertEqual(dict(pl), dict(pl2))

    def test_keysort_bytesio(self):
        pl = collections.OrderedDict()
        pl['b'] = 1
        pl['a'] = 2
        pl['c'] = 3

        for fmt in ALL_FORMATS:
            for sort_keys in (False, True):
                with self.subTest(fmt=fmt, sort_keys=sort_keys):
                    b = BytesIO()

                    plistlib.dump(pl, b, fmt=fmt, sort_keys=sort_keys)
                    pl2 = plistlib.load(BytesIO(b.getvalue()),
                        dict_type=collections.OrderedDict)

                    self.assertEqual(dict(pl), dict(pl2))
                    if sort_keys:
                        self.assertEqual(list(pl2.keys()), ['a', 'b', 'c'])
                    else:
                        self.assertEqual(list(pl2.keys()), ['b', 'a', 'c'])

    def test_keysort(self):
        pl = collections.OrderedDict()
        pl['b'] = 1
        pl['a'] = 2
        pl['c'] = 3

        for fmt in ALL_FORMATS:
            for sort_keys in (False, True):
                with self.subTest(fmt=fmt, sort_keys=sort_keys):
                    data = plistlib.dumps(pl, fmt=fmt, sort_keys=sort_keys)
                    pl2 = plistlib.loads(data, dict_type=collections.OrderedDict)

                    self.assertEqual(dict(pl), dict(pl2))
                    if sort_keys:
                        self.assertEqual(list(pl2.keys()), ['a', 'b', 'c'])
                    else:
                        self.assertEqual(list(pl2.keys()), ['b', 'a', 'c'])

    def test_keys_no_string(self):
        pl = { 42: 'aNumber' }

        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                self.assertRaises(TypeError, plistlib.dumps, pl, fmt=fmt)

                b = BytesIO()
                self.assertRaises(TypeError, plistlib.dump, pl, b, fmt=fmt)

    def test_skipkeys(self):
        pl = {
            42: 'aNumber',
            'snake': 'aWord',
        }

        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                data = plistlib.dumps(
                    pl, fmt=fmt, skipkeys=True, sort_keys=False)

                pl2 = plistlib.loads(data)
                self.assertEqual(pl2, {'snake': 'aWord'})

                fp = BytesIO()
                plistlib.dump(
                    pl, fp, fmt=fmt, skipkeys=True, sort_keys=False)
                data = fp.getvalue()
                pl2 = plistlib.loads(fp.getvalue())
                self.assertEqual(pl2, {'snake': 'aWord'})

    def test_tuple_members(self):
        pl = {
            'first': (1, 2),
            'second': (1, 2),
            'third': (3, 4),
        }

        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                data = plistlib.dumps(pl, fmt=fmt)
                pl2 = plistlib.loads(data)
                self.assertEqual(pl2, {
                    'first': [1, 2],
                    'second': [1, 2],
                    'third': [3, 4],
                })
                if fmt != plistlib.FMT_BINARY:
                    self.assertIsNot(pl2['first'], pl2['second'])

    def test_list_members(self):
        pl = {
            'first': [1, 2],
            'second': [1, 2],
            'third': [3, 4],
        }

        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                data = plistlib.dumps(pl, fmt=fmt)
                pl2 = plistlib.loads(data)
                self.assertEqual(pl2, {
                    'first': [1, 2],
                    'second': [1, 2],
                    'third': [3, 4],
                })
                self.assertIsNot(pl2['first'], pl2['second'])

    def test_dict_members(self):
        pl = {
            'first': {'a': 1},
            'second': {'a': 1},
            'third': {'b': 2 },
        }

        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                data = plistlib.dumps(pl, fmt=fmt)
                pl2 = plistlib.loads(data)
                self.assertEqual(pl2, {
                    'first': {'a': 1},
                    'second': {'a': 1},
                    'third': {'b': 2 },
                })
                self.assertIsNot(pl2['first'], pl2['second'])

    def test_controlcharacters(self):
        for i in range(128):
            c = chr(i)
            testString = "string containing %s" % c
            if i >= 32 or c in "\r\n\t":
                
                data = plistlib.dumps(testString, fmt=plistlib.FMT_XML)
                if c != "\r":
                    self.assertEqual(plistlib.loads(data), testString)
            else:
                with self.assertRaises(ValueError):
                    plistlib.dumps(testString, fmt=plistlib.FMT_XML)
            plistlib.dumps(testString, fmt=plistlib.FMT_BINARY)

    def test_non_bmp_characters(self):
        pl = {'python': '\U0001f40d'}
        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                data = plistlib.dumps(pl, fmt=fmt)
                self.assertEqual(plistlib.loads(data), pl)

    def test_lone_surrogates(self):
        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                with self.assertRaises(UnicodeEncodeError):
                    plistlib.dumps('\ud8ff', fmt=fmt)
                with self.assertRaises(UnicodeEncodeError):
                    plistlib.dumps('\udcff', fmt=fmt)

    def test_nondictroot(self):
        for fmt in ALL_FORMATS:
            with self.subTest(fmt=fmt):
                test1 = "abc"
                test2 = [1, 2, 3, "abc"]
                result1 = plistlib.loads(plistlib.dumps(test1, fmt=fmt))
                result2 = plistlib.loads(plistlib.dumps(test2, fmt=fmt))
                self.assertEqual(test1, result1)
                self.assertEqual(test2, result2)

    def test_invalidarray(self):
        for i in ["<key>key inside an array</key>",
                  "<key>key inside an array2</key><real>3</real>",
                  "<true/><key>key inside an array3</key>"]:
            self.assertRaises(ValueError, plistlib.loads,
                              ("<plist><array>%s</array></plist>"%i).encode())

    def test_invaliddict(self):
        for i in ["<key><true/>k</key><string>compound key</string>",
                  "<key>single key</key>",
                  "<string>missing key</string>",
                  "<key>k1</key><string>v1</string><real>5.3</real>"
                  "<key>k1</key><key>k2</key><string>double key</string>"]:
            self.assertRaises(ValueError, plistlib.loads,
                              ("<plist><dict>%s</dict></plist>"%i).encode())
            self.assertRaises(ValueError, plistlib.loads,
                              ("<plist><array><dict>%s</dict></array></plist>"%i).encode())

    def test_invalidinteger(self):
        self.assertRaises(ValueError, plistlib.loads,
                          b"<plist><integer>not integer</integer></plist>")

    def test_invalidreal(self):
        self.assertRaises(ValueError, plistlib.loads,
                          b"<plist><integer>not real</integer></plist>")

    def test_integer_notations(self):
        pl = b"<plist><integer>456</integer></plist>"
        value = plistlib.loads(pl)
        self.assertEqual(value, 456)

        pl = b"<plist><integer>0xa</integer></plist>"
        value = plistlib.loads(pl)
        self.assertEqual(value, 10)

        pl = b"<plist><integer>0123</integer></plist>"
        value = plistlib.loads(pl)
        self.assertEqual(value, 123)

    def test_xml_encodings(self):
        base = TESTDATA[plistlib.FMT_XML]

        for xml_encoding, encoding, bom in [
                    (b'utf-8', 'utf-8', codecs.BOM_UTF8),
                    (b'utf-16', 'utf-16-le', codecs.BOM_UTF16_LE),
                    (b'utf-16', 'utf-16-be', codecs.BOM_UTF16_BE),
                    
                    
                    
                ]:

            pl = self._create(fmt=plistlib.FMT_XML)
            with self.subTest(encoding=encoding):
                data = base.replace(b'UTF-8', xml_encoding)
                data = bom + data.decode('utf-8').encode(encoding)
                pl2 = plistlib.loads(data)
                self.assertEqual(dict(pl), dict(pl2))

    def test_dump_invalid_format(self):
        with self.assertRaises(ValueError):
            plistlib.dumps({}, fmt="blah")

    def test_load_invalid_file(self):
        with self.assertRaises(plistlib.InvalidFileException):
            plistlib.loads(b"these are not plist file contents")

    def test_modified_uid_negative(self):
        neg_uid = UID(1)
        neg_uid.data = -1  
        with self.assertRaises(ValueError):
            plistlib.dumps(neg_uid, fmt=plistlib.FMT_BINARY)

    def test_modified_uid_huge(self):
        huge_uid = UID(1)
        huge_uid.data = 2 ** 64  
        with self.assertRaises(OverflowError):
            plistlib.dumps(huge_uid, fmt=plistlib.FMT_BINARY)

    def test_xml_plist_with_entity_decl(self):
        with self.assertRaisesRegex(plistlib.InvalidFileException,
                                    "XML entity declarations are not supported"):
            plistlib.loads(XML_PLIST_WITH_ENTITY, fmt=plistlib.FMT_XML)


class TestBinaryPlistlib(unittest.TestCase):

    @staticmethod
    def decode(*objects, offset_size=1, ref_size=1):
        data = [b'bplist00']
        offset = 8
        offsets = []
        for x in objects:
            offsets.append(offset.to_bytes(offset_size, 'big'))
            data.append(x)
            offset += len(x)
        tail = struct.pack('>6xBBQQQ', offset_size, ref_size,
                           len(objects), 0, offset)
        data.extend(offsets)
        data.append(tail)
        return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY)

    def test_nonstandard_refs_size(self):
        
        data = (b'bplist00'
                b'\xd1\x00\x00\x01\x00\x00\x02QaQb'
                b'\x00\x00\x08\x00\x00\x0f\x00\x00\x11'
                b'\x00\x00\x00\x00\x00\x00'
                b'\x03\x03'
                b'\x00\x00\x00\x00\x00\x00\x00\x03'
                b'\x00\x00\x00\x00\x00\x00\x00\x00'
                b'\x00\x00\x00\x00\x00\x00\x00\x13')
        self.assertEqual(plistlib.loads(data), {'a': 'b'})

    def test_dump_duplicates(self):
        
        for x in (None, False, True, 12345, 123.45, 'abcde', 'абвгд', b'abcde',
                  datetime.datetime(2004, 10, 26, 10, 33, 33),
                  bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
            with self.subTest(x=x):
                data = plistlib.dumps([x]*1000, fmt=plistlib.FMT_BINARY)
                self.assertLess(len(data), 1100, repr(data))

    def test_identity(self):
        for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde',
                  datetime.datetime(2004, 10, 26, 10, 33, 33),
                  bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
            with self.subTest(x=x):
                data = plistlib.dumps([x]*2, fmt=plistlib.FMT_BINARY)
                a, b = plistlib.loads(data)
                if isinstance(x, tuple):
                    x = list(x)
                self.assertEqual(a, x)
                self.assertEqual(b, x)
                self.assertIs(a, b)

    def test_cycles(self):
        
        a = []
        a.append(a)
        b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
        self.assertIs(b[0], b)
        
        a = ([],)
        a[0].append(a)
        b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
        self.assertIs(b[0][0], b)
        
        a = {}
        a['x'] = a
        b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
        self.assertIs(b['x'], b)

    def test_deep_nesting(self):
        for N in [300, 100000]:
            chunks = [b'\xa1' + (i + 1).to_bytes(4, 'big') for i in range(N)]
            try:
                result = self.decode(*chunks, b'\x54seed', offset_size=4, ref_size=4)
            except RecursionError:
                pass
            else:
                for i in range(N):
                    self.assertIsInstance(result, list)
                    self.assertEqual(len(result), 1)
                    result = result[0]
                self.assertEqual(result, 'seed')

    def test_large_timestamp(self):
        
        for ts in -2**31-1, 2**31:
            with self.subTest(ts=ts):
                d = (datetime.datetime(1970, 1, 1, 0, 0) +
                     datetime.timedelta(seconds=ts))
                data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
                self.assertEqual(plistlib.loads(data), d)

    def test_load_singletons(self):
        self.assertIs(self.decode(b'\x00'), None)
        self.assertIs(self.decode(b'\x08'), False)
        self.assertIs(self.decode(b'\x09'), True)
        self.assertEqual(self.decode(b'\x0f'), b'')

    def test_load_int(self):
        self.assertEqual(self.decode(b'\x10\x00'), 0)
        self.assertEqual(self.decode(b'\x10\xfe'), 0xfe)
        self.assertEqual(self.decode(b'\x11\xfe\xdc'), 0xfedc)
        self.assertEqual(self.decode(b'\x12\xfe\xdc\xba\x98'), 0xfedcba98)
        self.assertEqual(self.decode(b'\x13\x01\x23\x45\x67\x89\xab\xcd\xef'),
                         0x0123456789abcdef)
        self.assertEqual(self.decode(b'\x13\xfe\xdc\xba\x98\x76\x54\x32\x10'),
                         -0x123456789abcdf0)

    def test_unsupported(self):
        unsupported = [*range(1, 8), *range(10, 15),
                       0x20, 0x21, *range(0x24, 0x33), *range(0x34, 0x40)]
        for i in [0x70, 0x90, 0xb0, 0xc0, 0xe0, 0xf0]:
            unsupported.extend(i + j for j in range(16))
        for token in unsupported:
            with self.subTest(f'token {token:02x}'):
                with self.assertRaises(plistlib.InvalidFileException):
                    self.decode(bytes([token]) + b'\x00'*16)

    def test_invalid_binary(self):
        for name, data in INVALID_BINARY_PLISTS:
            with self.subTest(name):
                with self.assertRaises(plistlib.InvalidFileException):
                    plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)


class TestKeyedArchive(unittest.TestCase):
    def test_keyed_archive_data(self):
        
        data = {
            '$version': 100000,
            '$objects': [
                '$null', {
                    'pytype': 1,
                    '$class': UID(2),
                    'NS.string': 'KeyArchive UID Test'
                },
                {
                    '$classname': 'OC_BuiltinPythonUnicode',
                    '$classes': [
                        'OC_BuiltinPythonUnicode',
                        'OC_PythonUnicode',
                        'NSString',
                        'NSObject'
                    ],
                    '$classhints': [
                        'OC_PythonString', 'NSString'
                    ]
                }
            ],
            '$archiver': 'NSKeyedArchiver',
            '$top': {
                'root': UID(1)
            }
        }
        self.assertEqual(plistlib.loads(TESTDATA["KEYED_ARCHIVE"]), data)


class MiscTestCase(unittest.TestCase):
    def test__all__(self):
        not_exported = {"PlistFormat", "PLISTHEADER"}
        support.check__all__(self, plistlib, not_exported=not_exported)

@unittest.skipUnless(sys.platform == "darwin", "plutil utility is for Mac os")
class TestPlutil(unittest.TestCase):
    file_name = "plutil_test.plist"
    properties = {
            "fname" : "H",
            "lname":"A",
            "marks" : {"a":100, "b":0x10}
        }
    exptected_properties = {
        "fname" : "H",
        "lname": "A",
        "marks" : {"a":100, "b":16}
    }
    pl = {
            "HexType" : 0x0100000c,
            "IntType" : 0o123
        }

    @classmethod
    def setUpClass(cls) -> None:
        
        with open(cls.file_name,'wb') as f:
            plistlib.dump(cls.properties, f, fmt=plistlib.FMT_BINARY)

    @classmethod
    def tearDownClass(cls) -> None:
        os.remove(cls.file_name)

    def get_lint_status(self):
        return subprocess.run(['plutil', "-lint", self.file_name], capture_output=True, text=True).stdout

    def convert_to_json(self):
        
        subprocess.run(['plutil', "-convert", 'json', self.file_name])

    def convert_to_bin(self):
        
        subprocess.run(['plutil', "-convert", 'binary1', self.file_name])

    def write_pl(self):
        
        with open(self.file_name, 'wb') as f:
            plistlib.dump(self.pl, f, fmt=plistlib.FMT_BINARY)

    def test_lint_status(self):
        
        self.assertEqual(f"{self.file_name}: OK\n", self.get_lint_status())

    def check_content(self):
        
        self.convert_to_json()
        with open(self.file_name) as f:
            ff = json.loads(f.read())
            self.assertEqual(ff, self.exptected_properties)

    def check_plistlib_parse(self):
        
        self.convert_to_bin()
        with open(self.file_name, 'rb') as f:
            self.assertEqual(plistlib.load(f), self.exptected_properties)

    def test_octal_and_hex(self):
        self.write_pl()
        self.convert_to_json()
        with open(self.file_name, 'r') as f:
            p = json.loads(f.read())
            self.assertEqual(p.get("HexType"), 16777228)
            self.assertEqual(p.get("IntType"), 83)

if __name__ == '__main__':
    unittest.main()