import 'package:test/test.dart'; import 'package:xml/xml.dart'; import 'utils/assertions.dart'; import 'utils/matchers.dart'; const nameStartChars = { 'A', 'Z', '_', 'a', 'z', '\u{c0}', '\u{d6}', '\u{d8}', '\u{f6}', '\u{f8}', '\u{2ff}', '\u{370}', '\u{37d}', '\u{37f}', '\u{1fff}', '\u{200c}', '\u{200d}', '\u{2070}', '\u{218f}', '\u{2c00}', '\u{2fef}', '\u{3001}', '\u{d7ff}', '\u{f900}', '\u{fdcf}', '\u{fdf0}', '\u{fffd}', '\u{10000}', '\u{effff}', }; const nameChars = { ...nameStartChars, '-', '.', '0', '9', '\u{B7}', '\u{300}', '\u{36F}', '\u{203F}', '\u{2040}', }; void main() { group('document', () { test('cdata', () { assertDocumentParseInvariants(''); }); test('cdata with xml', () { assertDocumentParseInvariants(']]>'); }); test('comment', () { assertDocumentParseInvariants( '' '', ); }); test('comment with xml', () { assertDocumentParseInvariants( '' '', ); }); test('declaration', () { assertDocumentParseInvariants(''); }); test('declaration with attribute', () { assertDocumentParseInvariants(''); }); test('doctype (system)', () { assertDocumentParseInvariants( '' '', ); }); test('doctype (public)', () { assertDocumentParseInvariants( '' '', ); }); test('doctype (empty)', () { assertDocumentParseInvariants( '\n' '', ); }); test('doctype (comment)', () { assertDocumentParseInvariants( '\n' ']>\n' '', ); }); test('doctype (processing)', () { assertDocumentParseInvariants( '\n' ']>\n' '', ); }); test('doctype (element type declarations)', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (element content models)', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (element mixed content)', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (attribute-list)', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (internal entity)', () { assertDocumentParseInvariants( '\n' ']>\n' '', ); }); test('doctype (internal entity, included)', () { assertDocumentParseInvariants( '\n' ' \n' ']>\n' '', ); }); test('doctype (internal entity, replacement text)', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (entity reference)', () { assertDocumentParseInvariants( '\n' ']>\n' '', ); }); test('doctype (external entities )', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (notation)', () { assertDocumentParseInvariants( '\n' ' \n' ' \n' ']>\n' '', ); }); test('doctype (ambiguous)', () { assertDocumentParseInvariants( '] -->\n' ' ]" ?>\n' ' ]" \'[]\'>\n' ' ]">\n' ' ]\'>\n' ' ]">\n' ' ]\'>\n' ' \n' ' \n' ' \n' ' \n' ' ]" #REQUIRED>\n' ' ]\' #IMPLIED>\n' ']>\n' '', ); }); test('element', () { assertDocumentParseInvariants(''); assertDocumentParseInvariants(''); assertDocumentParseInvariants(''); assertDocumentParseInvariants(''); }); test('element with namespace', () { assertDocumentParseInvariants(''); }); test('element with closing', () { assertDocumentParseInvariants(''); }); test('element with double quote attribute', () { assertDocumentParseInvariants(''); assertDocumentParseInvariants(''); }); test('element with single quote attribute', () { assertDocumentParseInvariants(""); assertDocumentParseInvariants(""); }); test('element with attribute without quotes', () { assertDocumentParseInvariants(''); assertDocumentParseInvariants(''); }); test('element with attribute without value', () { assertDocumentParseInvariants(''); assertDocumentParseInvariants(''); }); test('element with special initial char', () { for (final char in nameStartChars) { final qualified = char; final closed = XmlDocument.parse('<$qualified />'); expect(closed.rootElement.name.qualified, qualified); final openClosed = XmlDocument.parse('<$qualified>'); expect(openClosed.rootElement.name.qualified, qualified); } }); test('element with special continuation char', () { for (final char in nameChars) { final qualified = 'x$char'; final closed = XmlDocument.parse('<$qualified />'); expect(closed.rootElement.name.qualified, qualified); final openClosed = XmlDocument.parse('<$qualified>'); expect(openClosed.rootElement.name.qualified, qualified); } }); test('element with attribute with special initial char', () { for (final char in nameStartChars) { final qualified = char; final root = XmlDocument.parse(''); expect(root.rootElement.attributes.first.name.qualified, qualified); expect(root.rootElement.getAttribute(qualified), 'ok'); } }); test('element with attribute with with special continuation char', () { for (final char in nameChars) { final qualified = 'x$char'; final root = XmlDocument.parse(''); expect(root.rootElement.attributes.first.name.qualified, qualified); expect(root.rootElement.getAttribute(qualified), 'ok'); } }); test('processing instruction', () { assertDocumentParseInvariants(''); }); test('processing instruction with attribute', () { assertDocumentParseInvariants(''); }); test('document with comments', () { assertDocumentParseInvariants( '' '' '' '', ); assertDocumentParseInvariants( '' '' '' '', ); assertDocumentParseInvariants( '' '' '' '', ); }); group('validation errors', () { test('empty', () { expect( () => XmlDocument.parse(''), throwsA( isXmlParserException( message: 'Expected a single root element', position: 0, ), ), ); }); test('whitespace', () { expect( () => XmlDocument.parse(' '), throwsA( isXmlParserException( message: 'Expected a single root element', position: 2, ), ), ); }); test('repeated declaration', () { expect( () => XmlDocument.parse( '' '' '', ), throwsA( isXmlParserException( message: 'Expected at most one XML declaration', position: 21, ), ), ); }); test('repeated doctype', () { expect( () => XmlDocument.parse( '' '' '', ), throwsA( isXmlParserException( message: 'Expected at most one doctype declaration', position: 37, ), ), ); }); test('unexpected declaration', () { expect( () => XmlDocument.parse( '' '', ), throwsA( isXmlParserException( message: 'Unexpected XML declaration', position: 37, ), ), ); expect( () => XmlDocument.parse( '' '', ), throwsA( isXmlParserException( message: 'Unexpected XML declaration', position: 8, ), ), ); }); test('unexpected doctype', () { expect( () => XmlDocument.parse( '' '', ), throwsA( isXmlParserException( message: 'Unexpected doctype declaration', position: 8, ), ), ); }); test('unexpected root element', () { expect( () => XmlDocument.parse(''), throwsA( isXmlParserException( message: 'Unexpected root element', position: 9, ), ), ); }); }); group('parse errors', () { test('nesting', () { expect( () => XmlDocument.parse(''), throwsA( isXmlTagException( message: 'Expected , but found ', position: 5, ), ), ); expect( () => XmlDocument.parse(''), throwsA(isXmlTagException(message: 'Missing ', position: 5)), ); expect( () => XmlDocument.parse(''), throwsA(isXmlTagException(message: 'Unexpected ', position: 0)), ); }); test('element', () { expect( () => XmlDocument.parse('<'), throwsA(isXmlParserException(message: 'name expected', position: 1)), ); expect( () => XmlDocument.parse('" expected', position: 5)), ); expect( () => XmlDocument.parse('" expected', position: 9)), ); expect( () => XmlDocument.parse('" expected', position: 9)), ); }); test('comment', () { expect( () => XmlDocument.parse('" expected', position: 4)), ); expect( () => XmlDocument.parse('" expected', position: 4)), ); }); test('cdata', () { expect( () => XmlDocument.parse('" expected', position: 9)), ); expect( () => XmlDocument.parse('" expected', position: 9)), ); }); test('doctype', () { expect( () => XmlDocument.parse(' XmlDocument.parse('" expected', position: 14)), ); expect( () => XmlDocument.parse('" expected', position: 15)), ); }); test('declaration', () { expect( () => XmlDocument.parse(' XmlDocument.parse('" expected', position: 5)), ); expect( () => XmlDocument.parse('" expected', position: 13)), ); expect( () => XmlDocument.parse('" expected', position: 13)), ); expect( () => XmlDocument.parse('" expected', position: 13)), ); }); test('processing', () { expect( () => XmlDocument.parse(' XmlDocument.parse('" expected', position: 12)), ); expect( () => XmlDocument.parse('" expected', position: 12)), ); }); }); }); group('fragment', () { test('cdata', () { assertFragmentParseInvariants(''); }); test('cdata with xml', () { assertFragmentParseInvariants(']]>'); }); test('comment', () { assertFragmentParseInvariants(''); }); test('comment with xml', () { assertFragmentParseInvariants(''); }); test('declaration', () { assertFragmentParseInvariants(''); }); test('declaration with attribute', () { assertFragmentParseInvariants(''); }); test('doctype (system)', () { assertFragmentParseInvariants( '', ); }); test('doctype (public)', () { assertFragmentParseInvariants( '', ); }); test('doctype (subset)', () { assertFragmentParseInvariants( '' ' ' ' ' ']>', ); }); test('doctype (combined)', () { assertFragmentParseInvariants( '' ' ' ' ' ']>', ); }); test('element', () { assertFragmentParseInvariants(''); assertFragmentParseInvariants(''); assertFragmentParseInvariants(''); assertFragmentParseInvariants(''); }); test('element with namespace', () { assertFragmentParseInvariants(''); }); test('element with closing', () { assertFragmentParseInvariants(''); }); test('element double quote attribute', () { assertFragmentParseInvariants(''); }); test('element single quote attribute', () { assertFragmentParseInvariants(''); }); test('processing instruction', () { assertFragmentParseInvariants(''); }); test('processing instruction with attribute', () { assertFragmentParseInvariants(''); }); test('text', () { assertFragmentParseInvariants( 'I have a heart I swear I do, ' 'Just not baby when it comes to you.', ); }); test('empty', () { assertFragmentParseInvariants(''); assertFragmentParseInvariants(' '); assertFragmentParseInvariants('\t'); assertFragmentParseInvariants('\n'); assertFragmentParseInvariants(' '); }); group('parse errors', () { test('nesting', () { expect( () => XmlDocumentFragment.parse(''), throwsA( isXmlTagException( message: 'Expected , but found ', position: 5, ), ), ); expect( () => XmlDocumentFragment.parse(''), throwsA(isXmlTagException(message: 'Missing ', position: 5)), ); expect( () => XmlDocumentFragment.parse(''), throwsA(isXmlTagException(message: 'Unexpected ', position: 0)), ); }); test('element', () { expect( () => XmlDocumentFragment.parse('<'), throwsA(isXmlParserException(message: 'name expected', position: 1)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 5)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 9)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 9)), ); }); test('comment', () { expect( () => XmlDocumentFragment.parse('" expected', position: 4)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 4)), ); }); test('cdata', () { expect( () => XmlDocumentFragment.parse('" expected', position: 9)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 9)), ); }); test('doctype', () { expect( () => XmlDocumentFragment.parse(' XmlDocumentFragment.parse('" expected', position: 14)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 15)), ); }); test('declaration', () { expect( () => XmlDocumentFragment.parse(' XmlDocumentFragment.parse('" expected', position: 5)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 13)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 13)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 13)), ); }); test('processing', () { expect( () => XmlDocumentFragment.parse(' XmlDocumentFragment.parse('" expected', position: 12)), ); expect( () => XmlDocumentFragment.parse('" expected', position: 12)), ); }); }); }); }