import os
import sys
import traceback
import StringIO
import unittest
import warnings

warnings.simplefilter("error")

from support import html5lib_test_files, TestData, convert, convertExpected
import html5lib
from html5lib import html5parser, treebuilders, constants

treeTypes = {"simpletree":treebuilders.getTreeBuilder("simpletree"),
             "DOM":treebuilders.getTreeBuilder("dom")}

#Try whatever etree implementations are avaliable from a list that are
#"supposed" to work
try:
    import xml.etree.ElementTree as ElementTree
    treeTypes['ElementTree'] = treebuilders.getTreeBuilder("etree", ElementTree, fullTree=True)
except ImportError:
    try:
        import elementtree.ElementTree as ElementTree
        treeTypes['ElementTree'] = treebuilders.getTreeBuilder("etree", ElementTree, fullTree=True)
    except ImportError:
        pass

try:
    import xml.etree.cElementTree as cElementTree
    treeTypes['cElementTree'] = treebuilders.getTreeBuilder("etree", cElementTree, fullTree=True)
except ImportError:
    try:
        import cElementTree
        treeTypes['cElementTree'] = treebuilders.getTreeBuilder("etree", cElementTree, fullTree=True)
    except ImportError:
        pass
    
try:
    try:
        import lxml.html as lxml
    except ImportError:
        import lxml.etree as lxml
    treeTypes['lxml'] = treebuilders.getTreeBuilder("lxml", lxml, fullTree=True)
except ImportError:
    pass

try:
    import BeautifulSoup
    treeTypes["beautifulsoup"] = treebuilders.getTreeBuilder("beautifulsoup", fullTree=True)
except ImportError:
    pass

#Try whatever dom implementations are avaliable from a list that are
#"supposed" to work
try:
    import pxdom
    treeTypes["pxdom"] = treebuilders.getTreeBuilder("dom", pxdom)
except ImportError:
    pass

#Run the parse error checks
checkParseErrors = False

#XXX - There should just be one function here but for some reason the testcase
#format differs from the treedump format by a single space character
def convertTreeDump(data):
    return "\n".join(convert(3)(data).split("\n")[1:])

import re
attrlist = re.compile(r"^(\s+)\w+(?:\s\w+)?=.*(?:\n\1\w+(?:\s\w+)?=.*)+",re.M)
def sortattrs(x):
  lines = x.group(0).split("\n")
  lines.sort()
  return "\n".join(lines)
namespaceExpected = re.compile(r"^(\s*)<(\S+)>", re.M).sub

class TestCase(unittest.TestCase):
    def runParserTest(self, innerHTML, input, expected, errors, treeClass,
        namespaceHTMLElements):
        #XXX - move this out into the setup function
        #concatenate all consecutive character tokens into a single token
        try:
            p = html5parser.HTMLParser(tree = treeClass,
                                       namespaceHTMLElements=namespaceHTMLElements)
        except constants.DataLossWarning:
            return 

        errors = [item.decode("utf-8") for item in errors]
        
        try:
            if innerHTML:
                document = p.parseFragment(StringIO.StringIO(input), innerHTML)
            else:
                try:
                    document = p.parse(StringIO.StringIO(input))
                except constants.DataLossWarning:
                    return 
        except:
            errorMsg = "\n".join(["\n\nInput:", input, "\nExpected:", expected,
                                  "\nTraceback:", traceback.format_exc()])
            self.assertTrue(False, errorMsg)
        
        output = convertTreeDump(p.tree.testSerializer(document))
        output = attrlist.sub(sortattrs, output)
        
        expected = convertExpected(expected)
        expected = attrlist.sub(sortattrs, expected)
        if namespaceHTMLElements:
            expected = namespaceExpected(r"\1<html \2>", expected)
        
        errorMsg = "\n".join(["\n\nInput:", input, "\nExpected:", expected,
                              "\nReceived:", output])
        self.assertEquals(expected, output, errorMsg.encode("utf-8"))
        errStr = [u"Line: %i Col: %i %s %s"%(line, col, 
                                         constants.E[errorcode], datavars) for
                  ((line,col), errorcode, datavars) in p.errors]
        
        errorMsg2 = u"\n".join([u"\n\nInput:", input,
                                u"\nExpected errors (" + str(len(errors)) + u"):\n" + u"\n".join(errors),
                                u"\nActual errors (" + str(len(p.errors)) + u"):\n" + u"\n".join(errStr)])
        if checkParseErrors:
            self.assertEquals(len(p.errors), len(errors), errorMsg2.encode("utf-8"))

def buildTestSuite():
    sys.stdout.write('Testing tree builders '+ " ".join(treeTypes.keys()) + "\n")

    for treeName, treeCls in treeTypes.iteritems():
        files = html5lib_test_files('tree-construction')
        #files = [f for f in files if 
        #         not f.split(".")[-2][-2:] in ("s9", "10", "11", "12")] #skip namespace tests for now
        for filename in files:
            testName = os.path.basename(filename).replace(".dat","")

            tests = TestData(filename, "data")

            for index, test in enumerate(tests):
                input, errors, innerHTML, expected = [test[key] for key in
                                                      'data', 'errors',
                                                      'document-fragment',
                                                      'document']
                if errors:
                    errors = errors.split("\n")
                
                for namespaceHTMLElements in (True, False):
                    def testFunc(self, innerHTML=innerHTML, input=input,
                        expected=expected, errors=errors, treeCls=treeCls,
                        namespaceHTMLElements=namespaceHTMLElements): 
                        return self.runParserTest(innerHTML, input, expected,
                                                  errors, treeCls,
                                                  namespaceHTMLElements)
                    testFunc.__name__ = "test_%s_%d_%s_%s" % (testName,index+1,treeName, namespaceHTMLElements and "namespaced" or "no_html_namespace")
                    setattr(TestCase, testFunc.__name__,
                         testFunc)

    return unittest.TestLoader().loadTestsFromTestCase(TestCase)

def main():
    # the following is temporary while the unit tests for parse errors are
    # still in flux
    if '-p' in sys.argv: # suppress check for parse errors
        sys.argv.remove('-p')
        global checkParseErrors
        checkParseErrors = False
    buildTestSuite()
    try:
        unittest.main()
    except SystemExit:
	    pass

if __name__ == "__main__":
    print sys.argv
    main()

@@ -1827,20 +1827,19 @@
 rrors = 
-Fals
+Tru
 e%0A%0A#XXX 
@@ -3958,11 +3958,8 @@
  %25i 
-%25s 
 %25s%22%25
@@ -3971,16 +3971,17 @@
 , col, %0A
+ 
         
@@ -4035,26 +4035,72 @@
 rorcode%5D
-,
  
+%25 datavars if isinstance(datavars, dict) else (
 datavars
 ) for%0A  
@@ -4091,16 +4091,18 @@
 datavars
+,)
 ) for%0A  

Check parse errors.