diff options
Diffstat (limited to 'indra/llcorehttp/tests')
| -rwxr-xr-x | indra/llcorehttp/tests/llcorehttp_test.cpp | 175 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/llcorehttp_test.h | 64 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_allocator.cpp | 184 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_allocator.h | 47 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_bufferarray.hpp | 432 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_bufferstream.hpp | 304 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httpheaders.hpp | 108 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httpoperation.hpp | 125 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httprequest.hpp | 2670 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httprequestqueue.hpp | 186 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_httpstatus.hpp | 265 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_llcorehttp_peer.py | 190 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/test_refcounted.hpp | 156 | ||||
| -rwxr-xr-x | indra/llcorehttp/tests/testrunner.py | 265 | 
14 files changed, 5171 insertions, 0 deletions
| diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp new file mode 100755 index 0000000000..e863ddd13f --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -0,0 +1,175 @@ +/**  + * @file llcorehttp_test + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#include "llcorehttp_test.h" + +#include <iostream> +#include <sstream> + +// These are not the right way in viewer for some reason: +// #include <tut/tut.hpp> +// #include <tut/tut_reporter.hpp> +// This works: +#include "../test/lltut.h" + +// Pull in each of the test sets +#include "test_bufferarray.hpp" +#include "test_bufferstream.hpp" +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_httprequestqueue.hpp" + +#include "llproxy.h" + +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); + +#if 0	// lltut provides main and runner + +namespace tut +{ +	test_runner_singleton runner; +} + +int main() +{ +	curl_global_init(CURL_GLOBAL_ALL); + +	// *FIXME:  Need threaded/SSL curl setup here. +	 +	tut::reporter reporter; + +	tut::runner.get().set_callback(&reporter); +	tut::runner.get().run_tests(); +	return !reporter.all_ok(); + +	curl_global_cleanup(); +} + +#endif // 0 + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ +	curl_global_init(CURL_GLOBAL_ALL); + +	ssl_mutex_count = CRYPTO_num_locks(); +	if (ssl_mutex_count > 0) +	{ +		ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; +		 +		for (int i(0); i < ssl_mutex_count; ++i) +		{ +			ssl_mutex_list[i] = new LLCoreInt::HttpMutex; +		} + +		CRYPTO_set_locking_callback(ssl_locking_callback); +		CRYPTO_set_id_callback(ssl_thread_id_callback); +	} + +	LLProxy::getInstance(); +} + + +void term_curl() +{ +	LLProxy::cleanupClass(); +	 +	CRYPTO_set_locking_callback(NULL); +	for (int i(0); i < ssl_mutex_count; ++i) +	{ +		delete ssl_mutex_list[i]; +	} +	delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) +	return (unsigned long) GetCurrentThread(); +#else +	return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ +	if (type >= 0 && type < ssl_mutex_count) +	{ +		if (mode & CRYPTO_LOCK) +		{ +			ssl_mutex_list[type]->lock(); +		} +		else +		{ +			ssl_mutex_list[type]->unlock(); +		} +	} +} + + +std::string get_base_url() +{ +	const char * env(getenv("LL_TEST_PORT")); + +	if (! env) +	{ +		std::cerr << "LL_TEST_PORT environment variable missing." << std::endl; +		std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl; +		tut::ensure("LL_TEST_PORT set in environment", NULL != env); +	} + +	int port(atoi(env)); +	std::ostringstream out; +	out << "http://localhost:" << port << "/"; +	return out.str(); +} + + +void stop_thread(LLCore::HttpRequest * req) +{ +	if (req) +	{ +		req->requestStopThread(NULL); +	 +		int count = 0; +		int limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			req->update(1000); +			usleep(100000); +		} +	} +} + +	 diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h new file mode 100755 index 0000000000..a9567435ce --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -0,0 +1,64 @@ +/**  + * @file llcorehttp_test.h + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + + +#ifndef _LLCOREHTTP_TEST_H_ +#define	_LLCOREHTTP_TEST_H_ + +#include "linden_common.h"		// Modifies curl interfaces + +#include <curl/curl.h> +#include <openssl/crypto.h> +#include <string> + +#include "httprequest.h" + +// Initialization and cleanup for libcurl.  Mainly provides +// a mutex callback for SSL and a thread ID hash for libcurl. +// If you don't use these (or equivalent) and do use libcurl, +// you'll see stalls and other anomalies when performing curl +// operations. +extern void init_curl(); +extern void term_curl(); +extern std::string get_base_url(); +extern void stop_thread(LLCore::HttpRequest * req); + +class ScopedCurlInit +{ +public: +	ScopedCurlInit() +		{ +			init_curl(); +		} + +	~ScopedCurlInit() +		{ +			term_curl(); +		} +}; +	 + +#endif	// _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp new file mode 100755 index 0000000000..ea12dc58eb --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -0,0 +1,184 @@ +/**  + * @file test_allocator.cpp + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#include "test_allocator.h" + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +#include <libkern/OSAtomic.h> +#elif defined(_MSC_VER) +#include <Windows.h> +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +// atomic extensions are built into GCC on posix platforms +#endif + +#include <cassert> +#include <cstdlib> +#include <cstring> +#include <vector> +#include <iostream> +#include <new> + +#include <boost/thread.hpp> + + +#if	defined(WIN32) +#define	THROW_BAD_ALLOC()	_THROW1(std::bad_alloc) +#define	THROW_NOTHING()		_THROW0() +#else +#define	THROW_BAD_ALLOC()	throw(std::bad_alloc) +#define	THROW_NOTHING()		throw() +#endif + + +struct BlockHeader +{ +	struct Block * next; +	std::size_t size; +	bool in_use; +}; + +struct Block +{ +	BlockHeader hdr; +	unsigned char data[1]; +}; + +#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl; + +static unsigned char MemBuf[ 4096 * 1024 ]; +Block * pNext = static_cast<Block *>(static_cast<void *>(MemBuf)); +volatile std::size_t MemTotal = 0; + +// cross-platform compare and swap operation +static bool CAS(void * volatile * ptr, void * expected, void * new_value) +{ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +	return OSAtomicCompareAndSwapPtr( expected, new_value, ptr ); +#elif defined(_MSC_VER) +	return expected == InterlockedCompareExchangePointer( ptr, new_value, expected ); +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +	return __sync_bool_compare_and_swap( ptr, expected, new_value ); +#endif +} + +static void * GetMem(std::size_t size) +{ +	// TRACE_MSG(size); +	volatile Block * pBlock = NULL; +	volatile Block * pNewNext = NULL; + +	// do a lock-free update of the global next pointer +	do +	{ +		pBlock = pNext; +		pNewNext = (volatile Block *)(pBlock->data + size); + +	} while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext)); + +	// if we get here, we safely carved out a block of memory in the +	// memory pool... + +	// initialize our block +	pBlock->hdr.next = (Block *)(pBlock->data + size); +	pBlock->hdr.size = size; +	pBlock->hdr.in_use = true; +	memset((void *) pBlock->data, 0, pBlock->hdr.size); + +	// do a lock-free update of the global memory total +	volatile size_t total = 0; +	volatile size_t new_total = 0; +	do +	{ +		total = MemTotal; +		new_total = total + size; + +	} while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total)); + +	return (void *) pBlock->data; +} + + +static void FreeMem(void * p) +{ +	// get the pointer to the block record +	Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader)); + +	// TRACE_MSG(pBlock->hdr.size); +	bool * cur_in_use = &(pBlock->hdr.in_use); +	volatile bool in_use = false; +	bool new_in_use = false; +	do +	{ +		in_use = pBlock->hdr.in_use; +	} while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use)); + +	// do a lock-free update of the global memory total +	volatile size_t total = 0; +	volatile size_t new_total = 0; +	do +	{ +		total = MemTotal; +		new_total = total - pBlock->hdr.size; +	} while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total)); +} + + +std::size_t GetMemTotal() +{ +	return MemTotal; +} + + +void * operator new(std::size_t size) THROW_BAD_ALLOC() +{ +	return GetMem( size ); +} + + +void * operator new[](std::size_t size) THROW_BAD_ALLOC() +{ +	return GetMem( size ); +} + + +void operator delete(void * p) THROW_NOTHING() +{ +	if (p) +	{ +		FreeMem( p ); +	} +} + + +void operator delete[](void * p) THROW_NOTHING() +{ +	if (p) +	{ +		FreeMem( p ); +	} +} + + diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h new file mode 100755 index 0000000000..3572bbc5c5 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.h @@ -0,0 +1,47 @@ +/**  + * @file test_allocator.h + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef TEST_ALLOCATOR_H +#define TEST_ALLOCATOR_H + +#include <cstdlib> +#include <new> + +size_t GetMemTotal(); +#if	defined(WIN32) +void * operator new(std::size_t size) _THROW1(std::bad_alloc); +void * operator new[](std::size_t size) _THROW1(std::bad_alloc); +void operator delete(void * p) _THROW0(); +void operator delete[](void * p) _THROW0(); +#else +void * operator new(std::size_t size) throw (std::bad_alloc); +void * operator new[](std::size_t size) throw (std::bad_alloc); +void operator delete(void * p) throw (); +void operator delete[](void * p) throw (); +#endif + +#endif // TEST_ALLOCATOR_H + diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp new file mode 100755 index 0000000000..8a2a64d970 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -0,0 +1,432 @@ +/**  + * @file test_bufferarray.hpp + * @brief unit tests for the LLCore::BufferArray class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCORE_BUFFER_ARRAY_H_ +#define TEST_LLCORE_BUFFER_ARRAY_H_ + +#include "bufferarray.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCore; + + + +namespace tut +{ + +struct BufferArrayTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<BufferArrayTestData> BufferArrayTestGroupType; +typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; +BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); + +template <> template <> +void BufferArrayTestObjectType::test<1>() +{ +	set_test_name("BufferArray construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); +	ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); +	ensure("Nothing in BA", 0 == ba->size()); + +	// Try to read +	char buffer[20]; +	size_t read_len(ba->read(0, buffer, sizeof(buffer))); +	ensure("Read returns empty", 0 == read_len); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<2>() +{ +	set_test_name("BufferArray single write"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; + 	char buffer[256]; +	 +	size_t len = ba->write(0, str1, strlen(str1)); +	ensure("Wrote length correct", strlen(str1) == len); +	ensure("Recorded size correct", strlen(str1) == ba->size()); + +	// read some data back +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(2, buffer, 2); +	ensure("Read length correct", 2 == len); +	ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); +	ensure("Read didn't overwrite", 'X' == buffer[2]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferArrayTestObjectType::test<3>() +{ +	set_test_name("BufferArray multiple writes"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); + 	char buffer[256]; +	 +	size_t len = ba->write(0, str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1_len, str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// read some data back +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(8, buffer, 4); +	ensure("Read length correct", 4 == len); +	ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); +	ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); +	ensure("Read didn't overwrite", 'X' == buffer[4]); + +	// Read whole thing +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(0, buffer, sizeof(buffer)); +	ensure("Read length correct", (2 * str1_len) == len); +	ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); +	ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); +	ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<4>() +{ +	set_test_name("BufferArray overwriting"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJ"; + 	char buffer[256]; +	 +	size_t len = ba->write(0, str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1_len, str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// reposition and overwrite +	len = ba->write(8, str2, 4); +	ensure("Overwrite length correct", 4 == len); + +	// Leave position and read verifying content (stale really from seek() days) +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(12, buffer, 4); +	ensure("Read length correct", 4 == len); +	ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); +	ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); +	ensure("Read didn't overwrite", 'X' == buffer[4]); + +	// reposition and check +	len = ba->read(6, buffer, 8); +	ensure("Read length correct.2", 8 == len); +	ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); +	ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); +	ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]); +	ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]); +	ensure("Read didn't overwrite.7", 'X' == buffer[8]); + +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<5>() +{ +	set_test_name("BufferArray multiple writes - sequential reads"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); + 	char buffer[256]; +	 +	size_t len = ba->write(0, str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1_len, str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// read some data back +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(8, buffer, 4); +	ensure("Read length correct", 4 == len); +	ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); +	ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); +	ensure("Read didn't overwrite", 'X' == buffer[4]); + +	// Read some more without repositioning +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(12, buffer, sizeof(buffer)); +	ensure("Read length correct", (str1_len - 2) == len); +	ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); +	ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<6>() +{ +	set_test_name("BufferArray overwrite spanning blocks and appending"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJKLMNOPQRST"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; +	 +	size_t len = ba->write(0, str1, str1_len); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", str1_len == ba->size()); + +	// again... +	len = ba->write(str1_len, str1, strlen(str1)); +	ensure("Wrote length correct", str1_len == len); +	ensure("Recorded size correct", (2 * str1_len) == ba->size()); + +	// reposition and overwrite +	len = ba->write(8, str2, str2_len); +	ensure("Overwrite length correct", str2_len == len); + +	// Leave position and read verifying content +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(8 + str2_len, buffer, 0); +	ensure("Read length correct", 0 == len); +	ensure("Read didn't overwrite", 'X' == buffer[0]); + +	// reposition and check +	len = ba->read(0, buffer, sizeof(buffer)); +	ensure("Read length correct.2", (str1_len + str2_len - 2) == len); +	ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); +	ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); +	ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]); + +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<7>() +{ +	set_test_name("BufferArray overwrite spanning blocks and sequential writes"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJKLMNOPQRST"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; + +	// 2x str1 +	size_t len = ba->write(0, str1, str1_len); +	len = ba->write(str1_len, str1, str1_len); + +	// reposition and overwrite +	len = ba->write(6, str2, 2); +	ensure("Overwrite length correct", 2 == len); + +	len = ba->write(8, str2, 2); +	ensure("Overwrite length correct.2", 2 == len); + +	len = ba->write(10, str2, 2); +	ensure("Overwrite length correct.3", 2 == len); + +	// append some data +	len = ba->append(str2, str2_len); +	ensure("Append length correct", str2_len == len); + +	// append some more +	void * out_buf(ba->appendBufferAlloc(str1_len)); +	memcpy(out_buf, str1, str1_len); + +	// And some final writes +	len = ba->write(3 * str1_len + str2_len, str2, 2); +	ensure("Write length correct.2", 2 == len); +	 +	// Check contents +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(0, buffer, sizeof(buffer)); +	ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); +	ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); +	ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); +	ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2)); +	ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2)); +	ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2)); +	ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); +	ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len)); +	ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2)); +	ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<8>() +{ +	set_test_name("BufferArray zero-length appendBufferAlloc"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArray * ba = new BufferArray(); + +	// write some data to the buffer +	char str1[] = "abcdefghij"; +	size_t str1_len(strlen(str1)); +	char str2[] = "ABCDEFGHIJKLMNOPQRST"; +	size_t str2_len(strlen(str2)); + 	char buffer[256]; + +	// 2x str1 +	size_t len = ba->write(0, str1, str1_len); +	len = ba->write(str1_len, str1, str1_len); + +	// zero-length allocate (we allow this with a valid pointer returned) +	void * out_buf(ba->appendBufferAlloc(0)); +	ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); + +	// Do it again +	void * out_buf2(ba->appendBufferAlloc(0)); +	ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); +	ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); + +	// And some final writes +	len = ba->write(2 * str1_len, str2, str2_len); +	 +	// Check contents +	memset(buffer, 'X', sizeof(buffer)); +	len = ba->read(0, buffer, sizeof(buffer)); +	ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); +	ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); +	ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); +	ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); +	ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]); +	 +	// release the implicit reference, causing the object to be released +	ba->release(); + +	// make sure we didn't leak any memory +	ensure("All memory released", mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + + +#endif  // TEST_LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp new file mode 100755 index 0000000000..831c901b9d --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -0,0 +1,304 @@ +/**  + * @file test_bufferstream.hpp + * @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCORE_BUFFER_STREAM_H_ +#define TEST_LLCORE_BUFFER_STREAM_H_ + +#include "bufferstream.h" + +#include <iostream> + +#include "test_allocator.h" +#include "llsd.h" +#include "llsdserialize.h" + + +using namespace LLCore; + + +namespace tut +{ + +struct BufferStreamTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<BufferStreamTestData> BufferStreamTestGroupType; +typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; +BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); +typedef BufferArrayStreamBuf::traits_type tst_traits_t; + + +template <> template <> +void BufferStreamTestObjectType::test<1>() +{ +	set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// Not much will work with a NULL +	ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); +	ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow()); +	ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c')); +	ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1); +	ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c')); +	ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0); +	ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1)); +	 +	// release the implicit reference, causing the object to be released +	delete bsb; +	bsb = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<2>() +{ +	set_test_name("BufferArrayStream construction with NULL BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	BufferArrayStream * bas = new BufferArrayStream(NULL); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// Not much will work with a NULL here +	ensure("eof() is false on NULL", ! bas->eof()); +	ensure("fail() is false on NULL", ! bas->fail()); +	ensure("good() on NULL", bas->good()); +		    +	// release the implicit reference, causing the object to be released +	delete bas; +	bas = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<3>() +{ +	set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; +	BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// I can release my ref on the BA +	ba->release(); +	ba = NULL; +	 +	// release the implicit reference, causing the object to be released +	delete bsb; +	bsb = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<4>() +{ +	set_test_name("BufferArrayStream construction with empty BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; + +	{ +		// create a new ref counted object with an implicit reference +		BufferArrayStream bas(ba); +		ensure("Memory being used", mMemTotal < GetMemTotal()); +	} + +	// release the implicit reference, causing the object to be released +	ba->release(); +	ba = NULL; +	 +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<5>() +{ +	set_test_name("BufferArrayStreamBuf construction with real BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; +	const char * content("This is a string.  A fragment."); +	const size_t c_len(strlen(content)); +	ba->append(content, c_len); + +	// Creat an adapter for the BufferArray +	BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// I can release my ref on the BA +	ba->release(); +	ba = NULL; +	 +	// Various static state +	ensure("underflow() returns 'T'", bsb->underflow() == 'T'); +	ensure("underflow() returns 'T' again", bsb->underflow() == 'T'); +	ensure("uflow() returns 'T'", bsb->uflow() == 'T'); +	ensure("uflow() returns 'h'", bsb->uflow() == 'h'); +	ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i')); +	ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T')); +	ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h'); +	ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1)); +	ensure("overflow() appends", bsb->overflow('c') == 'c'); +	ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1)); +	ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4); +	ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5)); +	ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0)); +	ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4)); +	ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4)); +	ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4)); +	ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!')); +	 +	// release the implicit reference, causing the object to be released +	delete bsb; +	bsb = NULL; + +	// make sure we didn't leak any memory +	ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<6>() +{ +	set_test_name("BufferArrayStream construction with real BufferArray"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; +	//const char * content("This is a string.  A fragment."); +	//const size_t c_len(strlen(content)); +	//ba->append(content, strlen(content)); + +	{ +		// Creat an adapter for the BufferArray +		BufferArrayStream bas(ba); +		ensure("Memory being used", mMemTotal < GetMemTotal()); + +		// Basic operations +		bas << "Hello" << 27 << "."; +		ensure("BA length 8", ba->size() == 8); + +		std::string str; +		bas >> str; +		ensure("reads correctly", str == "Hello27."); +	} + +	// release the implicit reference, causing the object to be released +	ba->release(); +	ba = NULL; + +	// make sure we didn't leak any memory +	// ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +	// static U64 mem = GetMemTotal(); +} + + +template <> template <> +void BufferStreamTestObjectType::test<7>() +{ +	set_test_name("BufferArrayStream with LLSD serialization"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted BufferArray with implicit reference +	BufferArray * ba = new BufferArray; + +	{ +		// Creat an adapter for the BufferArray +		BufferArrayStream bas(ba); +		ensure("Memory being used", mMemTotal < GetMemTotal()); + +		// LLSD +		LLSD llsd = LLSD::emptyMap(); + +		llsd["int"] = LLSD::Integer(3); +		llsd["float"] = LLSD::Real(923289.28992); +		llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj"); + +		LLSD llsd_map = LLSD::emptyMap(); +		llsd_map["int"] = LLSD::Integer(-2889); +		llsd_map["float"] = LLSD::Real(2.37829e32); +		llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP"); + +		llsd["map"] = llsd_map; +		 +		// Serialize it +		LLSDSerialize::toXML(llsd, bas); + +		std::string str; +		bas >> str; +		// std::cout << "SERIALIZED LLSD:  " << str << std::endl; +		ensure("Extracted string has reasonable length", str.size() > 60); +	} + +	// release the implicit reference, causing the object to be released +	ba->release(); +	ba = NULL; + +	// make sure we didn't leak any memory +	// ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +}  // end namespace tut + + +#endif  // TEST_LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp new file mode 100755 index 0000000000..ce0d19b058 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -0,0 +1,108 @@ +/**  + * @file test_httpheaders.hpp + * @brief unit tests for the LLCore::HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCORE_HTTP_HEADERS_H_ +#define TEST_LLCORE_HTTP_HEADERS_H_ + +#include "httpheaders.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpHeadersTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<HttpHeadersTestData> HttpHeadersTestGroupType; +typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; +HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); + +template <> template <> +void HttpHeadersTestObjectType::test<1>() +{ +	set_test_name("HttpHeaders construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpHeaders * headers = new HttpHeaders(); +	ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); +	ensure("Nothing in headers", 0 == headers->mHeaders.size()); + +	// release the implicit reference, causing the object to be released +	headers->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpHeadersTestObjectType::test<2>() +{ +	set_test_name("HttpHeaders construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpHeaders * headers = new HttpHeaders(); +	 +	{ +		// Append a few strings +		std::string str1("Pragma:"); +		headers->mHeaders.push_back(str1); +		std::string str2("Accept: application/json"); +		headers->mHeaders.push_back(str2); +	 +		ensure("Headers retained", 2 == headers->mHeaders.size()); +		ensure("First is first", headers->mHeaders[0] == str1); +		ensure("Second is second", headers->mHeaders[1] == str2); +	} +	 +	// release the implicit reference, causing the object to be released +	headers->release(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + + +#endif  // TEST_LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp new file mode 100755 index 0000000000..17b1a96878 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -0,0 +1,125 @@ +/**  + * @file test_httpoperation.hpp + * @brief unit tests for the LLCore::HttpOperation-derived classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCORE_HTTP_OPERATION_H_ +#define TEST_LLCORE_HTTP_OPERATION_H_ + +#include "_httpoperation.h" +#include "httphandler.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +class TestHandler : public LLCore::HttpHandler +{ +public: +	virtual void onCompleted(HttpHandle, HttpResponse *) +		{ +			std::cout << "TestHandler::onCompleted() invoked" << std::endl; +		} +	 +}; + + +}  // end namespace anonymous + + +namespace tut +{ +	struct HttpOperationTestData +	{ +		// the test objects inherit from this so the member functions and variables +		// can be referenced directly inside of the test functions. +		size_t mMemTotal; +	}; + +	typedef test_group<HttpOperationTestData> HttpOperationTestGroupType; +	typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; +	HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); + +	template <> template <> +	void HttpOperationTestObjectType::test<1>() +	{ +		set_test_name("HttpOpNull construction"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// create a new ref counted object with an implicit reference +		HttpOpNull * op = new HttpOpNull(); +		ensure(op->getRefCount() == 1); +		ensure(mMemTotal < GetMemTotal()); +		 +		// release the implicit reference, causing the object to be released +		op->release(); + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void HttpOperationTestObjectType::test<2>() +	{ +		set_test_name("HttpOpNull construction with handlers"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// Get some handlers +		TestHandler * h1 = new TestHandler(); +		 +		// create a new ref counted object with an implicit reference +		HttpOpNull * op = new HttpOpNull(); + +		// Add the handlers +		op->setReplyPath(NULL, h1); + +		// Check ref count +		ensure(op->getRefCount() == 1); + +		// release the reference, releasing the operation but +		// not the handlers. +		op->release(); +		op = NULL; +		ensure(mMemTotal != GetMemTotal()); +		 +		// release the handlers +		delete h1; +		h1 = NULL; + +		ensure(mMemTotal == GetMemTotal()); +	} + +} + +#endif  // TEST_LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp new file mode 100755 index 0000000000..e5488cf941 --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -0,0 +1,2670 @@ +/**  + * @file test_httprequest.hpp + * @brief unit tests for the LLCore::HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCORE_HTTP_REQUEST_H_ +#define TEST_LLCORE_HTTP_REQUEST_H_ + +#include "httprequest.h" +#include "bufferarray.h" +#include "httphandler.h" +#include "httpheaders.h" +#include "httpresponse.h" +#include "httpoptions.h" +#include "_httpservice.h" +#include "_httprequestqueue.h" + +#include <curl/curl.h> +#include <boost/regex.hpp> +#include <sstream> + +#include "test_allocator.h" +#include "llcorehttp_test.h" + + +using namespace LLCoreInt; + + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec); + +#endif + +} + +namespace tut +{ + +struct HttpRequestTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t			mMemTotal; +	int				mHandlerCalls; +	HttpStatus		mStatus; +}; + +class TestHandler2 : public LLCore::HttpHandler +{ +public: +	TestHandler2(HttpRequestTestData * state, +				 const std::string & name) +		: mState(state), +		  mName(name), +		  mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) +		{} +	 +	virtual void onCompleted(HttpHandle handle, HttpResponse * response) +		{ +			if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle) +			{ +				ensure("Expected handle received in handler", mExpectHandle == handle); +			} +			ensure("Handler got a response", NULL != response); +			if (response && mState) +			{ +				const HttpStatus actual_status(response->getStatus()); +				std::ostringstream test; +				test << "Expected HttpStatus received in response.  Wanted:  " +					 << mState->mStatus.toHex() << " Received:  " << actual_status.toHex(); +				ensure(test.str().c_str(), actual_status == mState->mStatus); +			} +			if (mState) +			{ +				mState->mHandlerCalls++; +			} +			if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty()) +			{ +				ensure("Response required with header check", response != NULL); +				HttpHeaders * header(response->getHeaders());	// Will not hold onto this +				ensure("Some quantity of headers returned", header != NULL); + +				if (! mHeadersRequired.empty()) +				{ +					for (int i(0); i < mHeadersRequired.size(); ++i) +					{ +						bool found = false; +						for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); +							 header->mHeaders.end() != iter; +							 ++iter) +						{ +							if (boost::regex_match(*iter, mHeadersRequired[i])) +							{ +								found = true; +								break; +							} +						} +						std::ostringstream str; +						str << "Required header # " << i << " found in response"; +						ensure(str.str(), found); +					} +				} + +				if (! mHeadersDisallowed.empty()) +				{ +					for (int i(0); i < mHeadersDisallowed.size(); ++i) +					{ +						for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); +							 header->mHeaders.end() != iter; +							 ++iter) +						{ +							if (boost::regex_match(*iter, mHeadersDisallowed[i])) +							{ +								std::ostringstream str; +								str << "Disallowed header # " << i << " not found in response"; +								ensure(str.str(), false); +							} +						} +					} +				} +			} +			 +			if (! mCheckContentType.empty()) +			{ +				ensure("Response required with content type check", response != NULL); +				std::string con_type(response->getContentType()); +				ensure("Content-Type as expected (" + mCheckContentType + ")", +					   mCheckContentType == con_type); +			} + +			// std::cout << "TestHandler2::onCompleted() invoked" << std::endl; +		} + +	HttpRequestTestData * mState; +	std::string mName; +	HttpHandle mExpectHandle; +	std::string mCheckContentType; +	std::vector<boost::regex> mHeadersRequired; +	std::vector<boost::regex> mHeadersDisallowed; +}; + +typedef test_group<HttpRequestTestData> HttpRequestTestGroupType; +typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; +HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); + +template <> template <> +void HttpRequestTestObjectType::test<1>() +{ +	ScopedCurlInit ready; +	 +	set_test_name("HttpRequest construction"); + +	HttpRequest * req = NULL; + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory being used", mMemTotal < GetMemTotal()); +		 +		// release the request object +		delete req; +		req = NULL; + +		HttpRequest::destroyService(); + +		// make sure we didn't leak any memory +		ensure("Memory returned", mMemTotal == GetMemTotal()); +	} +	catch (...) +	{ +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + +template <> template <> +void HttpRequestTestObjectType::test<2>() +{ +	ScopedCurlInit ready; + +	set_test_name("HttpRequest and Null Op queued"); + +	HttpRequest * req = NULL; + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory being used", mMemTotal < GetMemTotal()); + +		// Issue a NoOp +		HttpHandle handle = req->requestNoOp(NULL); +		ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID); +		 +		// release the request object +		delete req; +		req = NULL; + +		// We're still holding onto the operation which is +		// sitting, unserviced, on the request queue so... +		ensure("Memory being used 2", mMemTotal < GetMemTotal()); + +		// Request queue should have two references:  global singleton & service object +		ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + +		// Okay, tear it down +		HttpRequest::destroyService(); +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory returned", mMemTotal == GetMemTotal()); +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<3>() +{ +	ScopedCurlInit ready; +	 +	set_test_name("HttpRequest NoOp + Stop execution"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	 +	try +	{ +		 +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a NoOp +		HttpHandle handle = req->requestNoOp(&handler); +		ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(20); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 100; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + +template <> template <> +void HttpRequestTestObjectType::test<4>() +{ +	ScopedCurlInit ready; + +	set_test_name("2 HttpRequest instances, one thread"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	TestHandler2 handler1(this, "handler1"); +	TestHandler2 handler2(this, "handler2"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req1 = NULL; +	HttpRequest * req2 = NULL; +	 +	try +	{ +		 +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req1 = new HttpRequest(); +		req2 = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue some NoOps +		HttpHandle handle = req1->requestNoOp(&handler1); +		ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); +		handler1.mExpectHandle = handle; + +		handle = req2->requestNoOp(&handler2); +		ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); +		handler2.mExpectHandle = handle; + +		// Run the notification pump. +		int count(0); +		int limit(20); +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req1->update(1000000); +			req2->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 2); + +		// Okay, request a shutdown of the servicing thread +		handle = req2->requestStopThread(&handler2); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +		handler2.mExpectHandle = handle; +	 +		// Run the notification pump again +		count = 0; +		limit = 100; +		while (count++ < limit && mHandlerCalls < 3) +		{ +			req1->update(1000000); +			req2->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 3); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req1; +		req1 = NULL; +		delete req2; +		req2 = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 3 == mHandlerCalls); +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +	} +	catch (...) +	{ +		stop_thread(req1); +		delete req1; +		delete req2; +		HttpRequest::destroyService(); +		throw; +	} +} + +template <> template <> +void HttpRequestTestObjectType::test<5>() +{ +	ScopedCurlInit ready; +	 +	set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	 +	try +	{ +		 +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a Spin +		HttpHandle handle = req->requestSpin(1); +		ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Issue a NoOp +		handle = req->requestNoOp(&handler); +		ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("NoOp notification received", mHandlerCalls == 1); + +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); + +		// Check memory usage +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +		// This memory test should work but could give problems as it +		// relies on the worker thread picking up a friendly request +		// to shutdown.  Doing so, it drops references to things and +		// we should go back to where we started.  If it gives you +		// problems, look into the code before commenting things out. +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<6>() +{ +	ScopedCurlInit ready; +	 +	set_test_name("HttpRequest Spin + NoOp + hard termination"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	 +	try +	{ +		 +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a Spin +		HttpHandle handle = req->requestSpin(0);		// Hard spin +		ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Issue a NoOp +		handle = req->requestNoOp(&handler); +		ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("No notifications received", mHandlerCalls == 0); + +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); + +		// Check memory usage +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		// ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +		// This memory test won't work because we're killing the thread +		// hard with the hard spinner.  There's no opportunity to join +		// nicely so many things leak or get destroyed unilaterally. +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ +	ScopedCurlInit ready; + +	set_test_name("HttpRequest GET to dead port + Stop execution"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * opts = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		opts = new HttpOptions(); +		opts->setRetries(1);			// Don't try for too long - default retries take about 18S +		 +		// Issue a GET that can't connect +		mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +													 0U, +													 "http://127.0.0.1:2/nothing/here", +													 0, +													 0, +													 opts, +													 NULL, +													 &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(50);				// With one retry, should fail quickish +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 100; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); + +		// release options +		opts->release(); +		opts = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) +		// Can't do this on any platform anymore, the LL logging system holds +		// on to memory and produces what looks like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		if (opts) +		{ +			opts->release(); +			opts = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<8>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest GET to real service"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a GET that *can* connect +		mStatus = HttpStatus(200); +		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base, +											NULL, +											NULL, +											&handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<9>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest GET with Range: header to real service"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a GET that *can* connect +		mStatus = HttpStatus(200); +		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +													 0U, +													 url_base, +													 0, +													 0, +													 NULL, +													 NULL, +													 &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<10>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest PUT to real service"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	BufferArray * body = new BufferArray; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a GET that *can* connect +		static const char * body_text("Now is the time for all good men..."); +		body->append(body_text, strlen(body_text)); +		mStatus = HttpStatus(200); +		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base, +											body, +											NULL, +											NULL, +											&handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); + +		// Lose the request body +		body->release(); +		body = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) +		// Can't do this on any platform anymore, the LL logging system holds +		// on to memory and produces what looks like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		if (body) +		{ +			body->release(); +		} +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + +template <> template <> +void HttpRequestTestObjectType::test<11>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest POST to real service"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	BufferArray * body = new BufferArray; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a GET that *can* connect +		static const char * body_text("Now is the time for all good men..."); +		body->append(body_text, strlen(body_text)); +		mStatus = HttpStatus(200); +		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, +											 0U, +											 url_base, +											 body, +											 NULL, +											 NULL, +											 &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); + +		// Lose the request body +		body->release(); +		body = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		if (body) +		{ +			body->release(); +		} +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest GET with some tracing"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); + +		// Enable tracing +		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a GET that *can* connect +		mStatus = HttpStatus(200); +		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +													 0U, +													 url_base, +													 0, +													 0, +													 NULL, +													 NULL, +													 &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0	// defined(WIN32) +		// Can't do this on any platform anymore, the LL logging system holds +		// on to memory and produces what looks like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<13>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); +	 +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest GET with returned headers"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +	handler.mHeadersRequired.reserve(20);				// Avoid memory leak test failure +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * opts = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); + +		// Enable tracing +		HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		opts = new HttpOptions(); +		opts->setWantHeaders(true); +		 +		// Issue a GET that succeeds +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase)); +		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +													 0U, +													 url_base, +													 0, +													 0, +													 opts, +													 NULL, +													 &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// release options +		opts->release(); +		opts = NULL; + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		if (opts) +		{ +			opts->release(); +			opts = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +template <> template <> +void HttpRequestTestObjectType::test<14>() +{ +	ScopedCurlInit ready; + +	set_test_name("HttpRequest GET timeout"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); +	std::string url_base(get_base_url() + "/sleep/");	// path to a 30-second sleep +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * opts = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		opts = new HttpOptions(); +		opts->setRetries(0);			// Don't retry +		opts->setTimeout(2); +		 +		// Issue a GET that sleeps +		mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); +		HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +													 0U, +													 url_base, +													 0, +													 0, +													 opts, +													 NULL, +													 &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(50);				// With one retry, should fail quickish +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 100; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); + +		// release options +		opts->release(); +		opts = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		if (opts) +		{ +			opts->release(); +			opts = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + +// Test retrieval of Content-Type/Content-Encoding headers +template <> template <> +void HttpRequestTestObjectType::test<15>() +{ +	ScopedCurlInit ready; + +	std::string url_base(get_base_url()); +	// std::cerr << "Base:  "  << url_base << std::endl; +	 +	set_test_name("HttpRequest GET with Content-Type"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// Load and clear the string setting to preload std::string object +	// for memory return tests. +	handler.mCheckContentType = "application/llsd+xml"; +	handler.mCheckContentType.clear(); +		 +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); +		ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + +		// Issue a GET that *can* connect +		mStatus = HttpStatus(200); +		handler.mCheckContentType = "application/llsd+xml"; +		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base, +											NULL, +											NULL, +											&handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mCheckContentType.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	 +		ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) +		// Can only do this memory test on Windows.  On other platforms, +		// the LL logging system holds on to memory and produces what looks +		// like memory leaks... +	 +		// printf("Old mem:  %d, New mem:  %d\n", mMemTotal, GetMemTotal()); +		ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif +	} +	catch (...) +	{ +		stop_thread(req); +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +// Test header generation on GET requests +template <> template <> +void HttpRequestTestObjectType::test<16>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); + +	std::string url_base(get_base_url()); +	 +	set_test_name("Header generation for HttpRequest GET"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * options = NULL; +	HttpHeaders * headers = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); + +		// options set +		options = new HttpOptions(); +		options->setWantHeaders(true); +		 +		// Issue a GET that *can* connect +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base + "reflect/", +											options, +											NULL, +											&handler); +		ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Do a texture-style fetch +		headers = new HttpHeaders; +		headers->mHeaders.push_back("Accept: image/x-j2c"); +		 +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, +										  0U, +										  url_base + "reflect/", +										  0, +										  47, +										  options, +										  headers, +										  &handler); +		ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 2); + + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 3) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 3); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release options & headers +		if (options) +		{ +			options->release(); +		} +		options = NULL; + +		if (headers) +		{ +			headers->release(); +		} +		headers = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	} +	catch (...) +	{ +		stop_thread(req); +		if (options) +		{ +			options->release(); +			options = NULL; +		} +		if (headers) +		{ +			headers->release(); +			headers = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +// Test header generation on POST requests +template <> template <> +void HttpRequestTestObjectType::test<17>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); + +	std::string url_base(get_base_url()); +	 +	set_test_name("Header generation for HttpRequest POST"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * options = NULL; +	HttpHeaders * headers = NULL; +	BufferArray * ba = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); + +		// options set +		options = new HttpOptions(); +		options->setWantHeaders(true); + +		// And a buffer array +		const char * msg("It was the best of times, it was the worst of times."); +		ba = new BufferArray; +		ba->append(msg, strlen(msg)); +			 +		// Issue a default POST +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); +		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, +											 0U, +											 url_base + "reflect/", +											 ba, +											 options, +											 NULL, +											 &handler); +		ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); +		ba->release(); +		ba = NULL; +			 +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release options & headers +		if (options) +		{ +			options->release(); +		} +		options = NULL; + +		if (headers) +		{ +			headers->release(); +		} +		headers = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	} +	catch (...) +	{ +		stop_thread(req); +		if (ba) +		{ +			ba->release(); +			ba = NULL; +		} +		if (options) +		{ +			options->release(); +			options = NULL; +		} +		if (headers) +		{ +			headers->release(); +			headers = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +// Test header generation on PUT requests +template <> template <> +void HttpRequestTestObjectType::test<18>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); + +	std::string url_base(get_base_url()); +	 +	set_test_name("Header generation for HttpRequest PUT"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * options = NULL; +	HttpHeaders * headers = NULL; +	BufferArray * ba = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); + +		// options set +		options = new HttpOptions(); +		options->setWantHeaders(true); + +		// And a buffer array +		const char * msg("It was the best of times, it was the worst of times."); +		ba = new BufferArray; +		ba->append(msg, strlen(msg)); +			 +		// Issue a default PUT +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase)); +		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base + "reflect/", +											ba, +											options, +											NULL, +											&handler); +		ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); +		ba->release(); +		ba = NULL; +			 +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release options & headers +		if (options) +		{ +			options->release(); +		} +		options = NULL; + +		if (headers) +		{ +			headers->release(); +		} +		headers = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	} +	catch (...) +	{ +		stop_thread(req); +		if (ba) +		{ +			ba->release(); +			ba = NULL; +		} +		if (options) +		{ +			options->release(); +			options = NULL; +		} +		if (headers) +		{ +			headers->release(); +			headers = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +// Test header generation on GET requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<19>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); + +	std::string url_base(get_base_url()); +	 +	set_test_name("Header generation for HttpRequest GET with header overrides"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * options = NULL; +	HttpHeaders * headers = NULL; + +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); + +		// options set +		options = new HttpOptions(); +		options->setWantHeaders(true); + +		// headers +		headers = new HttpHeaders; +		headers->mHeaders.push_back("Keep-Alive: 120"); +		headers->mHeaders.push_back("Accept-encoding: deflate"); +		headers->mHeaders.push_back("Accept: text/plain"); + +		// Issue a GET with modified headers +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base + "reflect/", +											options, +											headers, +											&handler); +		ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release options & headers +		if (options) +		{ +			options->release(); +		} +		options = NULL; + +		if (headers) +		{ +			headers->release(); +		} +		headers = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	} +	catch (...) +	{ +		stop_thread(req); +		if (options) +		{ +			options->release(); +			options = NULL; +		} +		if (headers) +		{ +			headers->release(); +			headers = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +// Test header generation on POST requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<20>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); + +	std::string url_base(get_base_url()); +	 +	set_test_name("Header generation for HttpRequest POST with header overrides"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * options = NULL; +	HttpHeaders * headers = NULL; +	BufferArray * ba = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); + +		// options set +		options = new HttpOptions(); +		options->setWantHeaders(true); + +		// headers +		headers = new HttpHeaders(); +		headers->mHeaders.push_back("keep-Alive: 120"); +		headers->mHeaders.push_back("Accept:  text/html"); +		headers->mHeaders.push_back("content-type:  application/llsd+xml"); +		headers->mHeaders.push_back("cache-control: no-store"); +		 +		// And a buffer array +		const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>"); +		ba = new BufferArray; +		ba->append(msg, strlen(msg)); +			 +		// Issue a default POST +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); +		HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, +											 0U, +											 url_base + "reflect/", +											 ba, +											 options, +											 headers, +											 &handler); +		ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); +		ba->release(); +		ba = NULL; +			 +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release options & headers +		if (options) +		{ +			options->release(); +		} +		options = NULL; + +		if (headers) +		{ +			headers->release(); +		} +		headers = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	} +	catch (...) +	{ +		stop_thread(req); +		if (ba) +		{ +			ba->release(); +			ba = NULL; +		} +		if (options) +		{ +			options->release(); +			options = NULL; +		} +		if (headers) +		{ +			headers->release(); +			headers = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +// Test header generation on PUT requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<21>() +{ +	ScopedCurlInit ready; + +	// Warmup boost::regex to pre-alloc memory for memory size tests +	boost::regex warmup("askldjflasdj;f", boost::regex::icase); +	boost::regex_match("akl;sjflajfk;ajsk", warmup); + +	std::string url_base(get_base_url()); +	 +	set_test_name("Header generation for HttpRequest PUT with header overrides"); + +	// Handler can be stack-allocated *if* there are no dangling +	// references to it after completion of this method. +	// Create before memory record as the string copy will bump numbers. +	TestHandler2 handler(this, "handler"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); +	mHandlerCalls = 0; + +	HttpRequest * req = NULL; +	HttpOptions * options = NULL; +	HttpHeaders * headers = NULL; +	BufferArray * ba = NULL; +	 +	try +	{ +		// Get singletons created +		HttpRequest::createService(); +		 +		// Start threading early so that thread memory is invariant +		// over the test. +		HttpRequest::startThread(); + +		// create a new ref counted object with an implicit reference +		req = new HttpRequest(); + +		// options set +		options = new HttpOptions(); +		options->setWantHeaders(true); + +		// headers +		headers = new HttpHeaders; +		headers->mHeaders.push_back("content-type:  text/plain"); +		headers->mHeaders.push_back("content-type:  text/html"); +		headers->mHeaders.push_back("content-type:  application/llsd+xml"); +		 +		// And a buffer array +		const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>"); +		ba = new BufferArray; +		ba->append(msg, strlen(msg)); +			 +		// Issue a default PUT +		mStatus = HttpStatus(200); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); +		handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase)); +		handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase)); +		HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, +											0U, +											url_base + "reflect/", +											ba, +											options, +											headers, +											&handler); +		ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); +		ba->release(); +		ba = NULL; +			 +		// Run the notification pump. +		int count(0); +		int limit(10); +		while (count++ < limit && mHandlerCalls < 1) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Request executed in reasonable time", count < limit); +		ensure("One handler invocation for request", mHandlerCalls == 1); + + +		// Okay, request a shutdown of the servicing thread +		mStatus = HttpStatus(); +		handler.mHeadersRequired.clear(); +		handler.mHeadersDisallowed.clear(); +		handle = req->requestStopThread(&handler); +		ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); +	 +		// Run the notification pump again +		count = 0; +		limit = 10; +		while (count++ < limit && mHandlerCalls < 2) +		{ +			req->update(1000000); +			usleep(100000); +		} +		ensure("Second request executed in reasonable time", count < limit); +		ensure("Second handler invocation", mHandlerCalls == 2); + +		// See that we actually shutdown the thread +		count = 0; +		limit = 10; +		while (count++ < limit && ! HttpService::isStopped()) +		{ +			usleep(100000); +		} +		ensure("Thread actually stopped running", HttpService::isStopped()); +	 +		// release options & headers +		if (options) +		{ +			options->release(); +		} +		options = NULL; + +		if (headers) +		{ +			headers->release(); +		} +		headers = NULL; +		 +		// release the request object +		delete req; +		req = NULL; + +		// Shut down service +		HttpRequest::destroyService(); +	} +	catch (...) +	{ +		stop_thread(req); +		if (ba) +		{ +			ba->release(); +			ba = NULL; +		} +		if (options) +		{ +			options->release(); +			options = NULL; +		} +		if (headers) +		{ +			headers->release(); +			headers = NULL; +		} +		delete req; +		HttpRequest::destroyService(); +		throw; +	} +} + + +}  // end namespace tut + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec) +{ +	Sleep((DWORD) (usec / 1000UL)); +} + +#endif + +} + +#endif  // TEST_LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp new file mode 100755 index 0000000000..1de2d8f9ab --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -0,0 +1,186 @@ +/**  + * @file test_httprequestqueue.hpp + * @brief unit tests for the LLCore::HttpRequestQueue class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCORE_HTTP_REQUESTQUEUE_H_ +#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_ + +#include "_httprequestqueue.h" + +#include <iostream> + +#include "test_allocator.h" +#include "_httpoperation.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpRequestqueueTestData +{ +	// the test objects inherit from this so the member functions and variables +	// can be referenced directly inside of the test functions. +	size_t mMemTotal; +}; + +typedef test_group<HttpRequestqueueTestData> HttpRequestqueueTestGroupType; +typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; +HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); + +template <> template <> +void HttpRequestqueueTestObjectType::test<1>() +{ +	set_test_name("HttpRequestQueue construction"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); +	 +	ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// release the implicit reference, causing the object to be released +	HttpRequestQueue::term(); + +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<2>() +{ +	set_test_name("HttpRequestQueue refcount works"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); + +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); +	rq->addRef(); +	 +	// release the singleton, hold on to the object +	HttpRequestQueue::term(); +	 +	ensure("One ref after term() called", rq->getRefCount() == 1); +	ensure("Memory being used", mMemTotal < GetMemTotal()); + +	// Drop ref +	rq->release(); +	 +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<3>() +{ +	set_test_name("HttpRequestQueue addOp/fetchOp work"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); + +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + +	HttpOperation * op = new HttpOpNull(); + +	rq->addOp(op);		// transfer my refcount + +	op = rq->fetchOp(true);		// Potentially hangs the test on failure +	ensure("One goes in, one comes out", NULL != op); +	op->release(); + +	op = rq->fetchOp(false); +	ensure("Better not be two of them", NULL == op); +	 +	// release the singleton, hold on to the object +	HttpRequestQueue::term(); +	 +	// make sure we didn't leak any memory +	ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<4>() +{ +	set_test_name("HttpRequestQueue addOp/fetchAll work"); + +	// record the total amount of dynamically allocated memory +	mMemTotal = GetMemTotal(); + +	// create a new ref counted object with an implicit reference +	HttpRequestQueue::init(); + +	HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + +	HttpOperation * op = new HttpOpNull(); +	rq->addOp(op);		// transfer my refcount + +	op = new HttpOpNull(); +	rq->addOp(op);		// transfer my refcount + +	op = new HttpOpNull(); +	rq->addOp(op);		// transfer my refcount +	 +	{ +		HttpRequestQueue::OpContainer ops; +		rq->fetchAll(true, ops);		// Potentially hangs the test on failure +		ensure("Three go in, three come out", 3 == ops.size()); + +		op = rq->fetchOp(false); +		ensure("Better not be any more of them", NULL == op); +	 +		// release the singleton, hold on to the object +		HttpRequestQueue::term(); +	 +		// We're still holding onto the ops. +		ensure(mMemTotal < GetMemTotal()); + +		// Release them +		while (! ops.empty()) +		{ +			HttpOperation * op = ops.front(); +			ops.erase(ops.begin()); +			op->release(); +		} +	} + +	// Should be clean +	ensure("All memory returned", mMemTotal == GetMemTotal()); +} + +}  // end namespace tut + + +#endif  // TEST_LLCORE_HTTP_REQUESTQUEUE_H_ diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp new file mode 100755 index 0000000000..f7b542d3b5 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -0,0 +1,265 @@ +/**  + * @file test_llrefcounted + * @brief unit tests for HttpStatus struct + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ + +#ifndef TEST_HTTP_STATUS_H_ +#define TEST_HTTP_STATUS_H_ + +#include "httpcommon.h" + +#include <curl/curl.h> +#include <curl/multi.h> + +using namespace LLCore; + +namespace tut +{ + +struct HttpStatusTestData +{ +	HttpStatusTestData() +		{} +}; + +typedef test_group<HttpStatusTestData> HttpStatusTestGroupType; +typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; + +HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); + +template <> template <> +void HttpStatusTestObjectType::test<1>() +{ +	set_test_name("HttpStatus construction"); +	 +	// auto allocation fine for this +	HttpStatus status; +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = 0; +	 +	ensure(bool(status)); +	ensure(false == !(status)); + +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = 0; + +	ensure(bool(status)); +	ensure(false == !(status)); +	 +	status.mType = HttpStatus::LLCORE; +	status.mStatus = HE_SUCCESS; +	 +	ensure(bool(status)); +	ensure(false == !(status)); + +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = -1; + +	ensure(false == bool(status)); +	ensure(!(status)); + +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + +	ensure(false == bool(status)); +	ensure(!(status)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<2>() +{ +	set_test_name("HttpStatus memory structure"); + +	// Require that an HttpStatus object can be trivially +	// returned as a function return value in registers. +	// One should fit in an int on all platforms. + +	ensure(sizeof(HttpStatus) <= sizeof(int)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<3>() +{ +	set_test_name("HttpStatus valid error string conversion"); +	 +	HttpStatus status; +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = 0; +	std::string msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(msg.empty()); +	 +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); + +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = CURLM_OUT_OF_MEMORY; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); + +	status.mType = HttpStatus::LLCORE; +	status.mStatus = HE_SHUTTING_DOWN; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); +} + + +template <> template <> +void HttpStatusTestObjectType::test<4>() +{ +	set_test_name("HttpStatus invalid error string conversion"); +	 +	HttpStatus status; +	status.mType = HttpStatus::EXT_CURL_EASY; +	status.mStatus = 32726; +	std::string msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); +	 +	status.mType = HttpStatus::EXT_CURL_MULTI; +	status.mStatus = -470; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); + +	status.mType = HttpStatus::LLCORE; +	status.mStatus = 923; +	msg = status.toString(); +	// std::cout << "Result:  " << msg << std::endl; +	ensure(! msg.empty()); +} + +template <> template <> +void HttpStatusTestObjectType::test<5>() +{ +	set_test_name("HttpStatus equality/inequality testing"); + +	// Make certain equality/inequality tests do not pass +	// through the bool conversion.  Distinct successful +	// and error statuses should compare unequal. + +	HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS); +	HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); +	ensure(status1 != status2); + +	status1.mType = HttpStatus::LLCORE; +	status1.mStatus = HE_REPLY_ERROR; +	status2.mType = HttpStatus::LLCORE; +	status2.mStatus= HE_SHUTTING_DOWN; +	ensure(status1 != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<6>() +{ +	set_test_name("HttpStatus basic HTTP status encoding"); +	 +	HttpStatus status; +	status.mType = 200; +	status.mStatus = HE_SUCCESS; +	std::string msg = status.toString(); +	ensure(msg.empty()); +	ensure(bool(status)); + +	// Normally a success but application says error +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(! msg.empty()); +	ensure(! bool(status)); +	ensure(status.toULong() > 1UL);				// Biggish number, not a bool-to-ulong + +	// Same statuses with distinct success/fail are distinct +	status.mType = 200; +	status.mStatus = HE_SUCCESS; +	HttpStatus status2(200, HE_REPLY_ERROR); +	ensure(status != status2); + +	// Normally an error but application says okay +	status.mType = 406; +	status.mStatus = HE_SUCCESS; +	msg = status.toString(); +	ensure(msg.empty()); +	ensure(bool(status)); + +	// Different statuses but both successful are distinct +	status.mType = 200; +	status.mStatus = HE_SUCCESS; +	status2.mType = 201; +	status2.mStatus = HE_SUCCESS; +	ensure(status != status2); + +	// Different statuses but both failed are distinct +	status.mType = 200; +	status.mStatus = HE_REPLY_ERROR; +	status2.mType = 201; +	status2.mStatus = HE_REPLY_ERROR; +	ensure(status != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<7>() +{ +	set_test_name("HttpStatus HTTP error text strings"); + +	HttpStatus status(100, HE_REPLY_ERROR); +	std::string msg(status.toString()); +	ensure(! msg.empty());				// Should be something +	ensure(msg == "Continue"); + +	status.mStatus = HE_SUCCESS; +	msg = status.toString(); +	ensure(msg.empty());				// Success is empty + +	status.mType = 199; +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "Unknown error"); + +	status.mType = 505;					// Last defined string +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "HTTP Version not supported"); + +	status.mType = 506;					// One beyond +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "Unknown error"); + +	status.mType = 999;					// Last HTTP status +	status.mStatus = HE_REPLY_ERROR; +	msg = status.toString(); +	ensure(msg == "Unknown error"); +} + +} // end namespace tut + +#endif	// TEST_HTTP_STATUS_H + diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py new file mode 100755 index 0000000000..75a3c39ef2 --- /dev/null +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +"""\ +@file   test_llsdmessage_peer.py +@author Nat Goodspeed +@date   2008-10-09 +@brief  This script asynchronously runs the executable (with args) specified on +        the command line, returning its result code. While that executable is +        running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2012, 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$ +""" + +import os +import sys +import time +import select +import getopt +from threading import Thread +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from SocketServer import ThreadingMixIn + +mydir = os.path.dirname(__file__)       # expected to be .../indra/llcorehttp/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +from indra.util.fastest_elementtree import parse as xml_parse +from indra.base import llsd +from testrunner import freeport, run, debug, VERBOSE + +class TestHTTPRequestHandler(BaseHTTPRequestHandler): +    """This subclass of BaseHTTPRequestHandler is to receive and echo +    LLSD-flavored messages sent by the C++ LLHTTPClient. +    """ +    def read(self): +        # The following logic is adapted from the library module +        # SimpleXMLRPCServer.py. +        # Get arguments by reading body of request. +        # We read this in chunks to avoid straining +        # socket.read(); around the 10 or 15Mb mark, some platforms +        # begin to have problems (bug #792570). +        try: +            size_remaining = int(self.headers["content-length"]) +        except (KeyError, ValueError): +            return "" +        max_chunk_size = 10*1024*1024 +        L = [] +        while size_remaining: +            chunk_size = min(size_remaining, max_chunk_size) +            chunk = self.rfile.read(chunk_size) +            L.append(chunk) +            size_remaining -= len(chunk) +        return ''.join(L) +        # end of swiped read() logic + +    def read_xml(self): +        # This approach reads the entire POST data into memory first +        return llsd.parse(self.read()) +##         # This approach attempts to stream in the LLSD XML from self.rfile, +##         # assuming that the underlying XML parser reads its input file +##         # incrementally. Unfortunately I haven't been able to make it work. +##         tree = xml_parse(self.rfile) +##         debug("Finished raw parse") +##         debug("parsed XML tree %s", tree) +##         debug("parsed root node %s", tree.getroot()) +##         debug("root node tag %s", tree.getroot().tag) +##         return llsd.to_python(tree.getroot()) + +    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=200, +                         reason="Your GET operation worked")) + +    def do_POST(self): +        # Read the provided POST data. +        # self.answer(self.read()) +        self.answer(dict(reply="success", status=200, +                         reason=self.read())) + +    def do_PUT(self): +        # Read the provided PUT data. +        # self.answer(self.read()) +        self.answer(dict(reply="success", status=200, +                         reason=self.read())) + +    def answer(self, data, withdata=True): +        debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) +        if "/sleep/" in self.path: +            time.sleep(30) + +        if "fail" not in self.path: +            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) +            if "/reflect/" in self.path: +                self.reflect_headers() +            self.send_header("Content-type", "application/llsd+xml") +            self.send_header("Content-Length", str(len(response))) +            self.send_header("X-LL-Special", "Mememememe"); +            self.end_headers() +            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 +            # strings. We want the longer string. That's why we pass a string +            # pair to get(): the [1] will select the second string, whether it +            # came from self.responses or from our default pair. +            reason = data.get("reason", +                               self.responses.get(status, +                                                  ("fail requested", +                                                   "Your request specified failure status %s " +                                                   "without providing a reason" % status))[1]) +            debug("fail requested: %s: %r", status, reason) +            self.send_error(status, reason) +            if "/reflect/" in self.path: +                self.reflect_headers() +            self.end_headers() + +    def reflect_headers(self): +        for name in self.headers.keys(): +            # print "Header:  %s: %s" % (name, self.headers[name]) +            self.send_header("X-Reflect-" + name, self.headers[name]) + +    if not VERBOSE: +        # When VERBOSE is set, skip both these overrides because they exist to +        # suppress output. + +        def log_request(self, code, size=None): +            # For present purposes, we don't want the request splattered onto +            # stderr, as it would upset devs watching the test run +            pass + +        def log_error(self, format, *args): +            # Suppress error output as well +            pass + +class Server(ThreadingMixIn, HTTPServer): +    # This pernicious flag is on by default in HTTPServer. But proper +    # operation of freeport() absolutely depends on it being off. +    allow_reuse_address = False + +if __name__ == "__main__": +    do_valgrind = False +    path_search = False +    options, args = getopt.getopt(sys.argv[1:], "V", ["valgrind"]) +    for option, value in options: +        if option == "-V" or option == "--valgrind": +            do_valgrind = True + +    # Instantiate a Server(TestHTTPRequestHandler) on the first free port +    # in the specified port range. Doing this inline is better than in a +    # daemon thread: if it blows up here, we'll get a traceback. If it blew up +    # in some other thread, the traceback would get eaten and we'd run the +    # subject test program anyway. +    httpd, port = freeport(xrange(8000, 8020), +                           lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + +    # Pass the selected port number to the subject test program via the +    # environment. We don't want to impose requirements on the test program's +    # command-line parsing -- and anyway, for C++ integration tests, that's +    # performed in TUT code rather than our own. +    os.environ["LL_TEST_PORT"] = str(port) +    debug("$LL_TEST_PORT = %s", port) +    if do_valgrind: +        args = ["valgrind", "--log-file=./valgrind.log"] + args +        path_search = True +    sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp new file mode 100755 index 0000000000..cb4b50287a --- /dev/null +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -0,0 +1,156 @@ +/**  + * @file test_refcounted.hpp + * @brief unit tests for the LLCoreInt::RefCounted class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, 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$ + */ +#ifndef TEST_LLCOREINT_REF_COUNTED_H_ +#define TEST_LLCOREINT_REF_COUNTED_H_ + +#include "_refcounted.h" + +#include "test_allocator.h" + +using namespace LLCoreInt; + +namespace tut +{ +	struct RefCountedTestData +	{ +		// the test objects inherit from this so the member functions and variables +		// can be referenced directly inside of the test functions. +		size_t mMemTotal; +	}; + +	typedef test_group<RefCountedTestData> RefCountedTestGroupType; +	typedef RefCountedTestGroupType::object RefCountedTestObjectType; +	RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); + +	template <> template <> +	void RefCountedTestObjectType::test<1>() +	{ +		set_test_name("RefCounted construction with implicit count"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// create a new ref counted object with an implicit reference +		RefCounted * rc = new RefCounted(true); +		ensure(rc->getRefCount() == 1); + +		// release the implicit reference, causing the object to be released +		rc->release(); + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<2>() +	{ +		set_test_name("RefCounted construction without implicit count"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		// create a new ref counted object with an implicit reference +		RefCounted * rc = new RefCounted(false); +		ensure(rc->getRefCount() == 0); + +		// add a reference +		rc->addRef(); +		ensure(rc->getRefCount() == 1); + +		// release the implicit reference, causing the object to be released +		rc->release(); + +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<3>() +	{ +		set_test_name("RefCounted addRef and release"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		RefCounted * rc = new RefCounted(false); + +		for (int i = 0; i < 1024; ++i) +		{ +			rc->addRef(); +		} + +		ensure(rc->getRefCount() == 1024); + +		for (int i = 0; i < 1024; ++i) +		{ +			rc->release(); +		} + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<4>() +	{ +		set_test_name("RefCounted isLastRef check"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		RefCounted * rc = new RefCounted(true); + +		// with only one reference, isLastRef should be true +		ensure(rc->isLastRef()); + +		// release it to clean up memory +		rc->release(); + +		// make sure we didn't leak any memory +		ensure(mMemTotal == GetMemTotal()); +	} + +	template <> template <> +	void RefCountedTestObjectType::test<5>() +	{ +		set_test_name("RefCounted noRef check"); + +		// record the total amount of dynamically allocated memory +		mMemTotal = GetMemTotal(); + +		RefCounted * rc = new RefCounted(false); + +		// set the noRef +		rc->noRef(); + +		// with only one reference, isLastRef should be true +		ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); + +		// allow this memory leak, but check that we're leaking a known amount +		ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); +	} +} + +#endif	// TEST_LLCOREINT_REF_COUNTED_H_ diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py new file mode 100755 index 0000000000..9a2de71142 --- /dev/null +++ b/indra/llcorehttp/tests/testrunner.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +"""\ +@file   testrunner.py +@author Nat Goodspeed +@date   2009-03-20 +@brief  Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&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$ +""" + +from __future__ import with_statement + +import os +import sys +import re +import errno +import socket + +VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet +# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if +# that construct actually turns on verbosity... +VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) + +if VERBOSE: +    def debug(fmt, *args): +        print fmt % args +        sys.stdout.flush() +else: +    debug = lambda *args: None + +def freeport(portlist, expr): +    """ +    Find a free server port to use. Specifically, evaluate 'expr' (a +    callable(port)) until it stops raising EADDRINUSE exception. + +    Pass: + +    portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the +    range, freeport() lets the socket.error exception propagate. If you want +    unbounded, you could pass itertools.count(baseport), though of course in +    practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain +    the range much more sharply: if we're iterating an absurd number of times, +    probably something else is wrong. + +    expr: a callable accepting a port number, specifically one of the items +    from portlist. If calling that callable raises socket.error with +    EADDRINUSE, freeport() retrieves the next item from portlist and retries. + +    Returns: (expr(port), port) + +    port: the value from portlist for which expr(port) succeeded + +    Raises: + +    Any exception raised by expr(port) other than EADDRINUSE. + +    socket.error if, for every item from portlist, expr(port) raises +    socket.error. The exception you see is the one from the last item in +    portlist. + +    StopIteration if portlist is completely empty. + +    Example: + +    class Server(HTTPServer): +        # If you use BaseHTTPServer.HTTPServer, turning off this flag is +        # essential for proper operation of freeport()! +        allow_reuse_address = False +    # ... +    server, port = freeport(xrange(8000, 8010), +                            lambda port: Server(("localhost", port), +                                                MyRequestHandler)) +    # pass 'port' to client code +    # call server.serve_forever() +    """ +    try: +        # If portlist is completely empty, let StopIteration propagate: that's an +        # error because we can't return meaningful values. We have no 'port', +        # therefore no 'expr(port)'. +        portiter = iter(portlist) +        port = portiter.next() + +        while True: +            try: +                # If this value of port works, return as promised. +                value = expr(port) + +            except socket.error, err: +                # Anything other than 'Address already in use', propagate +                if err.args[0] != errno.EADDRINUSE: +                    raise + +                # Here we want the next port from portiter. But on StopIteration, +                # we want to raise the original exception rather than +                # StopIteration. So save the original exc_info(). +                type, value, tb = sys.exc_info() +                try: +                    try: +                        port = portiter.next() +                    except StopIteration: +                        raise type, value, tb +                finally: +                    # Clean up local traceback, see docs for sys.exc_info() +                    del tb + +            else: +                debug("freeport() returning %s on port %s", value, port) +                return value, port + +            # Recap of the control flow above: +            # If expr(port) doesn't raise, return as promised. +            # If expr(port) raises anything but EADDRINUSE, propagate that +            # exception. +            # If portiter.next() raises StopIteration -- that is, if the port +            # value we just passed to expr(port) was the last available -- reraise +            # the EADDRINUSE exception. +            # If we've actually arrived at this point, portiter.next() delivered a +            # new port value. Loop back to pass that to expr(port). + +    except Exception, err: +        debug("*** freeport() raising %s: %s", err.__class__.__name__, err) +        raise + +def run(*args, **kwds): +    """All positional arguments collectively form a command line, executed as +    a synchronous child process. +    In addition, pass server=new_thread_instance as an explicit keyword (to +    differentiate it from an additional command-line argument). +    new_thread_instance should be an instantiated but not yet started Thread +    subclass instance, e.g.: +    run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) +    """ +    # If there's no server= keyword arg, don't start a server thread: simply +    # run a child process. +    try: +        thread = kwds.pop("server") +    except KeyError: +        pass +    else: +        # Start server thread. Note that this and all other comm server +        # threads should be daemon threads: we'll let them run "forever," +        # confident that the whole process will terminate when the main thread +        # terminates, which will be when the child process terminates. +        thread.setDaemon(True) +        thread.start() +    # choice of os.spawnv(): +    # - [v vs. l] pass a list of args vs. individual arguments, +    # - [no p] don't use the PATH because we specifically want to invoke the +    #   executable passed as our first arg, +    # - [no e] child should inherit this process's environment. +    debug("Running %s...", " ".join(args)) +    if kwds.get("use_path", False): +        rc = os.spawnvp(os.P_WAIT, args[0], args) +    else: +        rc = os.spawnv(os.P_WAIT, args[0], args) +    debug("%s returned %s", args[0], rc) +    return rc + +# **************************************************************************** +#   test code -- manual at this point, see SWAT-564 +# **************************************************************************** +def test_freeport(): +    # ------------------------------- Helpers -------------------------------- +    from contextlib import contextmanager +    # helper Context Manager for expecting an exception +    # with exc(SomeError): +    #     raise SomeError() +    # raises AssertionError otherwise. +    @contextmanager +    def exc(exception_class, *args): +        try: +            yield +        except exception_class, err: +            for i, expected_arg in enumerate(args): +                assert expected_arg == err.args[i], \ +                       "Raised %s, but args[%s] is %r instead of %r" % \ +                       (err.__class__.__name__, i, err.args[i], expected_arg) +            print "Caught expected exception %s(%s)" % \ +                  (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) +        else: +            assert False, "Failed to raise " + exception_class.__class__.__name__ + +    # helper to raise specified exception +    def raiser(exception): +        raise exception + +    # the usual +    def assert_equals(a, b): +        assert a == b, "%r != %r" % (a, b) + +    # ------------------------ Sanity check the above ------------------------ +    class SomeError(Exception): pass +    # Without extra args, accept any err.args value +    with exc(SomeError): +        raiser(SomeError("abc")) +    # With extra args, accept only the specified value +    with exc(SomeError, "abc"): +        raiser(SomeError("abc")) +    with exc(AssertionError): +        with exc(SomeError, "abc"): +            raiser(SomeError("def")) +    with exc(AssertionError): +        with exc(socket.error, errno.EADDRINUSE): +            raiser(socket.error(errno.ECONNREFUSED, 'Connection refused')) + +    # ----------- freeport() without engaging socket functionality ----------- +    # If portlist is empty, freeport() raises StopIteration. +    with exc(StopIteration): +        freeport([], None) + +    assert_equals(freeport([17], str), ("17", 17)) + +    # This is the magic exception that should prompt us to retry +    inuse = socket.error(errno.EADDRINUSE, 'Address already in use') +    # Get the iterator to our ports list so we can check later if we've used all +    ports = iter(xrange(5)) +    with exc(socket.error, errno.EADDRINUSE): +        freeport(ports, lambda port: raiser(inuse)) +    # did we entirely exhaust 'ports'? +    with exc(StopIteration): +        ports.next() + +    ports = iter(xrange(2)) +    # Any exception but EADDRINUSE should quit immediately +    with exc(SomeError): +        freeport(ports, lambda port: raiser(SomeError())) +    assert_equals(ports.next(), 1) + +    # ----------- freeport() with platform-dependent socket stuff ------------ +    # This is what we should've had unit tests to begin with (see CHOP-661). +    def newbind(port): +        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +        sock.bind(('127.0.0.1', port)) +        return sock + +    bound0, port0 = freeport(xrange(7777, 7780), newbind) +    assert_equals(port0, 7777) +    bound1, port1 = freeport(xrange(7777, 7780), newbind) +    assert_equals(port1, 7778) +    bound2, port2 = freeport(xrange(7777, 7780), newbind) +    assert_equals(port2, 7779) +    with exc(socket.error, errno.EADDRINUSE): +        bound3, port3 = freeport(xrange(7777, 7780), newbind) + +if __name__ == "__main__": +    test_freeport() | 
