import Testing @testable import PureXML /// The independent content-model membership oracle that adjudicates instance- /// differential disagreements. Pins the cases that matter, including the libxml2 /// leniency case the oracle exists to catch: a model libxml2 wrongly accepts an /// invalid instance for, which the oracle (and PureXML) correctly reject. @Suite("Content-model membership oracle") struct ContentModelOracleTests { private func tokens(_ string: String) -> [String] { string.map(String.init) } /// `( (a*|c*){1,3} b? ){1,3}`, the model from the DIFF_SEED=13371337 case. private var nestedNullable: ContentModel { let choice = ContentModel.occurs( .choice([.occurs(.element("a"), 0, nil), .occurs(.sequence([.occurs(.element("c"), 0, nil)]), 1, nil)]), 1, 3, ) return .occurs(.sequence([choice, .occurs(.element("b"), 0, 1)]), 1, 3) } @Test("the nested nullable-repetition model matches exactly its language") func test_nestedNullable() { // 'bbabca' is the instance libxml2 LENIENTLY accepts but is NOT in the // language: three b's anchor three (choice,b?) segments, leaving the // trailing 'ca' with no fourth segment. The oracle (like PureXML) rejects it. #expect(!ContentModelOracle.matches(nestedNullable, tokens("bbabca"))) #expect(!ContentModelOracle.matches(nestedNullable, tokens("bbabcab"))) // Members, cross-checked against an external regex engine. #expect(ContentModelOracle.matches(nestedNullable, tokens("bbab"))) #expect(ContentModelOracle.matches(nestedNullable, tokens("aca"))) #expect(ContentModelOracle.matches(nestedNullable, tokens("abcabc"))) #expect(ContentModelOracle.matches(nestedNullable, tokens("b"))) #expect(ContentModelOracle.matches(nestedNullable, tokens(""))) // one empty outer iteration } @Test("sequence, choice, occurrence, and wildcard match as specified") func test_basics() { let seq = ContentModel.sequence([.element("a"), .occurs(.element("b"), 0, 1), .element("c")]) #expect(ContentModelOracle.matches(seq, tokens("abc"))) #expect(ContentModelOracle.matches(seq, tokens("ac"))) #expect(!ContentModelOracle.matches(seq, tokens("abbc"))) let choice = ContentModel.occurs(.choice([.element("a"), .element("c")]), 1, 3) #expect(ContentModelOracle.matches(choice, tokens("aca"))) #expect(!ContentModelOracle.matches(choice, tokens("acac"))) let wildcard = ContentModel.occurs(.any, 0, nil) #expect(ContentModelOracle.matches(wildcard, tokens("abc"))) } @Test("an all group matches its members in any order, requiring the non-optional ones") func test_allGroup() { let all = ContentModel.all([.occurs(.element("a"), 1, 1), .occurs(.element("b"), 0, 1)]) #expect(ContentModelOracle.matches(all, tokens("a"))) #expect(ContentModelOracle.matches(all, tokens("ab"))) #expect(ContentModelOracle.matches(all, tokens("ba"))) #expect(!ContentModelOracle.matches(all, tokens("b"))) // required 'a' absent #expect(!ContentModelOracle.matches(all, tokens("aa"))) // 'a' is max 1 } }