import unittest

# These tests check that error handling in the Pyrex code is
# complete.
# It is likely that if there are errors, instead of failing the code
# will simply crash.

import gc
import os.path
import sys
import unittest
from lxml import etree

from .common_imports import HelperTestCase, IS_PYPY


class ErrorTestCase(HelperTestCase):
    etree = etree

    def test_bad_element(self):
        # attrib argument of Element() should be a dictionary, so if
        # we pass a string we should get an error.
        self.assertRaises(TypeError, self.etree.Element, 'a', 'b')

    def test_empty_parse(self):
        self.assertRaises(etree.XMLSyntaxError, etree.fromstring, '')

    @unittest.skipIf(IS_PYPY, "needs sys.getrefcount()")
    def test_element_cyclic_gc_none(self):
        # test if cyclic reference can crash etree
        Element = self.etree.Element
        getrefcount = sys.getrefcount

        # must disable tracing as it could change the refcounts
        trace_func = sys.gettrace()
        try:
            sys.settrace(None)
            gc.collect()

            count = getrefcount(None)

            l = [Element('name'), Element('name')]
            l.append(l)

            del l
            gc.collect()
            count = getrefcount(None) - count

            if sys.version_info[:2] == (3, 11) and count == -1:
                # FIXME: it's currently unclear why this happens, but it's reproducible on Py3.11.
                self.assertEqual(count, -1)
            else:
                self.assertEqual(count, 0)
        finally:
            sys.settrace(trace_func)

    def test_xmlsyntaxerror_has_info(self):
        broken_xml_name = 'test_broken.xml'
        broken_xml_path = os.path.join(os.path.dirname(__file__), broken_xml_name)
        fail_msg = 'test_broken.xml should raise an etree.XMLSyntaxError'
        try:
            etree.parse(broken_xml_path)
        except etree.XMLSyntaxError as e:
            # invariant
            self.assertEqual(e.position, (e.lineno, e.offset + 1), 'position and lineno/offset out of sync')
            # SyntaxError info derived from file & contents
            self.assertTrue(e.filename.endswith(broken_xml_name), 'filename must be preserved')
            self.assertEqual(e.lineno, 1)
            self.assertEqual(e.offset, 10)
        except Exception as e:
            self.fail(f'{fail_msg}, not {type(e)}')
        else:
            self.fail('test_broken.xml should raise an etree.XMLSyntaxError')


def test_suite():
    suite = unittest.TestSuite()
    suite.addTests([unittest.defaultTestLoader.loadTestsFromTestCase(ErrorTestCase)])
    return suite

if __name__ == '__main__':
    print('to test use test.py %s' % __file__)
