# Copyright (C) 2003 Python Software Foundation

import unittest
import plistlib
import os
import datetime
from test import support


# This test data was generated through Cocoa's NSDictionary class
TESTDATA = b"""<?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">
<plist version="1.0">
<dict>
        <key>aDate</key>
        <date>2004-10-26T10:33:33Z</date>
        <key>aDict</key>
        <dict>
                <key>aFalseValue</key>
                <false/>
                <key>aTrueValue</key>
                <true/>
                <key>aUnicodeValue</key>
                <string>M\xc3\xa4ssig, Ma\xc3\x9f</string>
                <key>anotherString</key>
                <string>&lt;hello &amp; 'hi' there!&gt;</string>
                <key>deeperDict</key>
                <dict>
                        <key>a</key>
                        <integer>17</integer>
                        <key>b</key>
                        <real>32.5</real>
                        <key>c</key>
                        <array>
                                <integer>1</integer>
                                <integer>2</integer>
                                <string>text</string>
                        </array>
                </dict>
        </dict>
        <key>aFloat</key>
        <real>0.5</real>
        <key>aList</key>
        <array>
                <string>A</string>
                <string>B</string>
                <integer>12</integer>
                <real>32.5</real>
                <array>
                        <integer>1</integer>
                        <integer>2</integer>
                        <integer>3</integer>
                </array>
        </array>
        <key>aString</key>
        <string>Doodah</string>
        <key>anEmptyDict</key>
        <dict/>
        <key>anEmptyList</key>
        <array/>
        <key>anInt</key>
        <integer>728</integer>
        <key>nestedData</key>
        <array>
                <data>
                PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5r
                PgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5
                IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBi
                aW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3Rz
                IG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQID
                PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAw==
                </data>
        </array>
        <key>someData</key>
        <data>
        PGJpbmFyeSBndW5rPg==
        </data>
        <key>someMoreData</key>
        <data>
        PGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8
        bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAzxs
        b3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxv
        dHMgb2YgYmluYXJ5IGd1bms+AAECAzxsb3RzIG9mIGJpbmFyeSBndW5rPgABAgM8bG90
        cyBvZiBiaW5hcnkgZ3Vuaz4AAQIDPGxvdHMgb2YgYmluYXJ5IGd1bms+AAECAw==
        </data>
        <key>\xc3\x85benraa</key>
        <string>That was a unicode key.</string>
</dict>
</plist>
""".replace(b" " * 8, b"\t")  # Apple as well as plistlib.py output hard tabs


class TestPlistlib(unittest.TestCase):

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

    def _create(self):
        pl = dict(
            aString="Doodah",
            aList=["A", "B", 12, 32.5, [1, 2, 3]],
            aFloat = 0.5,
            anInt = 728,
            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 = plistlib.Data(b"<binary gunk>"),
            someMoreData = plistlib.Data(b"<lots of binary gunk>\0\1\2\3" * 10),
            nestedData = [plistlib.Data(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()
        plistlib.writePlist(pl, support.TESTFN)
        pl2 = plistlib.readPlist(support.TESTFN)
        self.assertEqual(dict(pl), dict(pl2))

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

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

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

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

    def test_appleformatting(self):
        pl = plistlib.readPlistFromBytes(TESTDATA)
        data = plistlib.writePlistToBytes(pl)
        self.assertEqual(data, TESTDATA,
                         "generated data was not identical to Apple's output")

    def test_appleformattingfromliteral(self):
        pl = self._create()
        pl2 = plistlib.readPlistFromBytes(TESTDATA)
        self.assertEqual(dict(pl), dict(pl2),
                         "generated data was not identical to Apple's output")

    def test_bytesio(self):
        from io import BytesIO
        b = BytesIO()
        pl = self._create()
        plistlib.writePlist(pl, b)
        pl2 = plistlib.readPlist(BytesIO(b.getvalue()))
        self.assertEqual(dict(pl), dict(pl2))

    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":
                # \r, \n and \t are the only legal control chars in XML
                plistlib.writePlistToBytes(testString)
            else:
                self.assertRaises(ValueError,
                                  plistlib.writePlistToBytes,
                                  testString)

    def test_nondictroot(self):
        test1 = "abc"
        test2 = [1, 2, 3, "abc"]
        result1 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test1))
        result2 = plistlib.readPlistFromBytes(plistlib.writePlistToBytes(test2))
        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.readPlistFromBytes,
                              ("<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.readPlistFromBytes,
                              ("<plist><dict>%s</dict></plist>"%i).encode())
            self.assertRaises(ValueError, plistlib.readPlistFromBytes,
                              ("<plist><array><dict>%s</dict></array></plist>"%i).encode())

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

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


def test_main():
    support.run_unittest(TestPlistlib)


if __name__ == '__main__':
    test_main()
