#ifdef _MSC_VER #pragma warning(disable: 4250) #endif #include #include #include #include #include "../CppUnit/TestRunner.hpp" #include "../CppUnit/framework/Test.h" #include "../CppUnit/framework/TestCase.h" #include "../CppUnit/framework/TestSuite.h" #include "../CppUnit/framework/Test.h" #include "../CppUnit/framework/TestCase.h" #include "../CppUnit/framework/TestSuite.h" #include //#include //#include //#include //#include #include #include #include class Expected; template class Loader { public: Loader(); ~Loader(); TestSuite* XSLTTest_suite(const std::string& path); TestSuite* ArabicaTest_suite(const std::string& path); private: Expected* expected_fails_; std::map > catalogs_; Arabica::DOM::Document loadCatalog(const std::string& catalog_filename); TestSuite* suite(const std::string& path, const std::string& catalog_filename); }; #ifdef ARABICA_WINDOWS const std::string PATH_PREFIX="../tests/XSLT/testsuite/TESTS/"; const std::string SEPERATOR = "/"; #else #include "test_path.hpp" const std::string PATH_PREFIX=test_path; const std::string SEPERATOR = "/"; #endif template Arabica::DOM::Document buildDOM(const std::string& filename) { Arabica::SAX::InputSource is(string_adaptor::construct(filename)); Arabica::SAX2DOM::Parser parser; parser.parse(is); Arabica::DOM::Document d = parser.getDocument(); if(d != 0) d.normalize(); return d; } // buildDOM template Arabica::DOM::Document buildDOMFromString(const std::string& xml) { std::stringstream ss; ss << xml; Arabica::SAX::InputSource is(ss); Arabica::SAX2DOM::Parser parser; parser.parse(is); Arabica::DOM::Document d = parser.getDocument(); if(d != 0) d.normalize(); return d; } // buildDOMFromString std::string readFile(const std::string& filename) { std::ifstream in(filename.c_str()); std::string file = std::string(std::istreambuf_iterator(in), std::istreambuf_iterator()); if((file[0] == (char)0xff) && (file[1] == (char)0xfe)) // UTF16 BOM { // this is absolutely not a sensible thing to do // in this case, I know it's ok for the test data but in the general case don't even think about it file.erase(0, 2); for(std::string::size_type i = file.length() - 1; i != std::string::npos; i -= 2) file.erase(i, 1); } // if ... if((file[0] == (char)0xef) && (file[1] == (char)0xbb) && (file[2] == (char)0xbf)) // UTF-8 BOM file.erase(0, 3); return file; } // readFile std::string readFragment(const std::string& filename) { std::string frag = readFile(filename); if(frag.find("") + 2); return frag; } // readFragment template Arabica::XPath::NodeSet selectNodes(const char* path, const Arabica::DOM::Node& node) { Arabica::XPath::XPath xpath; return xpath.evaluate(string_adaptor::construct(path), node).asNodeSet(); } // selectNodes template Arabica::DOM::Node selectNode(const char* path, const Arabica::DOM::Node& node) { return selectNodes(path, node)[0]; } // selectNode std::string selectString(const std::string& path, const Arabica::DOM::Node& node) { Arabica::XPath::XPath xpath; return xpath.evaluate_expr(path, node).asString(); } // selectString std::string make_path(const std::string& path, const std::string& filename) { return PATH_PREFIX + path + SEPERATOR + filename; } // make_path class SkipTest : public TestCase { public: SkipTest(const std::string& name, const std::string& reason) : TestCase(name), reason_(reason) { } // SkipTest protected: virtual void runTest() { throw SkipException(reason_); } // runTest private: std::string reason_; }; // class SkipTest template class CompileFailsTest : public TestCase { public: CompileFailsTest(const std::string& name, const std::string& input_xslt, const std::string& reason) : TestCase(name), input_xslt_(input_xslt), reason_(reason) { } // CompileFailsTest protected: virtual void runTest() { Arabica::XSLT::StylesheetCompiler compiler; Arabica::SAX::InputSource source(string_adaptor::construct(input_xslt_)); std::auto_ptr > stylesheet = compiler.compile(source); if(stylesheet.get() != 0) assertImplementation(false, "Expected " + input_xslt_ + " not to compile. But it did :o"); throw SkipException(reason_ + " : " + compiler.error()); } // runTest private: std::string input_xslt_; std::string reason_; }; // CompileFailsTest template class RunFailsTest : public TestCase { public: RunFailsTest(const std::string& name, const std::string& input_xml, const std::string& input_xslt, const std::string& reason) : TestCase(name), input_xml_(input_xml), input_xslt_(input_xslt), reason_(reason) { } // RunFailsTest protected: virtual void runTest() { Arabica::XSLT::StylesheetCompiler compiler; Arabica::SAX::InputSource source(string_adaptor::construct(input_xslt_)); std::auto_ptr > stylesheet = compiler.compile(source); if(stylesheet.get() == 0) assertImplementation(false, "Failed to compile " + input_xslt_ + " : " + compiler.error()); Arabica::XSLT::DOMSink output; stylesheet->set_output(output); std::basic_ostringstream errors; stylesheet->set_error_output(errors); Arabica::DOM::Document document = buildDOM(input_xml_); try { stylesheet->execute(document); } catch(const std::exception& e) { throw SkipException(reason_ + " : " + e.what()); } assertImplementation(false, "Expected " + input_xslt_ + " to fail to execute. But it did :o"); } // runTest private: std::string input_xml_; std::string input_xslt_; std::string reason_; }; // RunFailsTest template class ExecutionErrorTest : public TestCase { public: ExecutionErrorTest(const std::string& name, const std::string& input_xml, const std::string& input_xslt) : TestCase(name), input_xml_(input_xml), input_xslt_(input_xslt) { } // ExecutionErrorTest protected: virtual void runTest() { Arabica::XSLT::StylesheetCompiler compiler; string_type input = string_adaptor::construct(input_xslt_); Arabica::SAX::InputSource source(input); std::auto_ptr > stylesheet = compiler.compile(source); if(stylesheet.get() == 0) return; Arabica::XSLT::DOMSink output; stylesheet->set_output(output); std::basic_ostringstream errors; stylesheet->set_error_output(errors); Arabica::DOM::Document document = buildDOM(input_xml_); try { stylesheet->execute(document); } catch(const std::exception&) { return; } assertImplementation(false, "Marked in catalog.xml as an execution error, but actually ran."); } // runTest private: std::string input_xml_; std::string input_xslt_; }; // ExecutionErrorTest template class CompareAsTextXSLTTest : public TestCase { public: CompareAsTextXSLTTest(const std::string& name, const std::string& input_xml, const std::string& input_xslt, const std::string& output_xml) : TestCase(name), input_xml_(input_xml), input_xslt_(input_xslt), output_xml_(output_xml) { } // CompareAsTextXSLTTest protected: virtual void runTest() { Arabica::XSLT::StylesheetCompiler compiler; Arabica::SAX::InputSource source(string_adaptor::construct(input_xslt_)); std::auto_ptr > stylesheet = compiler.compile(source); if(stylesheet.get() == 0) assertImplementation(false, "Failed to compile " + input_xslt_ + " : " + compiler.error()); std::basic_ostringstream xml_output; Arabica::XSLT::StreamSink output(xml_output); stylesheet->set_output(output); std::basic_ostringstream errors; stylesheet->set_error_output(errors); Arabica::DOM::Document document = buildDOM(input_xml_); try { stylesheet->execute(document); } catch(const std::exception& e) { assertImplementation(false, "Failed to run " + input_xslt_ + " : " + e.what()); } // catch std::string ref = readFile(output_xml_); std::string out = string_adaptor::asStdString(string_adaptor::construct(xml_output.str())); if(ref == out) return; std::string refs = trimXMLDecl(ref); std::string outs = trimXMLDecl(out); if(refs == outs) return; refs = stripWhitespace(refs); outs = stripWhitespace(outs); if(refs == outs) return; assertImplementation(false, "Expected text:\n" + ref + "\nbut got:\n" + out + "\n" + refs + "\nbut got:" + outs + "\n====="); } // runTest private: std::string stripWhitespace(const std::string& str) { std::string s = Arabica::text::normalize_whitespace >(str); std::string::size_type i = s.find("> "); while(i != std::string::npos) { s.erase(i+1, 1); i = s.find("> "); } // while .. i = s.find(" <"); while(i != std::string::npos) { s.erase(i, 1); i = s.find(" <"); } // while .. return s; } // stripWhitespace std::string trimXMLDecl(const std::string& str) { if(str.find("") + 2); } // trimXMLDecl std::string input_xml_; std::string input_xslt_; std::string output_xml_; }; // class CompareAsTextXSLTTest template class CompareAsXMLFragmentXSLTTest : public TestCase { public: CompareAsXMLFragmentXSLTTest(const std::string& name, const std::string& input_xml, const std::string& input_xslt, const std::string& output_xml) : TestCase(name), input_xml_(input_xml), input_xslt_(input_xslt), output_xml_(output_xml) { } // protected: virtual void runTest() { Arabica::XSLT::StylesheetCompiler compiler; Arabica::SAX::InputSource source(string_adaptor::construct(input_xslt_)); std::auto_ptr > stylesheet = compiler.compile(source); if(stylesheet.get() == 0) assertImplementation(false, "Failed to compile " + input_xslt_ + " : " + compiler.error()); Arabica::XSLT::DOMSink output; stylesheet->set_output(output); std::basic_ostringstream errors; stylesheet->set_error_output(errors); Arabica::DOM::Document document = buildDOM(input_xml_); try { stylesheet->execute(document); } catch(const std::exception& e) { assertImplementation(false, "Failed to run " + input_xslt_ + " : " + e.what()); } std::string refStr = "" + readFragment(output_xml_) + ""; Arabica::DOM::Document refDoc = buildDOMFromString(refStr); if(refDoc == 0) assertImplementation(false, "Couldn't read " + output_xml_ + ". Perhaps it isn't well-formed XML?"); Arabica::DOM::DocumentFragment ref = refDoc.createDocumentFragment(); for(Arabica::DOM::Node n = refDoc.getDocumentElement().getFirstChild(); n != 0; n = refDoc.getDocumentElement().getFirstChild()) ref.appendChild(n); Arabica::DOM::Node out = output.node(); out.normalize(); std::string refs = docToString(ref); std::string outs = docToString(out); if(refs == outs) return; stripWhitespace(ref); stripWhitespace(out); std::string refs2 = docToString(ref); std::string outs2 = docToString(out); if(refs2 == outs2) return; assertImplementation(false, "Expected XML frag:\n" + refs + "\nbut got:\n" + outs + "\n=====\n" + refs2 + "\nbut:\n" + outs2 + "\n\n====\n\n"); } // runTest std::string docToString(Arabica::DOM::Node node) { std::basic_ostringstream ss; ss << node; string_type streamed = string_adaptor::construct(ss.str()); streamed = Arabica::text::normalize_whitespace(streamed); return string_adaptor::asStdString(streamed); } // docToString void stripWhitespace(Arabica::DOM::Node doc) { Arabica::XPath::NodeSet textNodes = selectNodes("//text()", doc); for(size_t i = 0; i != textNodes.size(); ++i) { Arabica::DOM::Node t = textNodes[i]; string_type text = t.getNodeValue(); text = Arabica::text::normalize_whitespace(text); size_t index = string_adaptor::find(text, Arabica::text::Unicode::SPACE); while(index != string_adaptor::npos()) { text = string_adaptor::concat(string_adaptor::substr(text, 0, index), string_adaptor::substr(text, index+1)); index = string_adaptor::find(text, Arabica::text::Unicode::SPACE); } t.setNodeValue(text); if(string_adaptor::length(text) == 0) t.getParentNode().removeChild(t); } } // stripWhitespace std::string input_xml_; std::string input_xslt_; std::string output_xml_; }; // class CompareAsXMLFragment template class StandardXSLTTest : public TestCase { public: StandardXSLTTest(const std::string& name, const std::string& input_xml, const std::string& input_xslt, const std::string& output_xml) : TestCase(name), input_xml_(input_xml), input_xslt_(input_xslt), output_xml_(output_xml) { } // StandardXSLTTest protected: virtual void runTest() { Arabica::XSLT::StylesheetCompiler compiler; Arabica::SAX::InputSource source(string_adaptor::construct(input_xslt_)); std::auto_ptr > stylesheet = compiler.compile(source); if(stylesheet.get() == 0) assertImplementation(false, "Failed to compile " + input_xslt_ + " : " + compiler.error()); Arabica::XSLT::DOMSink output; stylesheet->set_output(output); std::basic_ostringstream errors; stylesheet->set_error_output(errors); Arabica::DOM::Document document = buildDOM(input_xml_); if(document == 0) assertImplementation(false, "Couldn't read " + input_xml_ + ". Perhaps it isn't well-formed XML?"); try { stylesheet->execute(document); } catch(const std::exception& e) { assertImplementation(false, "Failed to run " + input_xslt_ + " : " + e.what()); } Arabica::DOM::Document ref = buildDOM(output_xml_); if(ref == 0) assertImplementation(false, "Couldn't read " + output_xml_ + ". Perhaps it isn't well-formed XML?"); Arabica::DOM::Node out = output.node(); out.normalize(); std::string refs = docToString(ref); std::string outs = docToString(out); if(refs == outs) return; stripWhitespace(ref); stripWhitespace(out); std::string refs2 = docToString(ref); std::string outs2 = docToString(out); if(refs2 == outs2) return; assertImplementation(false, "Expected XML:\n" + refs + "\nbut got:\n" + outs); } // runTest std::string docToString(Arabica::DOM::Node node) { std::basic_ostringstream ss; ss << node; string_type streamed = string_adaptor::construct(ss.str()); streamed = Arabica::text::normalize_whitespace(streamed); std::string xml = string_adaptor::asStdString(streamed); if(xml.find(" ") == 0) xml.replace(0, 22, ""); return xml; } // docToString void stripWhitespace(Arabica::DOM::Node doc) { Arabica::XPath::NodeSet textNodes = selectNodes("//text()", doc); for(size_t i = 0; i != textNodes.size(); ++i) { Arabica::DOM::Node t = textNodes[i]; string_type text = t.getNodeValue(); text = Arabica::text::normalize_whitespace(text); size_t index = string_adaptor::find(text, Arabica::text::Unicode::SPACE); while(index != string_adaptor::npos()) { text = string_adaptor::concat(string_adaptor::substr(text, 0, index), string_adaptor::substr(text, index+1)); index = string_adaptor::find(text, Arabica::text::Unicode::SPACE); } t.setNodeValue(text); } } // stripWhitespace std::string input_xml_; std::string input_xslt_; std::string output_xml_; }; // class StandardXSLTTest class Expected { public: Expected() : loaded_(false) { } bool loaded() const { return loaded_; } void load() { if(loaded_) return; std::cerr << "Loaded expected fails" << std::endl; Arabica::DOM::Document fail_doc = buildDOM >(PATH_PREFIX + "arabica-expected-fails.xml"); Arabica::XPath::NodeSet failcases = selectNodes >("/test-suite/test-case", fail_doc); for(size_t i = 0; i != failcases.size(); ++i) { std::string name = selectString("@id", failcases[i]); std::string compiles = selectString("@compiles", failcases[i]); std::string runs = selectString("@runs", failcases[i]); std::string skip = selectString("@skip", failcases[i]); std::string reason = selectString("@reason", failcases[i]); std::string compare = selectString("@compare", failcases[i]); if(compiles == "no") fails_[name] = "compile"; else if(runs == "no") fails_[name] = "run"; else if(skip == "yes") fails_[name] = "skip"; else if(compare == "text") fails_[name] = "text"; else if(compare == "fragment") fails_[name] = "fragment"; reasons_[name] = reason; } // for ... loaded_ = true; } // load std::map& Fails() { return fails_; } std::map& Reasons() { return reasons_; } private: std::map fails_; std::map reasons_; bool loaded_; }; // class Expected //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// template Loader::Loader() : expected_fails_(new Expected()) { expected_fails_->load(); } // Loader template Loader::~Loader() { delete expected_fails_; } // ~Loader template Arabica::DOM::Document Loader::loadCatalog(const std::string& catalog_filename) { Arabica::DOM::Document c = catalogs_[catalog_filename]; if(c == 0) { std::cerr << "Loading " << catalog_filename << std::endl; c = buildDOM >(PATH_PREFIX + catalog_filename); catalogs_[catalog_filename] = c; } // if(c == 0) return c; } // catalog template TestSuite* Loader::suite(const std::string& path, const std::string& catalog_filename) { Arabica::DOM::Document catalog = loadCatalog(catalog_filename); TestSuite *suiteOfTests = new TestSuite; std::string testNamesSelector = "/test-suite/test-catalog/test-case[file-path='" + path + "']"; Arabica::XPath::NodeSet tests = selectNodes >(testNamesSelector.c_str(), catalog); std::cerr << "There are " << tests.size() << " " << path << " tests." << std::endl; for(size_t i = 0; i != tests.size(); ++i) { std::string operation = selectString("scenario/@operation", tests[i]); std::string name = selectString("@id", tests[i]); std::string path = selectString("concat(../major-path, '" + SEPERATOR + "', file-path)", tests[i]); std::string out_path = selectString("concat(../major-path, '" + SEPERATOR + "REF_OUT" + SEPERATOR + "', file-path)", tests[i]); std::string input_xml = selectString(".//input-file[@role='principal-data']", tests[i]); std::string input_xslt = selectString(".//input-file[@role='principal-stylesheet']", tests[i]); std::string output_xml = selectString(".//output-file[@role='principal']", tests[i]); if(expected_fails_->Fails().find(name) == expected_fails_->Fails().end()) { if(operation == "execution-error") suiteOfTests->addTest(new ExecutionErrorTest(name, make_path(path, input_xml), make_path(path, input_xslt))); else suiteOfTests->addTest(new StandardXSLTTest(name, make_path(path, input_xml), make_path(path, input_xslt), make_path(out_path, output_xml))); } else if(expected_fails_->Fails()[name] == "compile") suiteOfTests->addTest(new CompileFailsTest(name, make_path(path, input_xslt), expected_fails_->Reasons()[name])); else if(expected_fails_->Fails()[name] == "run") suiteOfTests->addTest(new RunFailsTest(name, make_path(path, input_xml), make_path(path, input_xslt), expected_fails_->Reasons()[name])); else if(expected_fails_->Fails()[name] == "skip") suiteOfTests->addTest(new SkipTest(name, expected_fails_->Reasons()[name])); else if(expected_fails_->Fails()[name] == "text") suiteOfTests->addTest(new CompareAsTextXSLTTest(name, make_path(path, input_xml), make_path(path, input_xslt), make_path(out_path, output_xml))); else if(expected_fails_->Fails()[name] == "fragment") suiteOfTests->addTest(new CompareAsXMLFragmentXSLTTest(name, make_path(path, input_xml), make_path(path, input_xslt), make_path(out_path, output_xml))); } // for ... return suiteOfTests; } // suite template TestSuite* Loader::XSLTTest_suite(const std::string& path) { return suite(path, "catalog.xml"); } // XSLTTest_suite template TestSuite* Loader::ArabicaTest_suite(const std::string& path) { return suite(path, "arabica-catalog.xml"); } // ArabicaTest_suite std::set parse_tests_to_run(int argc, const char* argv[]); template void add_tests(TestRunner& runner, Loader& loader, const std::set& wanted, const char** test_names); template void add_arabica_tests(TestRunner& runner, Loader& loader, const std::set& wanted, const char** test_names); const char* xalan_tests[] = {"attribvaltemplate", "axes", "boolean", "conditional", "conflictres", "copy", "dflt", "expression", "extend", /*"idkey",*/ "impincl", "lre", "match", "math", "mdocs", "message", "modes", "namedtemplate", "namespace", "node", /*"numberformat",*/ /*"numbering",*/ "output", "position", "predicate", "processorinfo", "reluri", "select", "sort", "string", "variable", "ver", "whitespace", 0}; const char* msft_tests[] = { "AVTs", /*"AttributeSets",*/ "Attributes", "BVTs", "Comment", "Completeness", "ConflictResolution", "Copying", "Elements", "Errors", "Fallback", "ForEach", /*"FormatNumber",*/ "ForwardComp", "Import", "Keys", "Messages", "Miscellaneous", "Modes", "NamedTemplates", "Namespace", "Namespace-alias", "Namespace_XPath", /*"Number",*/ /*"Output",*/ "ProcessingInstruction", "RTF", "Sorting", "Stylesheet", "Template", "Text", "Valueof", "Variables", "Whitespaces", "XSLTFunctions", 0 }; const char* arabica_tests[] = { "attributes", "errors", "include", "processing-instruction", "stylesheet", "text", "variables", 0 }; template bool XSLT_test_suite(int argc, const char* argv[]) { TestRunner runner; std::set tests_to_run = parse_tests_to_run(argc, argv); // runner.addTest("ScopeTest", ScopeTest_suite()); Loader loader; add_tests(runner, loader, tests_to_run, xalan_tests); add_tests(runner, loader, tests_to_run, msft_tests); add_arabica_tests(runner, loader, tests_to_run, arabica_tests); return runner.run(argc, argv); } // XSLT_test_suite std::set parse_tests_to_run(int argc, const char* argv[]) { std::set tests; for(int a = 1; a != argc; ++a) if(argv[a][0] != '-') tests.insert(argv[a]); else { if(std::string(argv[a]) == "-log") ++a; // skip next } // if ... return tests; } // tests_to_run template void add_tests(TestRunner& runner, Loader& loader, const std::set& wanted, const char** test_names) { std::set::const_iterator end = wanted.end(); while(*test_names != 0) { if(wanted.empty() || (wanted.find(*test_names) != end)) runner.addTest(*test_names, loader.XSLTTest_suite(*test_names)); ++test_names; } // while ... } // all_all_tests template void add_arabica_tests(TestRunner& runner, Loader& loader, const std::set& wanted, const char** test_names) { std::set::const_iterator end = wanted.end(); while(*test_names != 0) { if(wanted.empty() || (wanted.find(*test_names) != end)) runner.addTest(*test_names, loader.ArabicaTest_suite(*test_names)); ++test_names; } // while ... } // all_all_tests