/*============================================================================= registry =============================================================================== Test the method registry (server) C++ facilities of XML-RPC for C/C++. =============================================================================*/ #include #include "xmlrpc-c/girerr.hpp" using girerr::error; using girerr::throwf; #include "xmlrpc-c/base.hpp" #include "xmlrpc-c/registry.hpp" #include "tools.hpp" #include "registry.hpp" using namespace xmlrpc_c; using namespace std; namespace { static string const xmlPrologue("\r\n"); static string const apacheUrl("http://ws.apache.org/xmlrpc/namespaces/extensions"); static string const xmlnsApache("xmlns:ex=\"" + apacheUrl + "\""); string const noElementFoundXml( xmlPrologue + "\r\n" "\r\n" "\r\n" "faultCode\r\n" "-503\r\n" "faultString\r\n" "Call XML not a proper XML-RPC call. " "Call is not valid XML. no element found" "\r\n" "\r\n" "\r\n" "\r\n" ); string const invalidXMLCall( xmlPrologue + "\r\n" "\r\n" "\r\n" "faultCode\r\n" "-503\r\n" "faultString\r\n" "Call XML not a proper XML-RPC call. " "Call is not valid XML. XML parsing failed" "\r\n" "\r\n" "\r\n" "\r\n" ); string const sampleAddGoodCallXml( xmlPrologue + "\r\n" "sample.add\r\n" "\r\n" "5\r\n" "7\r\n" "\r\n" "\r\n" ); string const sampleAddGoodResponseXml( xmlPrologue + "\r\n" "\r\n" "12\r\n" "\r\n" "\r\n" ); string const sampleAddBadCallXml( xmlPrologue + "\r\n" "sample.add\r\n" "\r\n" "5\r\n" "\r\n" "\r\n" ); string const sampleAddBadResponseXml( xmlPrologue + "\r\n" "\r\n" "\r\n" "faultCode\r\n" "-501\r\n" "faultString\r\n" "Not enough parameters\r\n" "\r\n" "\r\n" "\r\n" ); string const testCallInfoCallXml( xmlPrologue + "\r\n" "test.callinfo\r\n" "\r\n" "\r\n" "\r\n" ); string const testCallInfoResponseXml( xmlPrologue + "\r\n" "\r\n" "this is a test callInfo" "\r\n" "\r\n" "\r\n" ); string const nonexistentMethodCallXml( xmlPrologue + "\r\n" "nosuchmethod\r\n" "\r\n" "5\r\n" "7\r\n" "\r\n" "\r\n" ); string const nonexistentMethodYesDefResponseXml( xmlPrologue + "\r\n" "\r\n" "no such method: nosuchmethod" "\r\n" "\r\n" "\r\n" ); string const nonexistentMethodNoDefResponseXml( xmlPrologue + "\r\n" "\r\n" "\r\n" "faultCode\r\n" "-506\r\n" "faultString\r\n" "Method 'nosuchmethod' not defined" "\r\n" "\r\n" "\r\n" "\r\n" ); string const echoI8ApacheCall( xmlPrologue + "\r\n" "echo\r\n" "\r\n" "5\r\n" "\r\n" "\r\n" ); string const echoI8ApacheResponse( xmlPrologue + "\r\n" "\r\n" "5\r\n" "\r\n" "\r\n" ); string const echoNilApacheCall( xmlPrologue + "\r\n" "echo\r\n" "\r\n" "\r\n" "\r\n" "\r\n" ); string const echoNilApacheResponse( xmlPrologue + "\r\n" "\r\n" "\r\n" "\r\n" "\r\n" ); class callInfo_test : public callInfo { public: callInfo_test() : data("this is a test callInfo") {} callInfo_test(string const& data) : data(data) {}; string data; }; class sampleAddMethod : public method { public: sampleAddMethod() { this->_signature = "i:ii"; this->_help = "This method adds two integers together"; } void execute(xmlrpc_c::paramList const& paramList, value * const retvalP) { int const addend(paramList.getInt(0)); int const adder(paramList.getInt(1)); paramList.verifyEnd(2); *retvalP = value_int(addend + adder); } }; class sampleAddMethod2 : public method2 { public: sampleAddMethod2() { this->_signature = "i:ii"; this->_help = "This method adds two integers together"; } void execute(xmlrpc_c::paramList const& paramList, const callInfo * const, value * const retvalP) { int const addend(paramList.getInt(0)); int const adder(paramList.getInt(1)); paramList.verifyEnd(2); *retvalP = value_int(addend + adder); } }; class testCallInfoMethod : public method2 { public: testCallInfoMethod() { this->_signature = "s:"; } void execute(xmlrpc_c::paramList const& paramList, const callInfo * const callInfoPtr, value * const retvalP) { const callInfo_test * const callInfoP( dynamic_cast(callInfoPtr)); TEST(callInfoP != NULL); paramList.verifyEnd(0); *retvalP = value_string(callInfoP->data); } }; class nameMethod : public defaultMethod { void execute(string const& methodName, xmlrpc_c::paramList const& , // paramList value * const retvalP) { *retvalP = value_string(string("no such method: ") + methodName); } }; class echoMethod : public method { public: void execute(xmlrpc_c::paramList const& paramList, value * const retvalP) { paramList.verifyEnd(1); *retvalP = paramList[0]; } }; static void testEmptyXmlDocCall(xmlrpc_c::registry const& myRegistry) { string response; myRegistry.processCall("", &response); #ifdef INTERNAL_EXPAT TEST(response == noElementFoundXml); #else // This is what we get with libxml2 TEST(response == invalidXMLCall); #endif } class registryRegMethodTestSuite : public testSuite { public: virtual string suiteName() { return "registryRegMethodTestSuite"; } virtual void runtests(unsigned int const) { xmlrpc_c::registry myRegistry; sampleAddMethod m; myRegistry.addMethod("xyz", &m); myRegistry.addMethod("sample.add", xmlrpc_c::methodPtr(new sampleAddMethod)); myRegistry.disableIntrospection(); testEmptyXmlDocCall(myRegistry); { string response; myRegistry.processCall(sampleAddGoodCallXml, &response); TEST(response == sampleAddGoodResponseXml); } { string response; myRegistry.processCall(sampleAddBadCallXml, &response); TEST(response == sampleAddBadResponseXml); } { string response; callInfo const callInfo; myRegistry.processCall(sampleAddBadCallXml, &callInfo, &response); TEST(response == sampleAddBadResponseXml); } } }; class registryDefaultMethodTestSuite : public testSuite { public: virtual string suiteName() { return "registryDefaultMethodTestSuite"; } virtual void runtests(unsigned int const) { xmlrpc_c::registry myRegistry; myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod)); { string response; myRegistry.processCall(sampleAddGoodCallXml, &response); TEST(response == sampleAddGoodResponseXml); } { string response; myRegistry.processCall(nonexistentMethodCallXml, &response); TEST(response == nonexistentMethodNoDefResponseXml); } // We're actually violating the spirit of setDefaultMethod by // doing this to a registry that's already been used, but as long // as it works, it's a convenient way to implement this test. nameMethod dm; myRegistry.setDefaultMethod(&dm); myRegistry.setDefaultMethod(defaultMethodPtr(new nameMethod)); { string response; myRegistry.processCall(nonexistentMethodCallXml, &response); TEST(response == nonexistentMethodYesDefResponseXml); } } }; class method2TestSuite : public testSuite { public: virtual string suiteName() { return "method2TestSuite"; } virtual void runtests(unsigned int const) { xmlrpc_c::registry myRegistry; myRegistry.addMethod("sample.add", xmlrpc_c::methodPtr(new sampleAddMethod2)); myRegistry.addMethod("test.callinfo", xmlrpc_c::methodPtr(new testCallInfoMethod)); { string response; myRegistry.processCall(sampleAddGoodCallXml, &response); TEST(response == sampleAddGoodResponseXml); } { string response; myRegistry.processCall(sampleAddBadCallXml, &response); TEST(response == sampleAddBadResponseXml); } { string response; callInfo_test const callInfo; myRegistry.processCall(testCallInfoCallXml, &callInfo, &response); TEST(response == testCallInfoResponseXml); } } }; class dialectTestSuite : public testSuite { public: virtual string suiteName() { return "dialectTestSuite"; } virtual void runtests(unsigned int const) { registry myRegistry; string response; myRegistry.addMethod("sample.add", methodPtr(new sampleAddMethod)); myRegistry.addMethod("echo", methodPtr(new echoMethod)); myRegistry.setDialect(xmlrpc_dialect_i8); myRegistry.setDialect(xmlrpc_dialect_apache); myRegistry.processCall(echoI8ApacheCall, &response); TEST(response == echoI8ApacheResponse); myRegistry.processCall(echoNilApacheCall, &response); TEST(response == echoNilApacheResponse); EXPECT_ERROR( // invalid dialect myRegistry.setDialect(static_cast(300)); ); } }; class testShutdown : public xmlrpc_c::registry::shutdown { /*---------------------------------------------------------------------------- This class is logically local to registryShutdownTestSuite::runtests(), but if we declare it that way, gcc 2.95.3 fails with some bogus messages about undefined references from random functions when we do that. -----------------------------------------------------------------------------*/ public: void doit(string const&, void * const) const { } }; class registryShutdownTestSuite : public testSuite { public: virtual string suiteName() { return "registryShutdownTestSuite"; } virtual void runtests(unsigned int const) { xmlrpc_c::registry myRegistry; testShutdown shutdown; myRegistry.setShutdown(&shutdown); } }; } // unnamed namespace string registryTestSuite::suiteName() { return "registryTestSuite"; } void registryTestSuite::runtests(unsigned int const indentation) { { registryPtr myRegistryP(new registry); myRegistryP->addMethod("sample.add", methodPtr(new sampleAddMethod)); } registryRegMethodTestSuite().run(indentation+1); registryDefaultMethodTestSuite().run(indentation+1); method2TestSuite().run(indentation+1); registry myRegistry; myRegistry.disableIntrospection(); dialectTestSuite().run(indentation+1); registryShutdownTestSuite().run(indentation+1); TEST(myRegistry.maxStackSize() >= 256); }