#ifndef XPATHIC_MATCH_TEST_HPP #define XPATHIC_MATCH_TEST_HPP #include "../CppUnit/framework/TestCase.h" #include "../CppUnit/framework/TestSuite.h" #include "../CppUnit/framework/TestCaller.h" #include #include #include template class TestIdFunction : public Arabica::XPath::XPathFunction { public: TestIdFunction(const std::vector >& args) : Arabica::XPath::XPathFunction(1, 1, args) { } virtual Arabica::XPath::ValueType type() const { return Arabica::XPath::STRING; } virtual Arabica::XPath::XPathValue_impl* evaluate(const Arabica::DOM::Node& context, const Arabica::XPath::ExecutionContext& /* executionContext */) const { string_type name = string_adaptor::construct_from_utf8("test-"); string_adaptor::append(name, context.getLocalName()); return new Arabica::XPath::StringValue(name); } // evaluate }; // TestIdFunction template class TestKeyFunction : public Arabica::XPath::XPathFunction { public: TestKeyFunction(const std::vector >& args) : Arabica::XPath::XPathFunction(2, 2, args) { } virtual Arabica::XPath::ValueType type() const { return Arabica::XPath::STRING; } virtual Arabica::XPath::XPathValue_impl* evaluate(const Arabica::DOM::Node& context, const Arabica::XPath::ExecutionContext& /* executionContext */) const { string_type name = string_adaptor::construct_from_utf8("test-"); string_adaptor::append(name, context.getLocalName()); return new Arabica::XPath::StringValue(name); } // evaluate }; // TestKeyFunction template class TestIdKeyFunctionResolver : public Arabica::XPath::NullFunctionResolver { //typedef string_adaptorstring_adaptor; public: virtual Arabica::XPath::XPathFunction* resolveFunction( const string_type& /* namespace_uri */, const string_type& name, const std::vector >& argExprs) const { if(name == string_adaptor::construct_from_utf8("id")) return new TestIdFunction(argExprs); if(name == string_adaptor::construct_from_utf8("key")) return new TestKeyFunction(argExprs); return 0; } // resolveFunction }; // class TestFunctionResolver template class MatchTest : public TestCase { Arabica::XPath::XPath parser; TestIdKeyFunctionResolver tfr; typedef string_adaptor SA; public: MatchTest(std::string name) : TestCase(name) { } // MatchTest void setUp() { parser.setFunctionResolver(tfr); } // setUp void testParse() { using namespace Arabica::XPath; assertTrue(compileThis("@hello")); assertTrue(compileThis("/")); assertTrue(compileThis("/element")); assertTrue(compileThis("//element")); assertTrue(compileThis("node()")); assertTrue(compileThis("text()")); assertTrue(compileThis("element")); assertTrue(compileThis("element/child/child")); assertTrue(compileThis("element/child[@id='1']/child")); assertTrue(compileThis("element/child[@id='1']/child[@id='1']")); assertTrue(compileThis("element/child::child[@id='1']/child::child")); assertTrue(compileThis("element/child::child[@id='1']/child::child[@id='1']")); assertTrue(compileThis("/element/child/child")); assertTrue(compileThis("/element/child[@id='1']/child")); assertTrue(compileThis("//element/child/child")); assertTrue(compileThis("/element//child/child")); assertTrue(compileThis("//element//child/child")); assertTrue(compileThis("//element//child//child")); assertTrue(compileThis("element[@ref]")); assertTrue(compileThis("hello")); assertTrue(compileThis("doc")); assertTrue(compileThis("child::hello")); assertTrue(compileThis("attribute::hello")); assertTrue(compileThis("hello[@ref]")); assertTrue(compileThis("@hello[../poop]")); assertTrue(compileThis("child::hello[../poop]")); assertTrue(compileThis("attribute::hello[../poop]")); assertTrue(compileThis("element/child")); assertTrue(compileThis("/element/child")); assertTrue(compileThis("//element/child")); assertTrue(compileThis("/element//child")); assertTrue(compileThis("//element//child")); assertTrue(compileThis("element/child::child")); assertTrue(compileThis("child::element/@child")); assertTrue(compileThis("/child::element/child::child")); assertTrue(compileThis("/child::element/@child")); assertTrue(compileThis("/child::element/attribute::child")); assertTrue(compileThis("//element/child::child")); assertTrue(compileThis("/child::element//child::child")); assertTrue(compileThis("//child::element//child::child")); assertTrue(compileThis("node()|@*")); assertTrue(compileThis("node()|attribute::*")); assertTrue(compileThis("element[@type]|element[complexType]|attribute")); assertTrue(compileThis("node()|/")); assertTrue(compileThis("/|node()")); assertTrue(compileThis("node|/")); assertTrue(compileThis("/|node")); assertTrue(compileThis("node|/|//charlie")); assertTrue(compileThis("//charlie|/|node")); assertTrue(compileThis("/|*")); assertTrue(compileThis("*|/")); assertTrue(compileThis("* | /")); assertTrue(compileThis(" / | * ")); assertTrue(compileThis(" / | * ")); } // testParse void testParseFails() { using namespace Arabica::XPath; assertTrue(dontCompileThis("boolean(../hello)")); assertTrue(dontCompileThis("../hello")); assertTrue(dontCompileThis("following-sibling::hello")); assertTrue(dontCompileThis("descendant::hello")); assertTrue(dontCompileThis("ancestor-or-self::hello")); assertTrue(dontCompileThis("///")); assertTrue(dontCompileThis("test///test")); assertTrue(dontCompileThis("descendant-or-self::element")); assertTrue(dontCompileThis("//element/following-sibling::trousers")); } // testParseFails void testEvaluateDocMatch() { Arabica::DOM::Document doc = parseXML("hello"); assertTrue(applyMatch("/", doc)); assertFalse(applyMatch("node()", doc)); assertFalse(applyMatch("*", doc)); assertFalse(applyMatch("@charlie", doc)); } // testEvaluateDocMatch void testDocElementMatch() { Arabica::DOM::Document doc = parseXML("hello"); assertTrue(applyMatch("doc", doc.getFirstChild())); assertTrue(applyMatch("doc[para]", doc.getFirstChild())); assertTrue(applyMatch("*", doc.getFirstChild())); assertTrue(applyMatch("node()", doc.getFirstChild())); assertTrue(applyMatch("/doc", doc.getFirstChild())); assertTrue(applyMatch("//doc", doc.getFirstChild())); assertTrue(applyMatches("comment()|doc", doc.getFirstChild())); assertTrue(applyMatches("comment()|/doc", doc.getFirstChild())); assertTrue(applyMatches("comment()|//doc", doc.getFirstChild())); assertTrue(applyMatches("processing-instruction()|comment()|doc", doc.getFirstChild())); assertTrue(applyMatches("processing-instruction()|comment()|/doc", doc.getFirstChild())); assertTrue(applyMatches("processing-instruction()|comment()|//doc", doc.getFirstChild())); assertTrue(applyMatches("processing-instruction()|doc|comment()", doc.getFirstChild())); assertTrue(applyMatches("processing-instruction()|/doc|comment()", doc.getFirstChild())); assertTrue(applyMatches("processing-instruction()|//doc|comment()", doc.getFirstChild())); assertTrue(applyMatches("doc|processing-instruction()|comment()", doc.getFirstChild())); assertTrue(applyMatches("/doc|processing-instruction()|comment()", doc.getFirstChild())); assertTrue(applyMatches("//doc|processing-instruction()|comment()", doc.getFirstChild())); } // testDocElementMatch void testDocElementNotMatch() { Arabica::DOM::Document doc = parseXML("hello"); assertFalse(applyMatch("para", doc.getFirstChild())); assertFalse(applyMatch("@*", doc.getFirstChild())); assertFalse(applyMatch("text()", doc.getFirstChild())); assertFalse(applyMatch("comment()", doc.getFirstChild())); assertFalse(applyMatch("processing-instruction()", doc.getFirstChild())); assertFalse(applyMatch("//para", doc.getFirstChild())); assertFalse(applyMatch("/para", doc.getFirstChild())); assertFalse(applyMatches("/para|text()", doc.getFirstChild())); assertFalse(applyMatches("/para|@*", doc.getFirstChild())); assertFalse(applyMatches("/para|poopsicle|comment()", doc.getFirstChild())); assertFalse(applyMatch("/", doc.getFirstChild())); } // testDocElementNotMatch void testAttributeMatch() { Arabica::DOM::Document doc = parseXML("hello"); Arabica::DOM::Node a1 = doc.getFirstChild().getFirstChild().getAttributes().getNamedItem(SA::construct_from_utf8("id")); Arabica::DOM::Node a2 = doc.getFirstChild().getFirstChild().getAttributes().getNamedItem(SA::construct_from_utf8("name")); assertTrue(applyMatch("@id", a1)); assertTrue(applyMatch("@*", a1)); assertTrue(applyMatch("para/@id", a1)); assertTrue(applyMatch("node()/@id", a1)); assertTrue(applyMatch("doc//@*", a1)); assertTrue(applyMatch("//@*", a1)); assertFalse(applyMatch("@id", a2)); assertTrue(applyMatch("@*", a2)); assertFalse(applyMatch("para/@id", a2)); assertTrue(applyMatch("doc/para/@*", a2)); assertFalse(applyMatch("@id[../para]", a2)); assertFalse(applyMatch("node()", a1)); assertFalse(applyMatch("*", a1)); assertFalse(applyMatch("pod/para/@*", a1)); assertFalse(applyMatch("/", a1)); assertFalse(applyMatch("/", a2)); assertFalse(applyMatches("/|comment()", a2)); assertTrue(applyMatches("@id|@name", a1)); assertTrue(applyMatches("@id|@name", a2)); assertTrue(applyMatches("@name|@id", a1)); assertTrue(applyMatches("@name|@id", a2)); } // testAttributeMatch void testCommentMatch() { Arabica::DOM::Document doc = parseXML("hello"); Arabica::DOM::Node comment = doc.getFirstChild().getLastChild(); assertFalse(applyMatch("comment()", doc)); assertFalse(applyMatch("comment()", doc.getFirstChild())); assertFalse(applyMatch("comment()", doc.getFirstChild().getFirstChild())); assertTrue(applyMatch("comment()", comment)); } // testCommentMatch void testProcessingInstructionMatch() { Arabica::DOM::Document doc = parseXML("hello"); Arabica::DOM::Node pi = doc.getFirstChild().getLastChild(); assertFalse(applyMatch("processing-instruction()", doc)); assertFalse(applyMatch("processing-instruction()", doc.getFirstChild())); assertFalse(applyMatch("processing-instruction()", doc.getFirstChild().getFirstChild())); assertTrue(applyMatch("processing-instruction()", pi)); assertFalse(applyMatch("processing-instruction('target')", doc)); assertFalse(applyMatch("processing-instruction('target')", doc.getFirstChild())); assertFalse(applyMatch("processing-instruction('target')", doc.getFirstChild().getFirstChild())); assertTrue(applyMatch("processing-instruction('target')", pi)); assertFalse(applyMatch("processing-instruction('budget')", doc)); assertFalse(applyMatch("processing-instruction('budget')", doc.getFirstChild())); assertFalse(applyMatch("processing-instruction('budget')", doc.getFirstChild().getFirstChild())); assertFalse(applyMatch("processing-instruction('budget')", pi)); } // testProcessingInstructionMatch void testNameTestMatch() { Arabica::XPath::StandardNamespaceContext nsContext; nsContext.addNamespaceDeclaration(SA::construct_from_utf8("bang"), SA::construct_from_utf8("bang")); parser.setNamespaceContext(nsContext); Arabica::DOM::Document doc = parseXML("hello"); assertFalse(applyMatch("bang:*", doc)); assertTrue(applyMatch("bang:*", doc.getFirstChild())); assertTrue(applyMatch("bang:*", doc.getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:*", doc.getFirstChild().getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:para", doc)); assertFalse(applyMatch("bang:para", doc.getFirstChild())); assertTrue(applyMatch("bang:para", doc.getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:para", doc.getFirstChild().getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:doc", doc)); assertTrue(applyMatch("bang:doc", doc.getFirstChild())); assertFalse(applyMatch("bang:doc", doc.getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:doc", doc.getFirstChild().getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:*/bang:para", doc)); assertFalse(applyMatch("bang:*/bang:para", doc.getFirstChild())); assertTrue(applyMatch("bang:*/bang:para", doc.getFirstChild().getFirstChild())); assertFalse(applyMatch("bang:*/bang:para", doc.getFirstChild().getFirstChild().getFirstChild())); parser.resetNamespaceContext(); } // testNameTestMatch void testPriority() { using namespace Arabica::XPath; Arabica::XPath::StandardNamespaceContext nsContext; nsContext.addNamespaceDeclaration(SA::construct_from_utf8("bang"), SA::construct_from_utf8("bang")); parser.setNamespaceContext(nsContext); assertEquals(0.5, matchPriority("/"), 0); assertEquals(0.5, matchPriority("/element"), 0); assertEquals(0.5, matchPriority("//element"), 0); assertEquals(-0.5, matchPriority("node()"), 0); assertEquals(-0.5, matchPriority("child::node()"), 0); assertEquals(-0.5, matchPriority("attribute::node()"), 0); assertEquals(-0.5, matchPriority("@node()"), 0); assertEquals(-0.5, matchPriority("text()"), 0); assertEquals(-0.5, matchPriority("child::text()"), 0); assertEquals(-0.5, matchPriority("comment()"), 0); assertEquals(-0.5, matchPriority("child::comment()"), 0); assertEquals(-0.5, matchPriority("processing-instruction()"), 0); assertEquals(-0.5, matchPriority("child::processing-instruction()"), 0); assertEquals(-0.5, matchPriority("*"), 0); assertEquals(-0.5, matchPriority("child::*"), 0); assertEquals(-0.5, matchPriority("attribute::*"), 0); assertEquals(-0.5, matchPriority("@*"), 0); assertEquals(-0.25, matchPriority("bang:*"), 0); assertEquals(-0.25, matchPriority("child::bang:*"), 0); assertEquals(-0.25, matchPriority("attribute::bang:*"), 0); assertEquals(-0.25, matchPriority("@bang:*"), 0); assertEquals(0, matchPriority("processing-instruction('noon')"), 0); assertEquals(0, matchPriority("element"), 0); assertEquals(0, matchPriority("hello"), 0); assertEquals(0, matchPriority("doc"), 0); assertEquals(0, matchPriority("@hello"), 0); assertEquals(0, matchPriority("child::hello"), 0); assertEquals(0, matchPriority("attribute::hello"), 0); assertEquals(0.5, matchPriority("*[2]"), 0); assertEquals(0.5, matchPriority("element/child/child"), 0); assertEquals(0.5, matchPriority("element/child[@id='1']/child"), 0); assertEquals(0.5, matchPriority("element/child[@id='1']/child[@id='1']"), 0); assertEquals(0.5, matchPriority("element/child::child[@id='1']/child::child"), 0); assertEquals(0.5, matchPriority("element/child::child[@id='1']/child::child[@id='1']"), 0); assertEquals(0.5, matchPriority("/element/child/child"), 0); assertEquals(0.5, matchPriority("/element/child[@id='1']/child"), 0); assertEquals(0.5, matchPriority("//element/child/child"), 0); assertEquals(0.5, matchPriority("/element//child/child"), 0); assertEquals(0.5, matchPriority("//element//child/child"), 0); assertEquals(0.5, matchPriority("//element//child//child"), 0); assertEquals(0.5, matchPriority("/bang:*"), 0); assertEquals(0.5, matchPriority("//bang:*"), 0); assertEquals(0.5, matchPriority("element[@ref]"), 0); assertEquals(0.5, matchPriority("hello[@ref]"), 0); assertEquals(0.5, matchPriority("@hello[../poop]"), 0); assertEquals(0.5, matchPriority("child::hello[../poop]"), 0); assertEquals(0.5, matchPriority("attribute::hello[../poop]"), 0); assertEquals(0.5, matchPriority("element/child"), 0); assertEquals(0.5, matchPriority("bang:element/bang:child"), 0); assertEquals(0.5, matchPriority("bang:*/bang:child"), 0); assertEquals(0.5, matchPriority("/element/child"), 0); assertEquals(0.5, matchPriority("//element/child"), 0); assertEquals(0.5, matchPriority("/element//child"), 0); assertEquals(0.5, matchPriority("//element//child"), 0); assertEquals(0.5, matchPriority("element/child::child"), 0); assertEquals(0.5, matchPriority("child::element/@child"), 0); assertEquals(0.5, matchPriority("/child::element/child::child"), 0); assertEquals(0.5, matchPriority("/child::element/@child"), 0); assertEquals(0.5, matchPriority("/child::element/attribute::child"), 0); assertEquals(0.5, matchPriority("//element/child::child"), 0); assertEquals(0.5, matchPriority("/child::element//child::child"), 0); assertEquals(0.5, matchPriority("//child::element//child::child"), 0); parser.resetNamespaceContext(); } // testPriority void testPriority2() { assertEquals(-0.5, matchPriority("node()"), 0); } // testPriority2 void testPriority3() { assertEquals(-0.5, matchPriority("*"), 0); } // testPriority3 void testPriority4() { assertEquals(0, matchPriority("foo"), 0); } // testPriority4 void testIdKey() { assertTrue(compileThis("id('nob')")); assertTrue(compileThis("id( 'nob' )")); assertTrue(dontCompileThis("id()")); assertTrue(dontCompileThis("id(nob)")); assertTrue(dontCompileThis("id('nob','c')")); } // testIdKey void testIdKey2() { assertTrue(compileThis("key('a','b')")); assertTrue(compileThis("key('a', 'b')")); // assertTrue(compileThis("key('a','b')/woot")); assertTrue(dontCompileThis("key()")); assertTrue(dontCompileThis("key(a)")); assertTrue(dontCompileThis("key('a', 'b', 'c')")); assertTrue(dontCompileThis("key(a, 'b')")); } // testIdKey2 bool dontCompileThis(const char* path) { try { compileMatches(path); return false; } catch(const Arabica::XPath::SyntaxException&) { } return true; } // dontCompileThis bool compileThis(const char* path) { try { compileMatches(path); return true; } catch(const Arabica::XPath::UnsupportedException&) { } catch(const Arabica::XPath::SyntaxException& ex) { std::cerr << ex.what() << std::endl; } return false; } // compileThis std::vector > matches; Arabica::XPath::MatchExpr compileMatch(const char* match) { //std::cout << "----\n" << match << std::endl; compileMatches(match); assertEquals(1, matches.size()); return matches[0]; } // compileMatch double matchPriority(const char* match) { compileMatches(match); assertEquals(1, matches.size()); return matches[0].priority(); } // matchPriority std::vector >& compileMatches(const char* match) { matches = parser.compile_match(SA::construct_from_utf8(match)); return matches; } // compileMatches bool applyMatches(const char* match, const Arabica::DOM::Node& node) { compileMatches(match); Arabica::XPath::ExecutionContext context; for(unsigned int i = 0; i < matches.size(); ++i) if(matches[i].evaluate(node, context)) return true; return false; } // applyMatches bool applyMatch(const char* match, const Arabica::DOM::Node& node) { Arabica::XPath::ExecutionContext context; return compileMatch(match).evaluate(node, context); } // applyMatch Arabica::DOM::Document parseXML(const char* match) { std::stringstream ss; ss << match; Arabica::SAX::InputSource is(ss); Arabica::SAX::CatchErrorHandler eh; Arabica::SAX2DOM::Parser parser; parser.setErrorHandler(eh); parser.parse(is); if(eh.errorsReported()) throw std::runtime_error(eh.errors()); return parser.getDocument(); } // parse }; // class MatchTest template TestSuite* MatchTest_suite() { TestSuite *suiteOfTests = new TestSuite; suiteOfTests->addTest(new TestCaller >("testParse", &MatchTest::testParse)); suiteOfTests->addTest(new TestCaller >("testParseFail", &MatchTest::testParseFails)); suiteOfTests->addTest(new TestCaller >("testEvaluateDocMatch", &MatchTest::testEvaluateDocMatch)); suiteOfTests->addTest(new TestCaller >("testDocElementMatch", &MatchTest::testDocElementMatch)); suiteOfTests->addTest(new TestCaller >("testDocElementNotMatch", &MatchTest::testDocElementNotMatch)); suiteOfTests->addTest(new TestCaller >("testAttributeMatch", &MatchTest::testAttributeMatch)); suiteOfTests->addTest(new TestCaller >("testCommentMatch", &MatchTest::testCommentMatch)); suiteOfTests->addTest(new TestCaller >("testProcessingInstructionMatch", &MatchTest::testProcessingInstructionMatch)); suiteOfTests->addTest(new TestCaller >("testNameTestMatch", &MatchTest::testNameTestMatch)); suiteOfTests->addTest(new TestCaller >("testPriority", &MatchTest::testPriority)); suiteOfTests->addTest(new TestCaller >("testPriority2", &MatchTest::testPriority2)); suiteOfTests->addTest(new TestCaller >("testPriority3", &MatchTest::testPriority3)); suiteOfTests->addTest(new TestCaller >("testPriority4", &MatchTest::testPriority4)); suiteOfTests->addTest(new TestCaller >("testIdKey", &MatchTest::testIdKey)); suiteOfTests->addTest(new TestCaller >("testIdKey2", &MatchTest::testIdKey2)); return suiteOfTests; } // MatchTest_suite #endif