summaryrefslogtreecommitdiff
path: root/indra/llcorehttp/tests
diff options
context:
space:
mode:
authorMonty Brandenberg <monty@lindenlab.com>2012-07-02 18:06:22 -0400
committerMonty Brandenberg <monty@lindenlab.com>2012-07-02 18:06:22 -0400
commit8e5197a71bef5704956ed61eab03509a1cdb6f6f (patch)
tree499fd5e906372f74a25d77331b9d5a0d9a8afef4 /indra/llcorehttp/tests
parent90547ff411db177bf6424ca553449a81a808fc0f (diff)
parente8b0088d1a0c02bfa1f9768dc91fc3df4322adae (diff)
Merge 3.3.3 release with Drano HTTP library at 3.3.0
Big delta was converting the new texture debugger support code to the new library. Viewer manifest should probably get an eyeball before release.
Diffstat (limited to 'indra/llcorehttp/tests')
-rw-r--r--indra/llcorehttp/tests/llcorehttp_test.cpp175
-rw-r--r--indra/llcorehttp/tests/llcorehttp_test.h64
-rw-r--r--indra/llcorehttp/tests/test_allocator.cpp184
-rw-r--r--indra/llcorehttp/tests/test_allocator.h47
-rw-r--r--indra/llcorehttp/tests/test_bufferarray.hpp432
-rw-r--r--indra/llcorehttp/tests/test_bufferstream.hpp304
-rw-r--r--indra/llcorehttp/tests/test_httpheaders.hpp108
-rw-r--r--indra/llcorehttp/tests/test_httpoperation.hpp125
-rw-r--r--indra/llcorehttp/tests/test_httprequest.hpp1526
-rw-r--r--indra/llcorehttp/tests/test_httprequestqueue.hpp186
-rw-r--r--indra/llcorehttp/tests/test_httpstatus.hpp265
-rw-r--r--indra/llcorehttp/tests/test_llcorehttp_peer.py166
-rw-r--r--indra/llcorehttp/tests/test_refcounted.hpp156
-rw-r--r--indra/llcorehttp/tests/testrunner.py262
14 files changed, 4000 insertions, 0 deletions
diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp
new file mode 100644
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 100644
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 100644
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 100644
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 100644
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 100644
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 100644
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 100644
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 100644
index 0000000000..ed4e239fe7
--- /dev/null
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -0,0 +1,1526 @@
+/**
+ * @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 "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),
+ mCheckHeader(false)
+ {}
+
+ 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 (mCheckHeader)
+ {
+ 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);
+ bool found_special(false);
+
+ for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin());
+ header->mHeaders.end() != iter;
+ ++iter)
+ {
+ if (std::string::npos != (*iter).find("X-LL-Special"))
+ {
+ found_special = true;
+ break;
+ }
+ }
+ ensure("Special header X-LL-Special in response", found_special);
+ }
+
+ // std::cout << "TestHandler2::onCompleted() invoked" << std::endl;
+ }
+
+ HttpRequestTestData * mState;
+ std::string mName;
+ HttpHandle mExpectHandle;
+ bool mCheckHeader;
+};
+
+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(1000);
+ 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(1000);
+ 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(1000);
+ req2->update(1000);
+ 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(1000);
+ req2->update(1000);
+ 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(1000);
+ 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(1000);
+ 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(1000);
+ 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(1000);
+ 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;
+ }
+}
+
+
+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(1000);
+ 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(1000);
+ 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(1000);
+ 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(1000);
+ 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(1000);
+ 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(1000);
+ 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<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(1000);
+ 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(1000);
+ 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(1000);
+ 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(1000);
+ 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<13>()
+{
+ ScopedCurlInit ready;
+
+ 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");
+
+ // 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();
+
+ // Issue a GET that succeeds
+ mStatus = HttpStatus(200);
+ handler.mCheckHeader = true;
+ 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(1000);
+ 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.mCheckHeader = false;
+ 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(1000);
+ 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;
+ }
+}
+
+
+// *NB: This test must be last. The sleeping webserver
+// won't respond for a long time.
+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(1000);
+ 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(1000);
+ 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;
+ }
+}
+// *NOTE: This test ^^^^^^^^ must be the last one in the set. It uses a
+// sleeping service that interferes with other HTTP tests. Keep it
+// last until that little HTTP server can get some attention...
+
+} // 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 100644
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 100644
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 100644
index 0000000000..0e38e5a87f
--- /dev/null
+++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py
@@ -0,0 +1,166 @@
+#!/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) 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$
+"""
+
+import os
+import sys
+import time
+from threading import Thread
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+
+mydir = os.path.dirname(__file__) # expected to be .../indra/llmessage/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 self.path.find("/sleep/") != -1:
+ 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)
+ 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 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(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__":
+ # 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)
+ sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:]))
diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp
new file mode 100644
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 100644
index 0000000000..5b9beb359b
--- /dev/null
+++ b/indra/llcorehttp/tests/testrunner.py
@@ -0,0 +1,262 @@
+#!/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))
+ 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()