From cd05a244da8d3249db1f3c1e8db215eb28da2816 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 6 Jun 2012 19:57:58 -0400 Subject: MAINT-1144: Break out llhttpclient_tut as standalone integration test. indra/test/llhttpclient_tut.cpp ==> indra/llmessage/tests/llhttpclient_test.cpp, with corresponding CMakeLists.txt changes in their respective directories. This first commit merely moves the test source file without changing it -- separating out moves from edits simplifies code review. --- indra/llmessage/CMakeLists.txt | 8 + indra/llmessage/tests/llhttpclient_test.cpp | 386 ++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+) create mode 100644 indra/llmessage/tests/llhttpclient_test.cpp (limited to 'indra/llmessage') diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 0f40a670fa..d98781e9e6 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -254,6 +254,14 @@ if (LL_TESTS) "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" ) + LL_ADD_INTEGRATION_TEST( + llhttpclient + "llhttpclient.cpp" + "${test_libs}" + ${PYTHON_EXECUTABLE} + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py" + ) + LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}") diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp new file mode 100644 index 0000000000..1e0296918c --- /dev/null +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -0,0 +1,386 @@ +/** + * @file llhttpclient_tut.cpp + * @brief Testing the HTTP client classes. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/** + * + * These classes test the HTTP client framework. + * + */ + +#include +#include "linden_common.h" + +// These are too slow on Windows to actually include in the build. JC +#if !LL_WINDOWS + +#include "lltut.h" +#include "llhttpclient.h" +#include "llformat.h" +#include "llpipeutil.h" +#include "llproxy.h" +#include "llpumpio.h" + +#include "llsdhttpserver.h" +#include "lliohttpserver.h" +#include "lliosocket.h" + +namespace tut +{ + LLSD storage; + + class LLSDStorageNode : public LLHTTPNode + { + public: + LLSD simpleGet() const { return storage; } + LLSD simplePut(const LLSD& value) const { storage = value; return LLSD(); } + }; + + class ErrorNode : public LLHTTPNode + { + public: + void get(ResponsePtr r, const LLSD& context) const + { r->status(599, "Intentional error"); } + void post(ResponsePtr r, const LLSD& context, const LLSD& input) const + { r->status(input["status"], input["reason"]); } + }; + + class TimeOutNode : public LLHTTPNode + { + public: + void get(ResponsePtr r, const LLSD& context) const + { + /* do nothing, the request will eventually time out */ + } + }; + + LLHTTPRegistration gStorageNode("/test/storage"); + LLHTTPRegistration gErrorNode("/test/error"); + LLHTTPRegistration gTimeOutNode("/test/timeout"); + + struct HTTPClientTestData + { + public: + HTTPClientTestData() + { + apr_pool_create(&mPool, NULL); + LLCurl::initClass(false); + mServerPump = new LLPumpIO(mPool); + mClientPump = new LLPumpIO(mPool); + + LLHTTPClient::setPump(*mClientPump); + } + + ~HTTPClientTestData() + { + delete mServerPump; + delete mClientPump; + LLProxy::cleanupClass(); + apr_pool_destroy(mPool); + } + + void setupTheServer() + { + LLHTTPNode& root = LLIOHTTPServer::create(mPool, *mServerPump, 8888); + + LLHTTPStandardServices::useServices(); + LLHTTPRegistrar::buildAllServices(root); + } + + void runThePump(float timeout = 100.0f) + { + LLTimer timer; + timer.setTimerExpirySec(timeout); + + while(!mSawCompleted && !mSawCompletedHeader && !timer.hasExpired()) + { + if (mServerPump) + { + mServerPump->pump(); + mServerPump->callback(); + } + if (mClientPump) + { + mClientPump->pump(); + mClientPump->callback(); + } + } + } + + void killServer() + { + delete mServerPump; + mServerPump = NULL; + } + + private: + apr_pool_t* mPool; + LLPumpIO* mServerPump; + LLPumpIO* mClientPump; + + + protected: + void ensureStatusOK() + { + if (mSawError) + { + std::string msg = + llformat("error() called when not expected, status %d", + mStatus); + fail(msg); + } + } + + void ensureStatusError() + { + if (!mSawError) + { + fail("error() wasn't called"); + } + } + + LLSD getResult() + { + return mResult; + } + LLSD getHeader() + { + return mHeader; + } + + protected: + bool mSawError; + U32 mStatus; + std::string mReason; + bool mSawCompleted; + bool mSawCompletedHeader; + LLSD mResult; + LLSD mHeader; + bool mResultDeleted; + + class Result : public LLHTTPClient::Responder + { + protected: + Result(HTTPClientTestData& client) + : mClient(client) + { + } + + public: + static boost::intrusive_ptr build(HTTPClientTestData& client) + { + return boost::intrusive_ptr(new Result(client)); + } + + ~Result() + { + mClient.mResultDeleted = true; + } + + virtual void error(U32 status, const std::string& reason) + { + mClient.mSawError = true; + mClient.mStatus = status; + mClient.mReason = reason; + } + + virtual void result(const LLSD& content) + { + mClient.mResult = content; + } + + virtual void completed( + U32 status, const std::string& reason, + const LLSD& content) + { + LLHTTPClient::Responder::completed(status, reason, content); + + mClient.mSawCompleted = true; + } + + virtual void completedHeader( + U32 status, const std::string& reason, + const LLSD& content) + { + mClient.mHeader = content; + mClient.mSawCompletedHeader = true; + } + + private: + HTTPClientTestData& mClient; + }; + + friend class Result; + + protected: + LLHTTPClient::ResponderPtr newResult() + { + mSawError = false; + mStatus = 0; + mSawCompleted = false; + mSawCompletedHeader = false; + mResult.clear(); + mHeader.clear(); + mResultDeleted = false; + + return Result::build(*this); + } + }; + + + typedef test_group HTTPClientTestGroup; + typedef HTTPClientTestGroup::object HTTPClientTestObject; + HTTPClientTestGroup httpClientTestGroup("http_client"); + + template<> template<> + void HTTPClientTestObject::test<1>() + { + skip("google.com unit tests stopped working 2012-06-06"); + LLHTTPClient::get("http://www.google.com/", newResult()); + runThePump(); + ensureStatusOK(); + ensure("result object wasn't destroyed", mResultDeleted); + } + + template<> template<> + void HTTPClientTestObject::test<2>() + { + skip("error test depends on dev's local ISP not supplying \"helpful\" search page"); + LLHTTPClient::get("http://www.invalid", newResult()); + runThePump(); + ensureStatusError(); + } + + template<> template<> + void HTTPClientTestObject::test<3>() + { + LLSD sd; + + sd["list"][0]["one"] = 1; + sd["list"][0]["two"] = 2; + sd["list"][1]["three"] = 3; + sd["list"][1]["four"] = 4; + + setupTheServer(); + + LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult()); + runThePump(); + ensureStatusOK(); + ensure_equals("echoed result matches", getResult(), sd); + } + + template<> template<> + void HTTPClientTestObject::test<4>() + { + LLSD sd; + + sd["message"] = "This is my test message."; + + setupTheServer(); + LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult()); + runThePump(); + ensureStatusOK(); + + LLHTTPClient::get("http://localhost:8888/test/storage", newResult()); + runThePump(); + ensureStatusOK(); + ensure_equals("echoed result matches", getResult(), sd); + + } + + template<> template<> + void HTTPClientTestObject::test<5>() + { + LLSD sd; + sd["status"] = 543; + sd["reason"] = "error for testing"; + + setupTheServer(); + + LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult()); + runThePump(); + ensureStatusError(); + ensure_contains("reason", mReason, sd["reason"]); + } + + template<> template<> + void HTTPClientTestObject::test<6>() + { + setupTheServer(); + + LLHTTPClient::get("http://localhost:8888/test/timeout", newResult()); + runThePump(1.0f); + killServer(); + runThePump(); + ensureStatusError(); + ensure_equals("reason", mReason, "STATUS_ERROR"); + } + + template<> template<> + void HTTPClientTestObject::test<7>() + { + // Can not use the little mini server. The blocking request + // won't ever let it run. Instead get from a known LLSD + // source and compare results with the non-blocking get which + // is tested against the mini server earlier. + skip("secondlife.com is not reliable enough for unit tests."); + + + LLSD expected; + + LLHTTPClient::get("http://secondlife.com/xmlhttp/homepage.php", newResult()); + runThePump(); + ensureStatusOK(); + expected = getResult(); + + LLSD result; + result = LLHTTPClient::blockingGet("http://secondlife.com/xmlhttp/homepage.php"); + LLSD body = result["body"]; + ensure_equals("echoed result matches", body.size(), expected.size()); + } + template<> template<> + void HTTPClientTestObject::test<8>() + { + skip("google.com unit tests stopped working 2012-06-06"); + // This is testing for the presence of the Header in the returned results + // from an HTTP::get call. + LLHTTPClient::get("http://www.google.com/", newResult()); + runThePump(); + ensureStatusOK(); + LLSD header = getHeader(); + ensure_equals("got a header", header.emptyMap().asBoolean(), FALSE); + } + template<> template<> + void HTTPClientTestObject::test<9>() + { + skip("google.com unit tests stopped working 2012-06-06"); + LLHTTPClient::head("http://www.google.com/", newResult()); + runThePump(); + ensureStatusOK(); + ensure("result object wasn't destroyed", mResultDeleted); + } +} + +#endif // !LL_WINDOWS -- cgit v1.2.3 From 3d1cb7ef111cbd5724262077702e0dc2a3d6998d Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 6 Jun 2012 23:12:11 -0400 Subject: MAINT-1144: Re-enable skipped LLHTTPClient tests with local server. Over the years we've skipped more and more of the tests in llhttpclient_test.cpp (nee llhttpclient_tut.cpp) because they've relied on particular behaviors from Internet sites not under our control. We skipped a test that fetches llsd+xml from secondlife.com because "secondlife.com is not reliable enough for unit tests." We skipped a test that tries to observe a failure with "http://www.invalid" because some local ISPs turn "no such domain" DNS errors into valid pages offering the requester to buy the specified domain name. Today we've had to skip tests attempting to contact "http://www.google.com" for reasons we haven't yet diagnosed, but that probably have to do with Google's IPv6 rollout. Use local temp server test_llsdmessage_peer.py as the success destination, eliminating DNS, Internet access and remote server behavior as failure modes. Use idle localhost port for failure test. Re-enable all skipped LLHTTPClient tests! Re-enable on Windows! In support of these tests, modify test_llsdmessage_peer.py: Support HEAD as no-data variant of GET. Change GET result dict to avoid resembling an error response -- confusing. Make GET/POST return actual dict rather than undecorated string "success". Because of that last, change llcapabilitylistener_test.cpp and llsdmessage_test.cpp to extract "reply" key from response rather than expecting response to be a string itself. --- indra/llmessage/tests/llhttpclient_test.cpp | 40 ++++++++++---------------- indra/llmessage/tests/llsdmessage_test.cpp | 2 +- indra/llmessage/tests/test_llsdmessage_peer.py | 19 ++++++++---- 3 files changed, 29 insertions(+), 32 deletions(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp index 1e0296918c..986fd5f3fc 100644 --- a/indra/llmessage/tests/llhttpclient_test.cpp +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -1,5 +1,5 @@ /** - * @file llhttpclient_tut.cpp + * @file llhttpclient_test.cpp * @brief Testing the HTTP client classes. * * $LicenseInfo:firstyear=2006&license=viewerlgpl$ @@ -33,9 +33,6 @@ #include #include "linden_common.h" -// These are too slow on Windows to actually include in the build. JC -#if !LL_WINDOWS - #include "lltut.h" #include "llhttpclient.h" #include "llformat.h" @@ -46,6 +43,7 @@ #include "llsdhttpserver.h" #include "lliohttpserver.h" #include "lliosocket.h" +#include "stringize.h" namespace tut { @@ -83,7 +81,8 @@ namespace tut struct HTTPClientTestData { public: - HTTPClientTestData() + HTTPClientTestData(): + local_server(STRINGIZE("http://127.0.0.1:" << getenv("PORT") << "/")) { apr_pool_create(&mPool, NULL); LLCurl::initClass(false); @@ -134,13 +133,14 @@ namespace tut delete mServerPump; mServerPump = NULL; } - + + const std::string local_server; + private: apr_pool_t* mPool; LLPumpIO* mServerPump; LLPumpIO* mClientPump; - protected: void ensureStatusOK() { @@ -257,8 +257,7 @@ namespace tut template<> template<> void HTTPClientTestObject::test<1>() { - skip("google.com unit tests stopped working 2012-06-06"); - LLHTTPClient::get("http://www.google.com/", newResult()); + LLHTTPClient::get(local_server, newResult()); runThePump(); ensureStatusOK(); ensure("result object wasn't destroyed", mResultDeleted); @@ -267,8 +266,8 @@ namespace tut template<> template<> void HTTPClientTestObject::test<2>() { - skip("error test depends on dev's local ISP not supplying \"helpful\" search page"); - LLHTTPClient::get("http://www.invalid", newResult()); + // Please nobody listen on this particular port... + LLHTTPClient::get("http://127.0.0.1:7950", newResult()); runThePump(); ensureStatusError(); } @@ -345,28 +344,22 @@ namespace tut // won't ever let it run. Instead get from a known LLSD // source and compare results with the non-blocking get which // is tested against the mini server earlier. - skip("secondlife.com is not reliable enough for unit tests."); - - - LLSD expected; - - LLHTTPClient::get("http://secondlife.com/xmlhttp/homepage.php", newResult()); + LLHTTPClient::get(local_server, newResult()); runThePump(); ensureStatusOK(); - expected = getResult(); + LLSD expected = getResult(); LLSD result; - result = LLHTTPClient::blockingGet("http://secondlife.com/xmlhttp/homepage.php"); + result = LLHTTPClient::blockingGet(local_server); LLSD body = result["body"]; ensure_equals("echoed result matches", body.size(), expected.size()); } template<> template<> void HTTPClientTestObject::test<8>() { - skip("google.com unit tests stopped working 2012-06-06"); // This is testing for the presence of the Header in the returned results // from an HTTP::get call. - LLHTTPClient::get("http://www.google.com/", newResult()); + LLHTTPClient::get(local_server, newResult()); runThePump(); ensureStatusOK(); LLSD header = getHeader(); @@ -375,12 +368,9 @@ namespace tut template<> template<> void HTTPClientTestObject::test<9>() { - skip("google.com unit tests stopped working 2012-06-06"); - LLHTTPClient::head("http://www.google.com/", newResult()); + LLHTTPClient::head(local_server, newResult()); runThePump(); ensureStatusOK(); ensure("result object wasn't destroyed", mResultDeleted); } } - -#endif // !LL_WINDOWS diff --git a/indra/llmessage/tests/llsdmessage_test.cpp b/indra/llmessage/tests/llsdmessage_test.cpp index 0f2c069303..31a791e4b4 100644 --- a/indra/llmessage/tests/llsdmessage_test.cpp +++ b/indra/llmessage/tests/llsdmessage_test.cpp @@ -143,7 +143,7 @@ namespace tut httpPump.post(request); ensure("got response", netio.pump()); ensure("success response", success); - ensure_equals(result.asString(), "success"); + ensure_equals(result["reply"].asString(), "success"); body["status"] = 499; body["reason"] = "custom error message"; diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 22edd9dad8..fe4f3a8c01 100644 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -78,25 +78,32 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler): ## debug("root node tag %s", tree.getroot().tag) ## return llsd.to_python(tree.getroot()) - def do_GET(self): + def do_HEAD(self): + self.do_GET(withdata=False) + + def do_GET(self, withdata=True): # Of course, don't attempt to read data. - self.answer(dict(reply="success", status=500, - reason="Your GET operation requested failure")) + data = dict(reply="success", body="avatar", random=17) + self.answer(data, withdata=withdata) def do_POST(self): # Read the provided POST data. self.answer(self.read_xml()) - def answer(self, data): + def answer(self, data, withdata=True): debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) if "fail" not in self.path: - response = llsd.format_xml(data.get("reply", llsd.LLSD("success"))) + data = data.copy() # we're going to modify + # Ensure there's a "reply" key in data, even if there wasn't before + data["reply"] = data.get("reply", llsd.LLSD("success")) + response = llsd.format_xml(data) debug("success: %s", response) self.send_response(200) self.send_header("Content-type", "application/llsd+xml") self.send_header("Content-Length", str(len(response))) self.end_headers() - self.wfile.write(response) + if withdata: + self.wfile.write(response) else: # fail requested status = data.get("status", 500) # self.responses maps an int status to a (short, long) pair of -- cgit v1.2.3 From d167ebe35f8cdec1ca88e0d817e2878f14a5aa68 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 7 Jun 2012 05:46:47 -0400 Subject: MAINT-1144: Try to fix Windows build error in llhttpclient_test.cpp. --- indra/llmessage/tests/llhttpclient_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llmessage') diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp index 986fd5f3fc..843c3bcc4b 100644 --- a/indra/llmessage/tests/llhttpclient_test.cpp +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -363,7 +363,7 @@ namespace tut runThePump(); ensureStatusOK(); LLSD header = getHeader(); - ensure_equals("got a header", header.emptyMap().asBoolean(), FALSE); + ensure("got a header", ! header.emptyMap().asBoolean()); } template<> template<> void HTTPClientTestObject::test<9>() -- cgit v1.2.3