summaryrefslogtreecommitdiff
path: root/indra/test
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/test
Print done when done.
Diffstat (limited to 'indra/test')
-rw-r--r--indra/test/io.cpp1368
-rw-r--r--indra/test/llhttpclient_tut.cpp296
-rw-r--r--indra/test/llhttpnode_tut.cpp409
-rw-r--r--indra/test/lliohttpserver_tut.cpp284
-rw-r--r--indra/test/llpipeutil.cpp139
-rw-r--r--indra/test/llpipeutil.h125
-rw-r--r--indra/test/llsd_new_tut.cpp821
-rw-r--r--indra/test/lltut.cpp149
-rw-r--r--indra/test/lltut.h76
-rw-r--r--indra/test/lluserrelations_tut.cpp138
-rw-r--r--indra/test/test.cpp248
11 files changed, 4053 insertions, 0 deletions
diff --git a/indra/test/io.cpp b/indra/test/io.cpp
new file mode 100644
index 0000000000..4695594a90
--- /dev/null
+++ b/indra/test/io.cpp
@@ -0,0 +1,1368 @@
+/**
+ * @file io.cpp
+ * @author Phoenix
+ * @date 2005-10-02
+ * @brief Tests for io classes and helpers
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+
+#include <iterator>
+
+#include <apr-1/apr_pools.h>
+
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "lliosocket.h"
+#include "llioutil.h"
+#include "llmemorystream.h"
+#include "llpipeutil.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdrpcclient.h"
+#include "llsdrpcserver.h"
+#include "llsdserialize.h"
+#include "lluuid.h"
+#include "llinstantmessage.h"
+
+namespace tut
+{
+ struct buffer_data
+ {
+ LLBufferArray mBuffer;
+ };
+ typedef test_group<buffer_data> buffer_test;
+ typedef buffer_test::object buffer_object;
+ tut::buffer_test tba("buffer_array");
+
+ template<> template<>
+ void buffer_object::test<1>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ const S32 str_len = strlen(HELLO_WORLD);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("total append size", count, str_len);
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ U8* first = (*it).data();
+ count = mBuffer.countAfter(ch.in(), first);
+ ensure_equals("offset append size", count, str_len - 1);
+ }
+
+ template<> template<>
+ void buffer_object::test<2>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ const S32 str_len = strlen(HELLO_WORLD);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("total append size", count, 2 * str_len);
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ U8* first = (*it).data();
+ count = mBuffer.countAfter(ch.in(), first);
+ ensure_equals("offset append size", count, (2 * str_len) - 1);
+ }
+
+ template<> template<>
+ void buffer_object::test<3>()
+ {
+ const char ONE[] = "one";
+ const char TWO[] = "two";
+ std::string expected(ONE);
+ expected.append(TWO);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)ONE, 3);
+ mBuffer.append(ch.in(), (U8*)TWO, 3);
+ char buffer[255];
+ S32 len = 6;
+ mBuffer.readAfter(ch.in(), NULL, (U8*)buffer, len);
+ ensure_equals(len, 6);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("read", actual, expected);
+ }
+
+ template<> template<>
+ void buffer_object::test<4>()
+ {
+ const char ONE[] = "one";
+ const char TWO[] = "two";
+ std::string expected(ONE);
+ expected.append(TWO);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)TWO, 3);
+ mBuffer.prepend(ch.in(), (U8*)ONE, 3);
+ char buffer[255];
+ S32 len = 6;
+ mBuffer.readAfter(ch.in(), NULL, (U8*)buffer, len);
+ ensure_equals(len, 6);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("read", actual, expected);
+ }
+
+ template<> template<>
+ void buffer_object::test<5>()
+ {
+ const char ONE[] = "one";
+ const char TWO[] = "two";
+ std::string expected("netwo");
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)TWO, 3);
+ mBuffer.prepend(ch.in(), (U8*)ONE, 3);
+ char buffer[255];
+ S32 len = 5;
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ U8* addr = (*it).data();
+ mBuffer.readAfter(ch.in(), addr, (U8*)buffer, len);
+ ensure_equals(len, 5);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("read", actual, expected);
+ }
+
+ template<> template<>
+ void buffer_object::test<6>()
+ {
+ std::string request("The early bird catches the worm.");
+ std::string response("If you're a worm, sleep late.");
+ std::ostringstream expected;
+ expected << "ContentLength: " << response.length() << "\r\n\r\n"
+ << response;
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)request.c_str(), request.length());
+ mBuffer.append(ch.out(), (U8*)response.c_str(), response.length());
+ S32 count = mBuffer.countAfter(ch.out(), NULL);
+ std::ostringstream header;
+ header << "ContentLength: " << count << "\r\n\r\n";
+ std::string head(header.str());
+ mBuffer.prepend(ch.out(), (U8*)head.c_str(), head.length());
+ char buffer[1024];
+ S32 len = response.size() + head.length();
+ ensure_equals("same length", len, (S32)expected.str().length());
+ mBuffer.readAfter(ch.out(), NULL, (U8*)buffer, len);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("threaded writes", actual, expected.str());
+ }
+
+ template<> template<>
+ void buffer_object::test<7>()
+ {
+ const S32 LINE_COUNT = 3;
+ std::string lines[LINE_COUNT] =
+ {
+ std::string("GET /index.htm HTTP/1.0\r\n"),
+ std::string("User-Agent: Wget/1.9.1\r\n"),
+ std::string("Host: localhost:8008\r\n")
+ };
+ std::string text;
+ S32 i;
+ for(i = 0; i < LINE_COUNT; ++i)
+ {
+ text.append(lines[i]);
+ }
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)text.c_str(), text.length());
+ const S32 BUFFER_LEN = 1024;
+ char buf[BUFFER_LEN];
+ S32 len;
+ U8* last = NULL;
+ std::string last_line;
+ for(i = 0; i < LINE_COUNT; ++i)
+ {
+ len = BUFFER_LEN;
+ last = mBuffer.readAfter(ch.in(), last, (U8*)buf, len);
+ char* newline = strchr((char*)buf, '\n');
+ S32 offset = -((len - 1) - (newline - buf));
+ ++newline;
+ *newline = '\0';
+ last_line.assign(buf);
+ std::ostringstream message;
+ message << "line reads in line[" << i << "]";
+ ensure_equals(message.str().c_str(), last_line, lines[i]);
+ last = mBuffer.seek(ch.in(), last, offset);
+ }
+ }
+
+ template<> template<>
+ void buffer_object::test<8>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)"1", 1);
+ LLBufferArray buffer;
+ buffer.append(ch.in(), (U8*)"2", 1);
+ mBuffer.takeContents(buffer);
+ mBuffer.append(ch.in(), (U8*)"3", 1);
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("buffer size", count, 3);
+ U8* temp = new U8[count];
+ mBuffer.readAfter(ch.in(), NULL, temp, count);
+ ensure("buffer content", (0 == memcmp(temp, (void*)"123", 3)));
+ delete[] temp;
+ }
+
+/*
+ template<> template<>
+ void buffer_object::test<9>()
+ {
+ char buffer[1024];
+ S32 size = sprintf(buffer,
+ "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x",
+ 7,
+ 7,
+ "Hang Glider INFO",
+ "18e84d1e-04a4-4c0d-8cb6-6c73477f0a9a",
+ "0e346d8b-4433-4d66-a6b0-fd37083abc4c",
+ "0e346d8b-4433-4d66-a6b0-fd37083abc4c",
+ "00000000-0000-0000-0000-000000000000",
+ 0x7fffffff,
+ 0x7fffffff,
+ 0,
+ 0,
+ 0x7fffffff,
+ "69e0d357-2e7c-8990-a2bc-7f61c868e5a3",
+ "2004-06-04 16:09:17 note card",
+ 0,
+ 10,
+ 0) + 1;
+
+ //const char* expected = "7|7|Hang Glider INFO|18e84d1e-04a4-4c0d-8cb6-6c73477f0a9a|0e346d8b-4433-4d66-a6b0-fd37083abc4c|0e346d8b-4433-4d66-a6b0-fd37083abc4c|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|7fffffff|69e0d357-2e7c-8990-a2bc-7f61c868e5a3|2004-06-04 16:09:17 note card|0|10|0\0";
+
+ LLSD* bin_bucket = LLIMInfo::buildSDfrombuffer((U8*)buffer,size);
+
+ char post_buffer[1024];
+ U32 post_size;
+ LLIMInfo::getBinaryBucket(bin_bucket,(U8*)post_buffer,post_size);
+ ensure_equals("Buffer sizes",size,(S32)post_size);
+ ensure("Buffer content",!strcmp(buffer,post_buffer));
+ }
+*/
+
+ /*
+ template<> template<>
+ void buffer_object::test<>()
+ {
+ }
+ */
+}
+
+namespace tut
+{
+ struct buffer_and_stream_data
+ {
+ LLBufferArray mBuffer;
+ };
+ typedef test_group<buffer_and_stream_data> bas_test;
+ typedef bas_test::object bas_object;
+ tut::bas_test tbs("buffer_stream");
+
+ template<> template<>
+ void bas_object::test<1>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ const S32 str_len = strlen(HELLO_WORLD);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream str(ch, &mBuffer);
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ std::string hello;
+ std::string world;
+ str >> hello >> world;
+ ensure_equals("first word", hello, std::string("hello"));
+ ensure_equals("second word", world, std::string("world"));
+ }
+
+ template<> template<>
+ void bas_object::test<2>()
+ {
+ std::string part1("Eat my shor");
+ std::string part2("ts ho");
+ std::string part3("mer");
+ std::string ignore("ignore me");
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream str(ch, &mBuffer);
+ mBuffer.append(ch.in(), (U8*)part1.c_str(), part1.length());
+ mBuffer.append(ch.in(), (U8*)part2.c_str(), part2.length());
+ mBuffer.append(ch.out(), (U8*)ignore.c_str(), ignore.length());
+ mBuffer.append(ch.in(), (U8*)part3.c_str(), part3.length());
+ std::string eat;
+ std::string my;
+ std::string shorts;
+ std::string homer;
+ str >> eat >> my >> shorts >> homer;
+ ensure_equals("word1", eat, std::string("Eat"));
+ ensure_equals("word2", my, std::string("my"));
+ ensure_equals("word3", shorts, std::string("shorts"));
+ ensure_equals("word4", homer, std::string("homer"));
+ }
+
+ template<> template<>
+ void bas_object::test<3>()
+ {
+ std::string part1("junk in ");
+ std::string part2("the trunk");
+ const S32 CHANNEL = 0;
+ mBuffer.append(CHANNEL, (U8*)part1.c_str(), part1.length());
+ mBuffer.append(CHANNEL, (U8*)part2.c_str(), part2.length());
+ U8* last = 0;
+ const S32 BUF_LEN = 128;
+ char buf[BUF_LEN];
+ S32 len = 11;
+ last = mBuffer.readAfter(CHANNEL, last, (U8*)buf, len);
+ buf[len] = '\0';
+ std::string actual(buf);
+ ensure_equals("first read", actual, std::string("junk in the"));
+ last = mBuffer.seek(CHANNEL, last, -6);
+ len = 12;
+ last = mBuffer.readAfter(CHANNEL, last, (U8*)buf, len);
+ buf[len] = '\0';
+ actual.assign(buf);
+ ensure_equals("seek and read", actual, std::string("in the trunk"));
+ }
+
+ template<> template<>
+ void bas_object::test<4>()
+ {
+ std::string phrase("zippity do da!");
+ const S32 CHANNEL = 0;
+ mBuffer.append(CHANNEL, (U8*)phrase.c_str(), phrase.length());
+ const S32 BUF_LEN = 128;
+ char buf[BUF_LEN];
+ S32 len = 7;
+ U8* last = mBuffer.readAfter(CHANNEL, NULL, (U8*)buf, len);
+ mBuffer.splitAfter(last);
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ LLBufferArray::segment_iterator_t end = mBuffer.endSegment();
+ std::string first((char*)((*it).data()), (*it).size());
+ ensure_equals("first part", first, std::string("zippity"));
+ ++it;
+ std::string second((char*)((*it).data()), (*it).size());
+ ensure_equals("second part", second, std::string(" do da!"));
+ ++it;
+ ensure("iterators equal", (it == end));
+ }
+
+ template<> template<>
+ void bas_object::test<5>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream str(ch, &mBuffer);
+ std::string h1("hello");
+ std::string h2(", how are you doing?");
+ std::string expected(h1);
+ expected.append(h2);
+ str << h1 << h2;
+ str.flush();
+ const S32 BUF_LEN = 128;
+ char buf[BUF_LEN];
+ S32 actual_len = BUF_LEN;
+ S32 expected_len = h1.size() + h2.size();
+ (void) mBuffer.readAfter(ch.out(), NULL, (U8*)buf, actual_len);
+ ensure_equals("streamed size", actual_len, expected_len);
+ buf[actual_len] = '\0';
+ std::string actual(buf);
+ ensure_equals("streamed to buf", actual, expected);
+ }
+
+ template<> template<>
+ void bas_object::test<6>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream bstr(ch, &mBuffer);
+ std::ostringstream ostr;
+ std::vector<LLUUID> ids;
+ LLUUID id;
+ for(int i = 0; i < 5; ++i)
+ {
+ id.generate();
+ ids.push_back(id);
+ }
+ bstr << "SELECT concat(u.username, ' ', l.name) "
+ << "FROM user u, user_last_name l "
+ << "WHERE u.last_name_id = l.last_name_id"
+ << " AND u.agent_id IN ('";
+ ostr << "SELECT concat(u.username, ' ', l.name) "
+ << "FROM user u, user_last_name l "
+ << "WHERE u.last_name_id = l.last_name_id"
+ << " AND u.agent_id IN ('";
+ std::copy(
+ ids.begin(),
+ ids.end(),
+ std::ostream_iterator<LLUUID>(bstr, "','"));
+ std::copy(
+ ids.begin(),
+ ids.end(),
+ std::ostream_iterator<LLUUID>(ostr, "','"));
+ bstr.seekp(-2, std::ios::cur);
+ ostr.seekp(-2, std::ios::cur);
+ bstr << ") ";
+ ostr << ") ";
+ bstr.flush();
+ const S32 BUF_LEN = 512;
+ char buf[BUF_LEN];
+ S32 actual_len = BUF_LEN;
+ (void) mBuffer.readAfter(ch.out(), NULL, (U8*)buf, actual_len);
+ buf[actual_len] = '\0';
+ std::string actual(buf);
+ std::string expected(ostr.str());
+ ensure_equals("size of string in seek",actual.size(),expected.size());
+ ensure_equals("seek in ostream", actual, expected);
+ }
+
+ template<> template<>
+ void bas_object::test<7>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream bstr(ch, &mBuffer);
+ bstr << "1";
+ bstr.flush();
+ S32 count = mBuffer.countAfter(ch.out(), NULL);
+ ensure_equals("buffer size 1", count, 1);
+ LLBufferArray buffer;
+ buffer.append(ch.out(), (U8*)"2", 1);
+ mBuffer.takeContents(buffer);
+ count = mBuffer.countAfter(ch.out(), NULL);
+ ensure_equals("buffer size 2", count, 2);
+ bstr << "3";
+ bstr.flush();
+ count = mBuffer.countAfter(ch.out(), NULL);
+ ensure_equals("buffer size 3", count, 3);
+ U8* temp = new U8[count];
+ mBuffer.readAfter(ch.out(), NULL, temp, count);
+ ensure("buffer content", (0 == memcmp(temp, (void*)"123", 3)));
+ delete[] temp;
+ }
+
+ template<> template<>
+ void bas_object::test<8>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream ostr(ch, &mBuffer);
+ typedef std::vector<U8> buf_t;
+ typedef std::vector<buf_t> actual_t;
+ actual_t actual;
+ buf_t source;
+ bool need_comma = false;
+ ostr << "[";
+ S32 total_size = 1;
+ for(S32 i = 2000; i < 2003; ++i)
+ {
+ if(need_comma)
+ {
+ ostr << ",";
+ ++total_size;
+ }
+ need_comma = true;
+ srand(69 + i);
+ S32 size = rand() % 1000 + 1000;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(source),
+ size,
+ rand);
+ actual.push_back(source);
+ ostr << "b(" << size << ")\"";
+ total_size += 8;
+ ostr.write((const char*)(&source[0]), size);
+ total_size += size;
+ source.clear();
+ ostr << "\"";
+ ++total_size;
+ }
+ ostr << "]";
+ ++total_size;
+ ostr.flush();
+
+ // now that we have a bunch of data on a stream, parse it all.
+ ch = mBuffer.nextChannel();
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("size of buffer", count, total_size);
+ LLBufferStream istr(ch, &mBuffer);
+ LLSD data;
+ count = LLSDSerialize::fromNotation(data, istr);
+ ensure("sd parsed", data.isDefined());
+
+ for(S32 j = 0; j < 3; ++j)
+ {
+ std::ostringstream name;
+ LLSD child(data[j]);
+ name << "found buffer " << j;
+ ensure(name.str(), child.isDefined());
+ source = child.asBinary();
+ name.str("");
+ name << "buffer " << j << " size";
+ ensure_equals(name.str().c_str(), source.size(), actual[j].size());
+ name.str("");
+ name << "buffer " << j << " contents";
+ ensure(
+ name.str(),
+ (0 == memcmp(&source[0], &actual[j][0], source.size())));
+ }
+ }
+
+ template<> template<>
+ void bas_object::test<9>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream ostr(ch, &mBuffer);
+ typedef std::vector<U8> buf_t;
+ buf_t source;
+ bool need_comma = false;
+ ostr << "{";
+ S32 total_size = 1;
+ for(S32 i = 1000; i < 3000; ++i)
+ {
+ if(need_comma)
+ {
+ ostr << ",";
+ ++total_size;
+ }
+ need_comma = true;
+ ostr << "'" << i << "':";
+ total_size += 7;
+ srand(69 + i);
+ S32 size = rand() % 1000 + 1000;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(source),
+ size,
+ rand);
+ ostr << "b(" << size << ")\"";
+ total_size += 8;
+ ostr.write((const char*)(&source[0]), size);
+ total_size += size;
+ source.clear();
+ ostr << "\"";
+ ++total_size;
+ }
+ ostr << "}";
+ ++total_size;
+ ostr.flush();
+
+ // now that we have a bunch of data on a stream, parse it all.
+ ch = mBuffer.nextChannel();
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("size of buffer", count, total_size);
+ LLBufferStream istr(ch, &mBuffer);
+ LLSD data;
+ count = LLSDSerialize::fromNotation(data, istr);
+ ensure("sd parsed", data.isDefined());
+ }
+
+ template<> template<>
+ void bas_object::test<10>()
+ {
+ const char LOGIN_STREAM[] = "{'method':'login', 'parameter': [ {"
+ "'uri': 'sl-am:kellys.region.siva.lindenlab.com/location?start=url&px=128&py=128&pz=128&lx=0&ly=0&lz=0'}, "
+ "{'version': i1}, {'texture_data': [ '61d724fb-ad79-f637-2186-5cf457560daa', '6e38b9be-b7cc-e77a-8aec-029a42b0b416', "
+ "'a9073524-e89b-2924-ca6e-a81944109a1a', '658f18b5-5f1e-e593-f5d5-36c3abc7249a', '0cc799f4-8c99-6b91-bd75-b179b12429e2', "
+ "'59fd9b64-8300-a425-aad8-2ffcbe9a49d2', '59fd9b64-8300-a425-aad8-2ffcbe9a49d2', '5748decc-f629-461c-9a36-a35a221fe21f', "
+ "'b8fc9be2-26a6-6b47-690b-0e902e983484', 'a13ca0fe-3802-dc97-e79a-70d12171c724', 'dd9643cf-fd5d-0376-ed4a-b1cc646a97d5', "
+ "'4ad13ae9-a112-af09-210a-cf9353a7a9e7', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', "
+ "'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', "
+ "'5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'],"
+ "'session_id': '324cfa9f-fe5d-4d1c-a317-35f20a86a4d1','position': [ i128, i128, i128],'last_name': 'Linden','group_title': '-> !BLING! <-','group_name': 'test!','agent_access': 'M',"
+ "'attachment_data': [ {'asset_id': 'aaede2b1-9955-09d4-5c93-2b557c778cf3','attachment_point': i6,'item_id': 'f3694abc-5122-db33-73d9-e0f4288dc2bf'}],"
+ "'buddy_ids': [ '101358d5-469d-4b24-9b85-4dc3c05e635d', '1b00fec7-6265-4875-acac-80d9cfe9295c', '203ad6df-b522-491d-ba48-4e24eb57aeff', "
+ "'22d4dcdb-aebb-47fa-b925-a871cc75ee48','27da3df5-1339-4463-80aa-40504ee3b3e5', '299d1720-b61f-4268-8c29-9614aa2d44c2', "
+ "'2b048a24-2737-4994-9fa5-becc8e466253', '2cd5dc14-a853-49a4-be3c-a5a7178e37bc', '3de548e1-57be-cfea-2b78-83ae3ad95998', "
+ "'3dee98e4-a6a3-4543-91c3-bbd528447ba7', '3e2d81a3-6263-6ffe-ad5c-8ce04bee07e9', '40e70b98-fed7-47f3-9700-1bce93f9350b', "
+ "'50a9b68e-b5aa-4d35-9137-3cfebda0a15c', '54295571-9357-43ff-ae74-a83b5138160f', '6191e2d7-5f96-4856-bdab-af0f79f47ae4', "
+ "'63e577d8-cd34-4235-a0a3-de0500133364', '79cfb666-4fd0-4af7-95df-fb7d96b4e24d', '8121c2f3-4a88-4c33-9899-8fc1273f47ee', "
+ "'909da964-ef23-4f2a-ba13-f2a8cfd454b6','a2e76fcd-9360-4f6d-a924-000000000001', 'aaa6d664-527e-4d83-9cbb-7ef79ccc7cc8', "
+ "'b79bfb6c-23be-49eb-b35b-30ff2f501b37', 'ba0d9c79-148c-4a79-8e3c-0665eebe2427', 'bc9bda98-57cd-498f-b993-4ff1ac9dec93', "
+ "'c62d16f6-81cb-419d-9cac-e46dc394084d', 'd48f8fa7-2512-4fe5-80c8-c0a923412e07', 'd77e3e24-7e6c-4c3f-96d0-a1746337f8fb', "
+ "'da615c63-a84b-4592-a3d6-a90dd3e92e6e', 'df47190a-7eb7-4aff-985f-2d1d3ad6c6e9', 'e3380196-72cd-499c-a2ba-caa180bd5fe4', "
+ "'e937863f-f134-4207-803b-d6e686651d6c', 'efcdf98b-5269-45ef-ac7a-0671f09ea9d9'],"
+ "'circuit_code': i124,'group_id': '8615c885-9cf0-bf0a-6e40-0c11462aa652','limited_to_estate': i1,'look_at': [ i0, i0, i0],"
+ "'agent_id': '0e346d8b-4433-4d66-a6b0-fd37083abc4c','first_name': 'Kelly','start': 'url'}]}";
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.out(), (U8*)LOGIN_STREAM, strlen(LOGIN_STREAM));
+ ch = mBuffer.nextChannel();
+ LLBufferStream istr(ch, &mBuffer);
+ LLSD data;
+ S32 count = LLSDSerialize::fromNotation(data, istr);
+ ensure("parsed something", (count > 0));
+ ensure("sd parsed", data.isDefined());
+ ensure_equals("sd type", data.type(), LLSD::TypeMap);
+ ensure("has method", data.has("method"));
+ ensure("has parameter", data.has("parameter"));
+ LLSD parameter = data["parameter"];
+ ensure_equals("parameter is array", parameter.type(), LLSD::TypeArray);
+ LLSD agent_params = parameter[2];
+ std::string s_value;
+ s_value = agent_params["last_name"].asString();
+ ensure_equals("last name", s_value, std::string("Linden"));
+ s_value = agent_params["first_name"].asString();
+ ensure_equals("first name", s_value, std::string("Kelly"));
+ s_value = agent_params["agent_access"].asString();
+ ensure_equals("agent access", s_value, std::string("M"));
+ s_value = agent_params["group_name"].asString();
+ ensure_equals("group name", s_value, std::string("test!"));
+ s_value = agent_params["group_title"].asString();
+ ensure_equals("group title", s_value, std::string("-> !BLING! <-"));
+
+ LLUUID agent_id("0e346d8b-4433-4d66-a6b0-fd37083abc4c");
+ LLUUID id = agent_params["agent_id"];
+ ensure_equals("agent id", id, agent_id);
+ LLUUID session_id("324cfa9f-fe5d-4d1c-a317-35f20a86a4d1");
+ id = agent_params["session_id"];
+ ensure_equals("session id", id, session_id);
+ LLUUID group_id ("8615c885-9cf0-bf0a-6e40-0c11462aa652");
+ id = agent_params["group_id"];
+ ensure_equals("group id", id, group_id);
+
+ S32 i_val = agent_params["limited_to_estate"];
+ ensure_equals("limited to estate", i_val, 1);
+ i_val = agent_params["circuit_code"];
+ ensure_equals("circuit code", i_val, 124);
+ }
+
+
+ template<> template<>
+ void bas_object::test<11>()
+ {
+ std::string val = "{!'foo'@:#'bar'}";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, istr);
+ ensure_equals("parser error return value", count, -1);
+ ensure("data undefined", sd.isUndefined());
+ }
+
+ template<> template<>
+ void bas_object::test<12>()
+ {
+ std::string val = "{!'foo':[i1,'hi',{@'bar'#:[$i2%,^'baz'&]*}+]=}";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, istr);
+ ensure_equals("parser error return value", count, -1);
+ ensure("data undefined", sd.isUndefined());
+ }
+
+/*
+ template<> template<>
+ void bas_object::test<13>()
+ {
+ }
+ template<> template<>
+ void bas_object::test<14>()
+ {
+ }
+ template<> template<>
+ void bas_object::test<15>()
+ {
+ }
+*/
+}
+
+
+namespace tut
+{
+ class PumpAndChainTestData
+ {
+ protected:
+ apr_pool_t* mPool;
+ LLPumpIO* mPump;
+ LLPumpIO::chain_t mChain;
+
+ public:
+ PumpAndChainTestData()
+ {
+ apr_pool_create(&mPool, NULL);
+ mPump = new LLPumpIO(mPool);
+ }
+
+ ~PumpAndChainTestData()
+ {
+ mChain.clear();
+ delete mPump;
+ apr_pool_destroy(mPool);
+ }
+ };
+ typedef test_group<PumpAndChainTestData> PumpAndChainTestGroup;
+ typedef PumpAndChainTestGroup::object PumpAndChainTestObject;
+ PumpAndChainTestGroup pumpAndChainTestGroup("pump_and_chain");
+
+ template<> template<>
+ void PumpAndChainTestObject::test<1>()
+ {
+ LLPipeStringExtractor* extractor = new LLPipeStringExtractor();
+
+ mChain.push_back(LLIOPipe::ptr_t(new LLIOFlush));
+ mChain.push_back(LLIOPipe::ptr_t(extractor));
+
+ LLTimer timer;
+ timer.setTimerExpirySec(100.0f);
+
+ mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS);
+ while(!extractor->done() && !timer.hasExpired())
+ {
+ mPump->pump();
+ mPump->callback();
+ }
+
+ ensure("reading string finished", extractor->done());
+ ensure_equals("string was empty", extractor->string(), "");
+ }
+}
+
+/*
+namespace tut
+{
+ struct double_construct
+ {
+ public:
+ double_construct()
+ {
+ llinfos << "constructed" << llendl;
+ }
+ ~double_construct()
+ {
+ llinfos << "destroyed" << llendl;
+ }
+ };
+ typedef test_group<double_construct> double_construct_test_group;
+ typedef double_construct_test_group::object dc_test_object;
+ double_construct_test_group dctest("double construct");
+ template<> template<>
+ void dc_test_object::test<1>()
+ {
+ ensure("test 1", true);
+ }
+}
+*/
+
+namespace tut
+{
+ /**
+ * @brief we want to test the pipes & pumps under bad conditions.
+ */
+ struct pipe_and_pump_fitness
+ {
+ public:
+ enum
+ {
+ SERVER_LISTEN_PORT = 13050
+ };
+
+ pipe_and_pump_fitness()
+ {
+ LLFrameTimer::updateFrameTime();
+ apr_pool_create(&mPool, NULL);
+ mPump = new LLPumpIO(mPool);
+ mSocket = LLSocket::create(
+ mPool,
+ LLSocket::STREAM_TCP,
+ SERVER_LISTEN_PORT);
+ }
+
+ ~pipe_and_pump_fitness()
+ {
+ mSocket.reset();
+ delete mPump;
+ apr_pool_destroy(mPool);
+ }
+
+ protected:
+ apr_pool_t* mPool;
+ LLPumpIO* mPump;
+ LLSocket::ptr_t mSocket;
+ };
+ typedef test_group<pipe_and_pump_fitness> fitness_test_group;
+ typedef fitness_test_group::object fitness_test_object;
+ fitness_test_group fitness("pipe and pump fitness");
+
+ template<> template<>
+ void fitness_test_object::test<1>()
+ {
+ lldebugs << "fitness_test_object::test<1>()" << llendl;
+
+ // Set up the server
+ //lldebugs << "fitness_test_object::test<1> - setting up server."
+ // << llendl;
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLPipeStringInjector> emitter_t;
+ emitter_t* emitter = new emitter_t(
+ new LLPipeStringInjector("suckers never play me"));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ //lldebugs << "fitness_test_object::test<1> - initializing server."
+ // << llendl;
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ //lldebugs << "fitness_test_object::test<1> - connecting client."
+ // << llendl;
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, 1.0f);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, 2.0f);
+ ensure("Did not take too long", (elapsed < 3.0f));
+ }
+
+ template<> template<>
+ void fitness_test_object::test<2>()
+ {
+ lldebugs << "fitness_test_object::test<2>()" << llendl;
+
+ // Set up the server
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLIOFuzz> emitter_t;
+ emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, SHORT_CHAIN_EXPIRY_SECS / 2.0f);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS * 2.0f);
+ ensure("Did not take too long", (elapsed < 3.0f));
+ }
+
+ template<> template<>
+ void fitness_test_object::test<3>()
+ {
+ lldebugs << "fitness_test_object::test<3>()" << llendl;
+
+ // Set up the server
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLIOFuzz> emitter_t;
+ emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, SHORT_CHAIN_EXPIRY_SECS * 2.0f);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS * 2.0f + 1.0f);
+ ensure("Did not take too long", (elapsed < 4.0f));
+ }
+
+ template<> template<>
+ void fitness_test_object::test<4>()
+ {
+ lldebugs << "fitness_test_object::test<4>()" << llendl;
+
+ // Set up the server
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLIOFuzz> emitter_t;
+ emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS + 2.0f);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS + 3.0f);
+ ensure("Did not take too long", (elapsed < DEFAULT_CHAIN_EXPIRY_SECS));
+ }
+}
+
+namespace tut
+{
+ struct rpc_server_data
+ {
+ class LLSimpleRPCResponse : public LLSDRPCResponse
+ {
+ public:
+ LLSimpleRPCResponse(LLSD* response) :
+ mResponsePtr(response)
+ {
+ }
+ ~LLSimpleRPCResponse() {}
+ virtual bool response(LLPumpIO* pump)
+ {
+ *mResponsePtr = mReturnValue;
+ return true;
+ }
+ virtual bool fault(LLPumpIO* pump)
+ {
+ *mResponsePtr = mReturnValue;
+ return false;
+ }
+ virtual bool error(LLPumpIO* pump)
+ {
+ ensure("LLSimpleRPCResponse::error()", false);
+ return false;
+ }
+ public:
+ LLSD* mResponsePtr;
+ };
+
+ class LLSimpleRPCClient : public LLSDRPCClient
+ {
+ public:
+ LLSimpleRPCClient(LLSD* response) :
+ mResponsePtr(response)
+ {
+ }
+ ~LLSimpleRPCClient() {}
+ void echo(const LLSD& parameter)
+ {
+ LLSimpleRPCResponse* resp;
+ resp = new LLSimpleRPCResponse(mResponsePtr);
+ static const std::string URI_NONE;
+ static const std::string METHOD_ECHO("echo");
+ call(URI_NONE, METHOD_ECHO, parameter, resp, EPBQ_CALLBACK);
+ }
+ public:
+ LLSD* mResponsePtr;
+ };
+
+ class LLSimpleRPCServer : public LLSDRPCServer
+ {
+ public:
+ LLSimpleRPCServer()
+ {
+ mMethods["echo"] = new mem_fn_t(
+ this,
+ &LLSimpleRPCServer::rpc_Echo);
+ }
+ ~LLSimpleRPCServer() {}
+ protected:
+ typedef LLSDRPCMethodCall<LLSimpleRPCServer> mem_fn_t;
+ ESDRPCSStatus rpc_Echo(
+ const LLSD& parameter,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data)
+ {
+ buildResponse(channels, data, parameter);
+ return ESDRPCS_DONE;
+ }
+ };
+
+ apr_pool_t* mPool;
+ LLPumpIO* mPump;
+ LLPumpIO::chain_t mChain;
+ LLSimpleRPCClient* mClient;
+ LLSD mResponse;
+
+ rpc_server_data() :
+ mPool(NULL),
+ mPump(NULL),
+ mClient(NULL)
+ {
+ apr_pool_create(&mPool, NULL);
+ mPump = new LLPumpIO(mPool);
+ mClient = new LLSimpleRPCClient(&mResponse);
+ mChain.push_back(LLIOPipe::ptr_t(mClient));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
+ mChain.push_back(LLIOPipe::ptr_t(new LLSimpleRPCServer));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
+ mChain.push_back(LLIOPipe::ptr_t(mClient));
+ }
+ ~rpc_server_data()
+ {
+ mChain.clear();
+ delete mPump;
+ mPump = NULL;
+ apr_pool_destroy(mPool);
+ mPool = NULL;
+ }
+ void pump_loop(const LLSD& request)
+ {
+ LLTimer timer;
+ timer.setTimerExpirySec(1.0f);
+ mClient->echo(request);
+ mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS);
+ while(mResponse.isUndefined() && !timer.hasExpired())
+ {
+ mPump->pump();
+ mPump->callback();
+ }
+ }
+ };
+ typedef test_group<rpc_server_data> rpc_server_test;
+ typedef rpc_server_test::object rpc_server_object;
+ tut::rpc_server_test rpc("rpc_server");
+
+ template<> template<>
+ void rpc_server_object::test<1>()
+ {
+ LLSD request;
+ request = 1;
+ pump_loop(request);
+ //llinfos << "request: " << *request << llendl;
+ //llinfos << "response: " << *mResponse << llendl;
+ ensure_equals("integer request response", mResponse.asInteger(), 1);
+ }
+
+ template<> template<>
+ void rpc_server_object::test<2>()
+ {
+ std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0");
+ std::stringstream stream;
+ stream << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}";
+ std::vector<U8> expected_binary;
+ expected_binary.resize(stream.str().size());
+ memcpy(&expected_binary[0], stream.str().c_str(), stream.str().size());
+ stream.str("");
+ stream << "[{'uri':'" << uri << "'}, {'version':i1}, "
+ << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], "
+ << "'attachment_data':["
+ << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},"
+ << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << expected_binary.size() << ")\"";
+ stream.write((const char*)&expected_binary[0], expected_binary.size());
+ stream << "\"}"
+ << "]"
+ << "}]";
+
+ LLSD request;
+ S32 count = LLSDSerialize::fromNotation(request, stream);
+ ensure("parsed something", (count > 0));
+
+ pump_loop(request);
+ ensure_equals("return type", mResponse.type(), LLSD::TypeArray);
+ ensure_equals("return size", mResponse.size(), 3);
+
+ ensure_equals(
+ "uri parameter type",
+ mResponse[0].type(),
+ LLSD::TypeMap);
+ ensure_equals(
+ "uri type",
+ mResponse[0]["uri"].type(),
+ LLSD::TypeString);
+ ensure_equals("uri value", mResponse[0]["uri"].asString(), uri);
+
+ ensure_equals(
+ "version parameter type",
+ mResponse[1].type(),
+ LLSD::TypeMap);
+ ensure_equals(
+ "version type",
+ mResponse[1]["version"].type(),
+ LLSD::TypeInteger);
+ ensure_equals(
+ "version value",
+ mResponse[1]["version"].asInteger(),
+ 1);
+
+ ensure_equals("agent params type", mResponse[2].type(), LLSD::TypeMap);
+ LLSD attachment_data = mResponse[2]["attachment_data"];
+ ensure("attachment data exists", attachment_data.isDefined());
+ ensure_equals(
+ "attachment type",
+ attachment_data.type(),
+ LLSD::TypeArray);
+ ensure_equals(
+ "attachment type 0",
+ attachment_data[0].type(),
+ LLSD::TypeMap);
+ ensure_equals(
+ "attachment type 1",
+ attachment_data[1].type(),
+ LLSD::TypeMap);
+ ensure_equals("attachment size 1", attachment_data[1].size(), 3);
+ ensure_equals(
+ "asset data type",
+ attachment_data[1]["asset_data"].type(),
+ LLSD::TypeBinary);
+ std::vector<U8> actual_binary;
+ actual_binary = attachment_data[1]["asset_data"].asBinary();
+ ensure_equals(
+ "binary data size",
+ actual_binary.size(),
+ expected_binary.size());
+ ensure(
+ "binary data",
+ (0 == memcmp(
+ &actual_binary[0],
+ &expected_binary[0],
+ expected_binary.size())));
+ }
+
+ template<> template<>
+ void rpc_server_object::test<3>()
+ {
+ std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0");
+
+ LLBufferArray buffer;
+ LLChannelDescriptors buffer_channels = buffer.nextChannel();
+ LLBufferStream stream(buffer_channels, &buffer);
+ stream << "[{'uri':'" << uri << "'}, {'version':i1}, "
+ << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], "
+ << "'attachment_data':["
+ << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},";
+
+ std::stringstream tmp_str;
+ tmp_str << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}";
+ std::vector<U8> expected_binary;
+ expected_binary.resize(tmp_str.str().size());
+ memcpy(
+ &expected_binary[0],
+ tmp_str.str().c_str(),
+ tmp_str.str().size());
+
+ LLBufferArray attachment_buffer;
+ LLChannelDescriptors attach_channels = attachment_buffer.nextChannel();
+ LLBufferStream attach_stream(attach_channels, &attachment_buffer);
+ attach_stream.write((const char*)&expected_binary[0], expected_binary.size());
+ attach_stream.flush();
+ S32 len = attachment_buffer.countAfter(attach_channels.out(), NULL);
+ stream << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << len << ")\"";
+ stream.flush();
+ buffer.takeContents(attachment_buffer);
+ stream << "\"}]}]";
+ stream.flush();
+
+ LLChannelDescriptors read_channel = buffer.nextChannel();
+ LLBufferStream read_stream(read_channel, &buffer);
+ LLSD request;
+ S32 count = LLSDSerialize::fromNotation(request, read_stream);
+ ensure("parsed something", (count > 0));
+ ensure("deserialized", request.isDefined());
+
+ // do the rpc round trip
+ pump_loop(request);
+
+ ensure_equals("return type", mResponse.type(), LLSD::TypeArray);
+ ensure_equals("return size", mResponse.size(), 3);
+
+ LLSD child = mResponse[0];
+ ensure("uri map exists", child.isDefined());
+ ensure_equals("uri parameter type", child.type(), LLSD::TypeMap);
+ ensure("uri string exists", child.has("uri"));
+ ensure_equals("uri type", child["uri"].type(), LLSD::TypeString);
+ ensure_equals("uri value", child["uri"].asString(), uri);
+
+ child = mResponse[1];
+ ensure("version map exists", child.isDefined());
+ ensure_equals("version param type", child.type(), LLSD::TypeMap);
+ ensure_equals(
+ "version type",
+ child["version"].type(),
+ LLSD::TypeInteger);
+ ensure_equals("version value", child["version"].asInteger(), 1);
+
+ child = mResponse[2];
+ ensure("agent params map exists", child.isDefined());
+ ensure_equals("agent params type", child.type(), LLSD::TypeMap);
+ child = child["attachment_data"];
+ ensure("attachment data exists", child.isDefined());
+ ensure_equals("attachment type", child.type(), LLSD::TypeArray);
+ LLSD attachment = child[0];
+ ensure_equals("attachment type 0", attachment.type(), LLSD::TypeMap);
+ attachment = child[1];
+ ensure_equals("attachment type 1", attachment.type(), LLSD::TypeMap);
+ ensure_equals("attachment size 1", attachment.size(), 3);
+ ensure_equals(
+ "asset data type",
+ attachment["asset_data"].type(),
+ LLSD::TypeBinary);
+ std::vector<U8> actual_binary = attachment["asset_data"].asBinary();
+ ensure_equals(
+ "binary data size",
+ actual_binary.size(),
+ expected_binary.size());
+ ensure(
+ "binary data",
+ (0 == memcmp(
+ &actual_binary[0],
+ &expected_binary[0],
+ expected_binary.size())));
+ }
+
+ template<> template<>
+ void rpc_server_object::test<4>()
+ {
+ std::string message("parcel '' is naughty.");
+ std::stringstream str;
+ str << "{'message':'" << LLSDNotationFormatter::escapeString(message)
+ << "'}";
+ LLSD request;
+ S32 count = LLSDSerialize::fromNotation(request, str);
+ ensure_equals("parse count", count, 2);
+ ensure_equals("request type", request.type(), LLSD::TypeMap);
+ pump_loop(request);
+ ensure("valid response", mResponse.isDefined());
+ ensure_equals("response type", mResponse.type(), LLSD::TypeMap);
+ std::string actual = mResponse["message"].asString();
+ ensure_equals("message contents", actual, message);
+ }
+
+ template<> template<>
+ void rpc_server_object::test<5>()
+ {
+ // test some of the problem cases with llsdrpc over xmlrpc -
+ // for example:
+ // * arrays are auto-converted to parameter lists, thus, this
+ // becomes one parameter.
+ // * undef goes over the wire as false (this might not be a good idea)
+ // * uuids are converted to string.
+ std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, istr);
+ pump_loop(sd);
+ ensure("valid response", mResponse.isDefined());
+ ensure_equals("parsed type", mResponse.type(), LLSD::TypeMap);
+ ensure_equals("parsed size", mResponse.size(), 2);
+ LLSD failures = mResponse["failures"];
+ ensure_equals("no failures.", failures.asBoolean(), false);
+ LLSD success = mResponse["successfuls"];
+ ensure_equals("success type", success.type(), LLSD::TypeArray);
+ ensure_equals("success size", success.size(), 1);
+ ensure_equals(
+ "success instance type",
+ success[0].type(),
+ LLSD::TypeString);
+ }
+
+/*
+ template<> template<>
+ void rpc_server_object::test<5>()
+ {
+ std::string expected("\xf3");//\xffsomething");
+ LLSD* request = LLSD::createString(expected);
+ pump_loop(request);
+ std::string actual;
+ mResponse->getString(actual);
+ if(actual != expected)
+ {
+ //llwarns << "iteration " << i << llendl;
+ std::ostringstream e_str;
+ std::string::iterator iter = expected.begin();
+ std::string::iterator end = expected.end();
+ for(; iter != end; ++iter)
+ {
+ e_str << (S32)((U8)(*iter)) << " ";
+ }
+ e_str << std::endl;
+ llsd_serialize_string(e_str, expected);
+ llwarns << "expected size: " << expected.size() << llendl;
+ llwarns << "expected: " << e_str.str() << llendl;
+
+ std::ostringstream a_str;
+ iter = actual.begin();
+ end = actual.end();
+ for(; iter != end; ++iter)
+ {
+ a_str << (S32)((U8)(*iter)) << " ";
+ }
+ a_str << std::endl;
+ llsd_serialize_string(a_str, actual);
+ llwarns << "actual size: " << actual.size() << llendl;
+ llwarns << "actual: " << a_str.str() << llendl;
+ }
+ ensure_equals("binary string request response", actual, expected);
+ delete request;
+ }
+
+ template<> template<>
+ void rpc_server_object::test<5>()
+ {
+ }
+*/
+}
+
+
+/*
+'asset_data':b(12100)"{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemID STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n"
+*/
diff --git a/indra/test/llhttpclient_tut.cpp b/indra/test/llhttpclient_tut.cpp
new file mode 100644
index 0000000000..98f24c1bdd
--- /dev/null
+++ b/indra/test/llhttpclient_tut.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llhttpclient_tut.cpp
+ * @brief Testing the HTTP client classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * These classes test the HTTP client framework.
+ *
+ */
+
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llhttpclient.h"
+#include "llpipeutil.h"
+#include "llpumpio.h"
+
+#include "llsdhttpserver.h"
+#include "lliohttpserver.h"
+#include "lliosocket.h"
+
+namespace tut
+{
+ LLSD storage;
+
+ class LLSDStorageNode : public LLHTTPNode
+ {
+ public:
+ LLSD get() const { return storage; }
+ LLSD put(const LLSD& value) const { storage = value; return LLSD(); }
+ };
+
+ class ErrorNode : public LLHTTPNode
+ {
+ public:
+ void get(ResponsePtr r, const LLSD& context) const
+ { r->status(599, "Intentional error"); }
+ void post(ResponsePtr r, const LLSD& context, const LLSD& input) const
+ { r->status(input["status"], input["reason"]); }
+ };
+
+ class TimeOutNode : public LLHTTPNode
+ {
+ public:
+ void get(ResponsePtr r, const LLSD& context) const
+ {
+ /* do nothing, the request will eventually time out */
+ }
+ };
+
+ LLHTTPRegistration<LLSDStorageNode> gStorageNode("/test/storage");
+ LLHTTPRegistration<ErrorNode> gErrorNode("/test/error");
+ LLHTTPRegistration<TimeOutNode> gTimeOutNode("/test/timeout");
+
+ struct HTTPClientTestData
+ {
+ public:
+ HTTPClientTestData()
+ {
+ apr_pool_create(&mPool, NULL);
+ mServerPump = new LLPumpIO(mPool);
+ mClientPump = new LLPumpIO(mPool);
+
+ LLHTTPClient::setPump(*mClientPump);
+ }
+
+ ~HTTPClientTestData()
+ {
+ delete mServerPump;
+ delete mClientPump;
+ apr_pool_destroy(mPool);
+ }
+
+ void setupTheServer()
+ {
+ LLHTTPNode& root = LLCreateHTTPServer(mPool, *mServerPump, 8888);
+
+ LLHTTPStandardServices::useServices();
+ LLHTTPRegistrar::buildAllServices(root);
+ }
+
+ void runThePump(float timeout = 100.0f)
+ {
+ LLTimer timer;
+ timer.setTimerExpirySec(timeout);
+
+ while(!mSawCompleted && !timer.hasExpired())
+ {
+ if (mServerPump)
+ {
+ mServerPump->pump();
+ mServerPump->callback();
+ }
+ if (mClientPump)
+ {
+ mClientPump->pump();
+ mClientPump->callback();
+ }
+ }
+ }
+
+ void killServer()
+ {
+ delete mServerPump;
+ mServerPump = NULL;
+ }
+
+ private:
+ apr_pool_t* mPool;
+ LLPumpIO* mServerPump;
+ LLPumpIO* mClientPump;
+
+
+ protected:
+ void ensureStatusOK()
+ {
+ if (mSawError)
+ {
+ std::string msg =
+ llformat("error() called when not expected, status %d",
+ mStatus);
+ fail(msg);
+ }
+ }
+
+ void ensureStatusError()
+ {
+ if (!mSawError)
+ {
+ fail("error() wasn't called");
+ }
+ }
+
+ LLSD getResult()
+ {
+ return mResult;
+ }
+
+ protected:
+ bool mSawError;
+ U32 mStatus;
+ std::string mReason;
+ bool mSawCompleted;
+ LLSD mResult;
+ bool mResultDeleted;
+
+ class Result : public LLHTTPClient::Responder
+ {
+ protected:
+ Result(HTTPClientTestData& client)
+ : mClient(client)
+ {
+ }
+
+ public:
+ static boost::intrusive_ptr<Result> build(HTTPClientTestData& client)
+ {
+ return boost::intrusive_ptr<Result>(new Result(client));
+ }
+
+ ~Result()
+ {
+ mClient.mResultDeleted = true;
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ mClient.mSawError = true;
+ mClient.mStatus = status;
+ mClient.mReason = reason;
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ mClient.mResult = content;
+ }
+
+ virtual void completed(
+ U32 status, const std::string& reason,
+ const LLSD& content)
+ {
+ LLHTTPClient::Responder::completed(status, reason, content);
+
+ mClient.mSawCompleted = true;
+ }
+
+ private:
+ HTTPClientTestData& mClient;
+ };
+
+ friend class Result;
+
+ protected:
+ LLHTTPClient::ResponderPtr newResult()
+ {
+ mSawError = false;
+ mStatus = 0;
+ mSawCompleted = false;
+ mResult.clear();
+ mResultDeleted = false;
+
+ return Result::build(*this);
+ }
+ };
+
+
+ typedef test_group<HTTPClientTestData> HTTPClientTestGroup;
+ typedef HTTPClientTestGroup::object HTTPClientTestObject;
+ HTTPClientTestGroup httpClientTestGroup("http_client");
+
+ template<> template<>
+ void HTTPClientTestObject::test<1>()
+ {
+ LLHTTPClient::get("http://www.google.com/", newResult());
+ runThePump();
+ ensureStatusOK();
+ ensure("result object wasn't destroyed", mResultDeleted);
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<2>()
+ {
+ LLHTTPClient::get("http://www.invalid", newResult());
+ runThePump();
+ ensureStatusError();
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<3>()
+ {
+ LLSD sd;
+
+ sd["list"][0]["one"] = 1;
+ sd["list"][0]["two"] = 2;
+ sd["list"][1]["three"] = 3;
+ sd["list"][1]["four"] = 4;
+
+ setupTheServer();
+
+ LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult());
+ runThePump();
+ ensureStatusOK();
+ ensure_equals("echoed result matches", getResult(), sd);
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<4>()
+ {
+ LLSD sd;
+
+ sd["message"] = "This is my test message.";
+
+ setupTheServer();
+ LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult());
+ runThePump();
+ ensureStatusOK();
+
+ LLHTTPClient::get("http://localhost:8888/test/storage", newResult());
+ runThePump();
+ ensureStatusOK();
+ ensure_equals("echoed result matches", getResult(), sd);
+
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<5>()
+ {
+ LLSD sd;
+ sd["status"] = 543;
+ sd["reason"] = "error for testing";
+
+ setupTheServer();
+
+ LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult());
+ runThePump();
+ ensureStatusError();
+ ensure_contains("reason", mReason, sd["reason"]);
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<6>()
+ {
+ setupTheServer();
+
+ LLHTTPClient::get("http://localhost:8888/test/timeout", newResult());
+ runThePump(1.0f);
+ killServer();
+ runThePump();
+ ensureStatusError();
+ ensure_equals("reason", mReason, "STATUS_ERROR");
+ }
+}
diff --git a/indra/test/llhttpnode_tut.cpp b/indra/test/llhttpnode_tut.cpp
new file mode 100644
index 0000000000..9350645d2a
--- /dev/null
+++ b/indra/test/llhttpnode_tut.cpp
@@ -0,0 +1,409 @@
+/**
+ * @file lliohttpserver_tut.cpp
+ * @date May 2006
+ * @brief HTTP server unit tests
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llhttpnode.h"
+#include "llsdhttpserver.h"
+
+namespace tut
+{
+ struct HTTPNodeTestData
+ {
+ LLHTTPNode mRoot;
+ LLSD mContext;
+
+ const LLSD& context() { return mContext; }
+
+ std::string remainderPath()
+ {
+ std::ostringstream pathOutput;
+ bool addSlash = false;
+
+ LLSD& remainder = mContext["request"]["remainder"];
+ for (LLSD::array_const_iterator i = remainder.beginArray();
+ i != remainder.endArray();
+ ++i)
+ {
+ if (addSlash) { pathOutput << '/'; }
+ pathOutput << i->asString();
+ addSlash = true;
+ }
+
+ return pathOutput.str();
+ }
+
+ void ensureRootTraversal(const std::string& path,
+ const LLHTTPNode* expectedNode,
+ const char* expectedRemainder)
+ {
+ mContext.clear();
+
+ const LLHTTPNode* actualNode = mRoot.traverse(path, mContext);
+
+ ensure_equals("traverse " + path + " node",
+ actualNode, expectedNode);
+ ensure_equals("traverse " + path + " remainder",
+ remainderPath(), expectedRemainder);
+ }
+
+ class Response : public LLHTTPNode::Response
+ {
+ public:
+ static LLPointer<Response> create() {return new Response();}
+
+ LLSD mResult;
+
+ void result(const LLSD& result) { mResult = result; }
+ void status(S32 code, const std::string& message) { }
+
+ private:
+ Response() {;} // Must be accessed through LLPointer.
+ };
+
+ typedef LLPointer<Response> ResponsePtr;
+
+ LLSD get(const std::string& path)
+ {
+ mContext.clear();
+ const LLHTTPNode* node = mRoot.traverse(path, mContext);
+ ensure(path + " found", node != NULL);
+
+ ResponsePtr response = Response::create();
+ node->get(LLHTTPNode::ResponsePtr(response), mContext);
+ return response->mResult;
+ }
+
+ LLSD post(const std::string& path, const LLSD& input)
+ {
+ mContext.clear();
+ const LLHTTPNode* node = mRoot.traverse(path, mContext);
+ ensure(path + " found", node != NULL);
+
+ ResponsePtr response = Response::create();
+ node->post(LLHTTPNode::ResponsePtr(response), mContext, input);
+ return response->mResult;
+ }
+
+ void ensureMemberString(const std::string& name,
+ const LLSD& actualMap, const std::string& member,
+ const std::string& expectedValue)
+ {
+ ensure_equals(name + " " + member,
+ actualMap[member].asString(), expectedValue);
+ }
+
+
+ void ensureInArray(const LLSD& actualArray,
+ const std::string& expectedValue)
+ {
+ LLSD::array_const_iterator i = actualArray.beginArray();
+ LLSD::array_const_iterator end = actualArray.endArray();
+
+ for (; i != end; ++i)
+ {
+ std::string path = i->asString();
+
+ if (path == expectedValue)
+ {
+ return;
+ }
+ }
+
+ fail("didn't find " + expectedValue);
+ }
+
+ };
+
+ typedef test_group<HTTPNodeTestData> HTTPNodeTestGroup;
+ typedef HTTPNodeTestGroup::object HTTPNodeTestObject;
+ HTTPNodeTestGroup httpNodeTestGroup("http node");
+
+ template<> template<>
+ void HTTPNodeTestObject::test<1>()
+ {
+ // traversal of the lone node
+
+ ensureRootTraversal("", &mRoot, "");
+ ensureRootTraversal("/", &mRoot, "");
+ ensureRootTraversal("foo", NULL, "foo");
+ ensureRootTraversal("foo/bar", NULL, "foo/bar");
+
+ ensure_equals("root of root", mRoot.rootNode(), &mRoot);
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<2>()
+ {
+ // simple traversal of a single node
+
+ LLHTTPNode* helloNode = new LLHTTPNode;
+ mRoot.addNode("hello", helloNode);
+
+ ensureRootTraversal("hello", helloNode, "");
+ ensureRootTraversal("/hello", helloNode, "");
+ ensureRootTraversal("hello/", helloNode, "");
+ ensureRootTraversal("/hello/", helloNode, "");
+
+ ensureRootTraversal("hello/there", NULL, "there");
+
+ ensure_equals("root of hello", helloNode->rootNode(), &mRoot);
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<3>()
+ {
+ // traversal of mutli-branched tree
+
+ LLHTTPNode* greekNode = new LLHTTPNode;
+ LLHTTPNode* alphaNode = new LLHTTPNode;
+ LLHTTPNode* betaNode = new LLHTTPNode;
+ LLHTTPNode* gammaNode = new LLHTTPNode;
+
+ greekNode->addNode("alpha", alphaNode);
+ greekNode->addNode("beta", betaNode);
+ greekNode->addNode("gamma", gammaNode);
+ mRoot.addNode("greek", greekNode);
+
+ LLHTTPNode* hebrewNode = new LLHTTPNode;
+ LLHTTPNode* alephNode = new LLHTTPNode;
+
+ hebrewNode->addNode("aleph", alephNode);
+ mRoot.addNode("hebrew", hebrewNode);
+
+ ensureRootTraversal("greek/alpha", alphaNode, "");
+ ensureRootTraversal("greek/beta", betaNode, "");
+ ensureRootTraversal("greek/delta", NULL, "delta");
+ ensureRootTraversal("greek/gamma", gammaNode, "");
+ ensureRootTraversal("hebrew/aleph", alephNode, "");
+
+ ensure_equals("root of greek", greekNode->rootNode(), &mRoot);
+ ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
+ ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
+ ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
+ ensure_equals("root of hebrew", hebrewNode->rootNode(), &mRoot);
+ ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<4>()
+ {
+ // automatic creation of parent nodes and not overriding existing nodes
+
+ LLHTTPNode* alphaNode = new LLHTTPNode;
+ LLHTTPNode* betaNode = new LLHTTPNode;
+ LLHTTPNode* gammaNode = new LLHTTPNode;
+ LLHTTPNode* gamma2Node = new LLHTTPNode;
+
+ mRoot.addNode("greek/alpha", alphaNode);
+ mRoot.addNode("greek/beta", betaNode);
+
+ mRoot.addNode("greek/gamma", gammaNode);
+ mRoot.addNode("greek/gamma", gamma2Node);
+
+ LLHTTPNode* alephNode = new LLHTTPNode;
+
+ mRoot.addNode("hebrew/aleph", alephNode);
+
+ ensureRootTraversal("greek/alpha", alphaNode, "");
+ ensureRootTraversal("greek/beta", betaNode, "");
+ ensureRootTraversal("greek/delta", NULL, "delta");
+ ensureRootTraversal("greek/gamma", gammaNode, "");
+ ensureRootTraversal("hebrew/aleph", alephNode, "");
+
+ ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
+ ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
+ ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
+ ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
+ }
+
+ class IntegerNode : public LLHTTPNode
+ {
+ public:
+ virtual void get(ResponsePtr response, const LLSD& context) const
+ {
+ int n = context["extra"]["value"];
+
+ LLSD info;
+ info["value"] = n;
+ info["positive"] = n > 0;
+ info["zero"] = n == 0;
+ info["negative"] = n < 0;
+
+ response->result(info);
+ }
+
+ virtual bool validate(const std::string& name, LLSD& context) const
+ {
+ int n;
+ std::istringstream i_stream(name);
+ i_stream >> n;
+
+ if (i_stream.fail() || i_stream.get() != EOF)
+ {
+ return false;
+ }
+
+ context["extra"]["value"] = n;
+ return true;
+ }
+ };
+
+ class SquareNode : public LLHTTPNode
+ {
+ public:
+ virtual void get(ResponsePtr response, const LLSD& context) const
+ {
+ int n = context["extra"]["value"];
+ response->result(n*n);
+ }
+ };
+
+ template<> template<>
+ void HTTPNodeTestObject::test<5>()
+ {
+ // wildcard nodes
+
+ LLHTTPNode* miscNode = new LLHTTPNode;
+ LLHTTPNode* iNode = new IntegerNode;
+ LLHTTPNode* sqNode = new SquareNode;
+
+ mRoot.addNode("test/misc", miscNode);
+ mRoot.addNode("test/<int>", iNode);
+ mRoot.addNode("test/<int>/square", sqNode);
+
+ ensureRootTraversal("test/42", iNode, "");
+ ensure_equals("stored integer",
+ context()["extra"]["value"].asInteger(), 42);
+
+ ensureRootTraversal("test/bob", NULL, "bob");
+ ensure("nothing stored",
+ context()["extra"]["value"].isUndefined());
+
+ ensureRootTraversal("test/3/square", sqNode, "");
+ ResponsePtr response = Response::create();
+ sqNode->get(LLHTTPNode::ResponsePtr(response), context());
+ ensure_equals("square result", response->mResult.asInteger(), 9);
+ }
+
+ class AlphaNode : public LLHTTPNode
+ {
+ public:
+ virtual bool handles(const LLSD& remainder, LLSD& context) const
+ {
+ LLSD::array_const_iterator i = remainder.beginArray();
+ LLSD::array_const_iterator end = remainder.endArray();
+
+ for (; i != end; ++i)
+ {
+ std::string s = i->asString();
+ if (s.empty() || s[0] != 'a')
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+
+ template<> template<>
+ void HTTPNodeTestObject::test<6>()
+ {
+ // nodes that handle remainders
+
+ LLHTTPNode* miscNode = new LLHTTPNode;
+ LLHTTPNode* aNode = new AlphaNode;
+ LLHTTPNode* zNode = new LLHTTPNode;
+
+ mRoot.addNode("test/misc", miscNode);
+ mRoot.addNode("test/alpha", aNode);
+ mRoot.addNode("test/alpha/zebra", zNode);
+
+ ensureRootTraversal("test/alpha", aNode, "");
+ ensureRootTraversal("test/alpha/abe", aNode, "abe");
+ ensureRootTraversal("test/alpha/abe/amy", aNode, "abe/amy");
+ ensureRootTraversal("test/alpha/abe/bea", NULL, "abe/bea");
+ ensureRootTraversal("test/alpha/bob", NULL, "bob");
+ ensureRootTraversal("test/alpha/zebra", zNode, "");
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<7>()
+ {
+ // test auto registration
+
+ LLHTTPStandardServices::useServices();
+ LLHTTPRegistrar::buildAllServices(mRoot);
+
+ {
+ LLSD result = get("web/hello");
+ ensure_equals("hello result", result.asString(), "hello");
+ }
+ {
+ LLSD stuff = 3.14159;
+ LLSD result = post("web/echo", stuff);
+ ensure_equals("echo result", result, stuff);
+ }
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<8>()
+ {
+ // test introspection
+
+ LLHTTPRegistrar::buildAllServices(mRoot);
+
+ mRoot.addNode("test/misc", new LLHTTPNode);
+ mRoot.addNode("test/<int>", new IntegerNode);
+ mRoot.addNode("test/<int>/square", new SquareNode);
+
+ const LLSD result = get("web/server/api");
+
+ ensure("result is array", result.isArray());
+ ensure("result size", result.size() >= 2);
+
+ ensureInArray(result, "web/echo");
+ ensureInArray(result, "web/hello");
+ ensureInArray(result, "test/misc");
+ ensureInArray(result, "test/<int>");
+ ensureInArray(result, "test/<int>/square");
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<9>()
+ {
+ // test introspection details
+
+ LLHTTPRegistrar::buildAllServices(mRoot);
+
+ const LLSD helloDetails = get("web/server/api/web/hello");
+
+ ensure_contains("hello description",
+ helloDetails["description"].asString(), "hello");
+ ensure_equals("method name", helloDetails["api"][0].asString(), std::string("GET"));
+ ensureMemberString("hello", helloDetails, "output", "\"hello\"");
+ ensure_contains("hello __file__",
+ helloDetails["__file__"].asString(), "llsdhttpserver.cpp");
+ ensure("hello line", helloDetails["__line__"].isInteger());
+
+
+ const LLSD echoDetails = get("web/server/api/web/echo");
+
+ ensure_contains("echo description",
+ echoDetails["description"].asString(), "echo");
+ ensure_equals("method name", echoDetails["api"][0].asString(), std::string("POST"));
+ ensureMemberString("echo", echoDetails, "input", "<any>");
+ ensureMemberString("echo", echoDetails, "output", "<the input>");
+ ensure_contains("echo __file__",
+ echoDetails["__file__"].asString(), "llsdhttpserver.cpp");
+ ensure("echo", echoDetails["__line__"].isInteger());
+ }
+}
diff --git a/indra/test/lliohttpserver_tut.cpp b/indra/test/lliohttpserver_tut.cpp
new file mode 100644
index 0000000000..1284a1fc0d
--- /dev/null
+++ b/indra/test/lliohttpserver_tut.cpp
@@ -0,0 +1,284 @@
+/**
+ * @file lliohttpserver_tut.cpp
+ * @date May 2006
+ * @brief HTTP server unit tests
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llbufferstream.h"
+#include "lliohttpserver.h"
+#include "llsdhttpserver.h"
+#include "llsdserialize.h"
+
+#include "llpipeutil.h"
+
+
+namespace tut
+{
+ class HTTPServiceTestData
+ {
+ public:
+ class DelayedEcho : public LLHTTPNode
+ {
+ HTTPServiceTestData* mTester;
+
+ public:
+ DelayedEcho(HTTPServiceTestData* tester) : mTester(tester) { }
+
+ void post(ResponsePtr response, const LLSD& context, const LLSD& input) const
+ {
+ ensure("response already set", mTester->mResponse == ResponsePtr(NULL));
+ mTester->mResponse = response;
+ mTester->mResult = input;
+ }
+ };
+
+ class WireHello : public LLIOPipe
+ {
+ protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+ {
+ if(!eos) return STATUS_BREAK;
+ LLSD sd = "yo!";
+ LLBufferStream ostr(channels, buffer.get());
+ ostr << LLSDXMLStreamer(sd);
+ return STATUS_DONE;
+ }
+ };
+
+ HTTPServiceTestData()
+ : mResponse(NULL)
+ {
+ LLHTTPStandardServices::useServices();
+ LLHTTPRegistrar::buildAllServices(mRoot);
+ mRoot.addNode("/delayed/echo", new DelayedEcho(this));
+ mRoot.addNode("/wire/hello", new LLHTTPNodeForPipe<WireHello>);
+ }
+
+ LLHTTPNode mRoot;
+ LLHTTPNode::ResponsePtr mResponse;
+ LLSD mResult;
+
+ void pumpPipe(LLPumpIO* pump, S32 iterations)
+ {
+ while(iterations > 0)
+ {
+ pump->pump();
+ pump->callback();
+ --iterations;
+ }
+ }
+
+ std::string makeRequest(
+ const std::string& name,
+ const std::string& httpRequest,
+ bool timeout = false)
+ {
+ LLPipeStringInjector* injector = new LLPipeStringInjector(httpRequest);
+ LLPipeStringExtractor* extractor = new LLPipeStringExtractor();
+
+ apr_pool_t* pool;
+ apr_pool_create(&pool, NULL);
+
+ LLPumpIO* pump;
+ pump = new LLPumpIO(pool);
+
+ LLPumpIO::chain_t chain;
+ LLSD context;
+
+ chain.push_back(LLIOPipe::ptr_t(injector));
+ LLCreateHTTPPipe(chain, mRoot);
+ chain.push_back(LLIOPipe::ptr_t(extractor));
+
+ pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
+
+ pumpPipe(pump, 10);
+ if(mResponse && (! timeout))
+ {
+ mResponse->result(mResult);
+ mResponse = NULL;
+ }
+ pumpPipe(pump, 10);
+
+ std::string httpResult = extractor->string();
+
+ chain.clear();
+ delete pump;
+ apr_pool_destroy(pool);
+
+ if(mResponse && timeout)
+ {
+ mResponse->result(mResult);
+ mResponse = NULL;
+ }
+
+ return httpResult;
+ }
+
+ std::string httpGET(const std::string& uri,
+ bool timeout = false)
+ {
+ std::string httpRequest = "GET " + uri + " HTTP/1.0\r\n\r\n";
+ return makeRequest(uri, httpRequest, timeout);
+ }
+
+ std::string httpPOST(const std::string& uri,
+ const std::string& body,
+ bool timeout,
+ const std::string& evilExtra = "")
+ {
+ std::ostringstream httpRequest;
+ httpRequest << "POST " + uri + " HTTP/1.0\r\n";
+ httpRequest << "Content-Length: " << body.size() << "\r\n";
+ httpRequest << "\r\n";
+ httpRequest << body;
+ httpRequest << evilExtra;
+
+ return makeRequest(uri, httpRequest.str(), timeout);
+ }
+
+ std::string httpPOST(const std::string& uri,
+ const std::string& body,
+ const std::string& evilExtra = "")
+ {
+ bool timeout = false;
+ return httpPOST(uri, body, timeout, evilExtra);
+ }
+ };
+
+ typedef test_group<HTTPServiceTestData> HTTPServiceTestGroup;
+ typedef HTTPServiceTestGroup::object HTTPServiceTestObject;
+ HTTPServiceTestGroup httpServiceTestGroup("http service");
+
+ template<> template<>
+ void HTTPServiceTestObject::test<1>()
+ {
+ std::string result = httpGET("web/hello");
+
+ ensure_starts_with("web/hello status", result,
+ "HTTP/1.0 200 OK\r\n");
+
+ ensure_contains("web/hello content type", result,
+ "Content-Type: application/xml\r\n");
+
+ ensure_contains("web/hello content length", result,
+ "Content-Length: 36\r\n");
+
+ ensure_contains("web/hello content", result,
+ "\r\n"
+ "<llsd><string>hello</string></llsd>"
+ );
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<2>()
+ {
+ // test various HTTP errors
+
+ std::string actual;
+
+ actual = httpGET("web/missing");
+ ensure_starts_with("web/missing 404", actual,
+ "HTTP/1.0 404 Not Found\r\n");
+
+ actual = httpGET("web/echo");
+ ensure_starts_with("web/echo 405", actual,
+ "HTTP/1.0 405 Method Not Allowed\r\n");
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<3>()
+ {
+ // test POST & content-length handling
+
+ std::string result;
+
+ result = httpPOST("web/echo",
+ "<llsd><integer>42</integer></llsd>");
+
+ ensure_starts_with("web/echo status", result,
+ "HTTP/1.0 200 OK\r\n");
+
+ ensure_contains("web/echo content type", result,
+ "Content-Type: application/xml\r\n");
+
+ ensure_contains("web/echo content length", result,
+ "Content-Length: 35\r\n");
+
+ ensure_contains("web/hello content", result,
+ "\r\n"
+ "<llsd><integer>42</integer></llsd>"
+ );
+
+/* TO DO: this test doesn't pass!!
+
+ result = httpPOST("web/echo",
+ "<llsd><string>evil</string></llsd>",
+ "really! evil!!!");
+
+ ensure_equals("web/echo evil result", result,
+ "HTTP/1.0 200 OK\r\n"
+ "Content-Length: 34\r\n"
+ "\r\n"
+ "<llsd><string>evil</string></llsd>"
+ );
+*/
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<4>()
+ {
+ // test calling things based on pipes
+
+ std::string result;
+
+ result = httpGET("wire/hello");
+
+ ensure_contains("wire/hello", result, "yo!");
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<5>()
+ {
+ // test timeout before async response
+ std::string result;
+
+ bool timeout = true;
+ result = httpPOST("delayed/echo",
+ "<llsd><string>agent99</string></llsd>", timeout);
+
+ ensure_equals("timeout delayed/echo status", result, std::string(""));
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<6>()
+ {
+ // test delayed service
+ std::string result;
+
+ result = httpPOST("delayed/echo",
+ "<llsd><string>agent99</string></llsd>");
+
+ ensure_starts_with("delayed/echo status", result,
+ "HTTP/1.0 200 OK\r\n");
+
+ ensure_contains("delayed/echo content", result,
+ "\r\n"
+ "<llsd><string>agent99</string></llsd>"
+ );
+ }
+
+ /* TO DO:
+ test generation of not found and method not allowed errors
+ */
+}
diff --git a/indra/test/llpipeutil.cpp b/indra/test/llpipeutil.cpp
new file mode 100644
index 0000000000..e4389aaa33
--- /dev/null
+++ b/indra/test/llpipeutil.cpp
@@ -0,0 +1,139 @@
+/**
+ * @file llpipeutil.cpp
+ * @date 2006-05-18
+ * @brief Utility pipe fittings for injecting and extracting strings
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llpipeutil.h"
+
+#include <stdlib.h>
+
+#include "llbufferstream.h"
+#include "llframetimer.h"
+#include "llpumpio.h"
+#include "llrand.h"
+#include "lltimer.h"
+
+F32 pump_loop(LLPumpIO* pump, F32 seconds)
+{
+ LLTimer timer;
+ timer.setTimerExpirySec(seconds);
+ while(!timer.hasExpired())
+ {
+ LLFrameTimer::updateFrameTime();
+ pump->pump();
+ pump->callback();
+ }
+ return timer.getElapsedTimeF32();
+}
+
+//virtual
+LLIOPipe::EStatus LLPipeStringInjector::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ buffer->append(channels.out(), (U8*) mString.data(), mString.size());
+ eos = true;
+ return STATUS_DONE;
+}
+
+
+LLIOPipe::EStatus LLPipeStringExtractor::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ LLBufferStream istr(channels, buffer.get());
+ std::ostringstream ostr;
+ while (istr.good())
+ {
+ char buf[1024];
+ istr.read(buf, sizeof(buf));
+ ostr.write(buf, istr.gcount());
+ }
+ mString = ostr.str();
+ mDone = true;
+
+ return STATUS_DONE;
+}
+
+
+// virtual
+LLIOPipe::EStatus LLIOFuzz::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ while(mByteCount)
+ {
+ std::vector<U8> data;
+ data.reserve(10000);
+ int size = llmin(10000, mByteCount);
+ std::generate_n(
+ std::back_insert_iterator< std::vector<U8> >(data),
+ size,
+ rand);
+ buffer->append(channels.out(), &data[0], size);
+ mByteCount -= size;
+ }
+ return STATUS_OK;
+}
+
+struct random_ascii_generator
+{
+ random_ascii_generator() {}
+ U8 operator()()
+ {
+ int rv = rand();
+ rv %= (127 - 32);
+ rv += 32;
+ return rv;
+ }
+};
+
+// virtual
+LLIOPipe::EStatus LLIOASCIIFuzz::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ while(mByteCount)
+ {
+ std::vector<U8> data;
+ data.reserve(10000);
+ int size = llmin(10000, mByteCount);
+ std::generate_n(
+ std::back_insert_iterator< std::vector<U8> >(data),
+ size,
+ random_ascii_generator());
+ buffer->append(channels.out(), &data[0], size);
+ mByteCount -= size;
+ }
+ return STATUS_OK;
+}
+
+// virtual
+LLIOPipe::EStatus LLIONull::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ return STATUS_OK;
+}
diff --git a/indra/test/llpipeutil.h b/indra/test/llpipeutil.h
new file mode 100644
index 0000000000..e63f49f02f
--- /dev/null
+++ b/indra/test/llpipeutil.h
@@ -0,0 +1,125 @@
+/**
+ * @file llpipeutil.h
+ * @date 2006-05-18
+ * @brief Utility pipe fittings for injecting and extracting strings
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPIPEUTIL_H
+#define LL_LLPIPEUTIL_H
+
+#include "lliopipe.h"
+
+
+/**
+ * @brief Simple function which pumps for the specified time.
+ */
+F32 pump_loop(LLPumpIO* pump, F32 seconds);
+
+/**
+ * @brief Simple class which writes a string and then marks the stream
+ * as done.
+ */
+class LLPipeStringInjector : public LLIOPipe
+{
+public:
+ LLPipeStringInjector(const std::string& string)
+ : mString(string)
+ { }
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ std::string mString;
+};
+
+
+class LLPipeStringExtractor : public LLIOPipe
+{
+public:
+ LLPipeStringExtractor() : mDone(false) { }
+
+ bool done() { return mDone; }
+ std::string string() { return mString; }
+
+protected:
+ // LLIOPipe API implementation.
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ LLIOPipe::buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ bool mDone;
+ std::string mString;
+};
+
+/**
+ * @brief Generate a specified number of bytes of random data
+ */
+class LLIOFuzz : public LLIOPipe
+{
+public:
+ LLIOFuzz(int byte_count) : mByteCount(byte_count) {}
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ int mByteCount;
+};
+
+/**
+ * @brief Generate some ascii fuz
+ */
+class LLIOASCIIFuzz : public LLIOPipe
+{
+public:
+ LLIOASCIIFuzz(int byte_count) : mByteCount(byte_count) {}
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ int mByteCount;
+};
+
+
+/**
+ * @brief Pipe that does nothing except return STATUS_OK
+ */
+class LLIONull : public LLIOPipe
+{
+public:
+ LLIONull() {}
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+};
+
+#endif // LL_LLPIPEUTIL_H
diff --git a/indra/test/llsd_new_tut.cpp b/indra/test/llsd_new_tut.cpp
new file mode 100644
index 0000000000..e99da7195a
--- /dev/null
+++ b/indra/test/llsd_new_tut.cpp
@@ -0,0 +1,821 @@
+/**
+ * @file llsd_new_tut.cpp
+ * @date February 2006
+ * @brief LLSD unit tests
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <math.h>
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llsd.h"
+
+namespace tut
+{
+ template<class T>
+ class SDTraits
+ {
+ protected:
+ typedef T (LLSD::*Getter)() const;
+
+ LLSD::Type type;
+ Getter getter;
+
+ public:
+ SDTraits();
+
+ T get(const LLSD& actual)
+ {
+ return (actual.*getter)();
+ }
+
+ bool checkType(const LLSD& actual)
+ {
+ return actual.type() == type;
+ }
+ };
+
+ template<>
+ SDTraits<LLSD::Boolean>::SDTraits()
+ : type(LLSD::TypeBoolean), getter(&LLSD::asBoolean)
+ { }
+
+ template<>
+ SDTraits<LLSD::Integer>::SDTraits()
+ : type(LLSD::TypeInteger), getter(&LLSD::asInteger)
+ { }
+
+ template<>
+ SDTraits<LLSD::Real>::SDTraits()
+ : type(LLSD::TypeReal), getter(&LLSD::asReal)
+ { }
+
+ template<>
+ SDTraits<LLSD::UUID>::SDTraits()
+ : type(LLSD::TypeUUID), getter(&LLSD::asUUID)
+ { }
+
+ template<>
+ SDTraits<LLSD::String>::SDTraits()
+ : type(LLSD::TypeString), getter(&LLSD::asString)
+ { }
+
+ template<>
+ class SDTraits<LLString> : public SDTraits<LLSD::String>
+ { };
+
+ template<>
+ class SDTraits<const char*> : public SDTraits<LLSD::String>
+ { };
+
+ template<>
+ SDTraits<LLSD::Date>::SDTraits()
+ : type(LLSD::TypeDate), getter(&LLSD::asDate)
+ { }
+
+ template<>
+ SDTraits<LLSD::URI>::SDTraits()
+ : type(LLSD::TypeURI), getter(&LLSD::asURI)
+ { }
+
+ template<>
+ SDTraits<LLSD::Binary>::SDTraits()
+ : type(LLSD::TypeBinary), getter(&LLSD::asBinary)
+ { }
+
+ class SDCleanupCheck
+ {
+ private:
+ U32 mOutstandingAtStart;
+ public:
+ SDCleanupCheck() : mOutstandingAtStart(LLSD::outstandingCount()) { }
+ ~SDCleanupCheck()
+ {
+ ensure_equals("SDCleanupCheck",
+ LLSD::outstandingCount(), mOutstandingAtStart);
+ }
+ };
+
+ class SDAllocationCheck : public SDCleanupCheck
+ {
+ private:
+ std::string mMessage;
+ U32 mExpectedAllocations;
+ U32 mAllocationAtStart;
+ public:
+ SDAllocationCheck(const std::string& message, int expectedAllocations)
+ : mMessage(message),
+ mExpectedAllocations(expectedAllocations),
+ mAllocationAtStart(LLSD::allocationCount())
+ { }
+ ~SDAllocationCheck()
+ {
+ ensure_equals(mMessage + " SDAllocationCheck",
+ LLSD::allocationCount() - mAllocationAtStart,
+ mExpectedAllocations);
+ }
+ };
+
+ struct SDTestData {
+ template<class T>
+ static void ensureTypeAndValue(const char* msg, const LLSD& actual,
+ T expectedValue)
+ {
+ SDTraits<T> traits;
+
+ std::string s(msg);
+
+ ensure( s + " type", traits.checkType(actual));
+ ensure_equals( s + " value", traits.get(actual), expectedValue);
+ }
+ };
+
+ typedef test_group<SDTestData> SDTestGroup;
+ typedef SDTestGroup::object SDTestObject;
+
+ SDTestGroup sdTestGroup("LLSD(new)");
+
+ template<> template<>
+ void SDTestObject::test<1>()
+ // construction and test of undefined
+ {
+ SDCleanupCheck check;
+
+ LLSD u;
+ ensure("is undefined", u.isUndefined());
+ }
+
+ template<> template<>
+ void SDTestObject::test<2>()
+ // setting and fetching scalar types
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ v = true; ensureTypeAndValue("set true", v, true);
+ v = false; ensureTypeAndValue("set false", v, false);
+ v = true; ensureTypeAndValue("set true again", v, true);
+
+ v = 42; ensureTypeAndValue("set to 42", v, 42);
+ v = 0; ensureTypeAndValue("set to zero", v, 0);
+ v = -12345; ensureTypeAndValue("set to neg", v, -12345);
+ v = 2000000000; ensureTypeAndValue("set to big", v, 2000000000);
+
+ v = 3.14159265359;
+ ensureTypeAndValue("set to pi", v, 3.14159265359);
+ ensure_not_equals("isn't float", v.asReal(),
+ (float)3.14159265359);
+ v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256);
+
+ LLUUID nullUUID;
+ LLUUID newUUID;
+ newUUID.generate();
+
+ v = nullUUID; ensureTypeAndValue("set to null UUID", v, nullUUID);
+ v = newUUID; ensureTypeAndValue("set to new UUID", v, newUUID);
+ v = nullUUID; ensureTypeAndValue("set to null again", v, nullUUID);
+
+ // strings must be tested with three (!) types of string objects
+ std::string s = "now is the time";
+ LLString ls = "for all good zorks";
+ const char* cs = "to come to the air of their planet";
+
+ v = s; ensureTypeAndValue("set to std::string", v, s);
+ v = ls; ensureTypeAndValue("set to LLString", v, ls);
+ v = cs; ensureTypeAndValue("set to const char*", v, cs);
+
+ LLDate epoch;
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+
+ v = epoch; ensureTypeAndValue("set to epoch", v, epoch);
+ v = aDay; ensureTypeAndValue("set to a day", v, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+
+ v = path; ensureTypeAndValue("set to a uri", v, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+
+ v = data; ensureTypeAndValue("set to data", v, data);
+
+ v.clear();
+ ensure("reset to undefined", v.type() == LLSD::TypeUndefined);
+ }
+
+ template<> template<>
+ void SDTestObject::test<3>()
+ // construction via scalar values
+ // tests both constructor and initialize forms
+ {
+ SDCleanupCheck check;
+
+ LLSD b1(true); ensureTypeAndValue("construct boolean", b1, true);
+ LLSD b2 = true; ensureTypeAndValue("initialize boolean", b2, true);
+ LLSD i1(42); ensureTypeAndValue("construct int", i1, 42);
+ LLSD i2 =42; ensureTypeAndValue("initialize int", i2, 42);
+ LLSD d1(1.2); ensureTypeAndValue("construct double", d1, 1.2);
+ LLSD d2 = 1.2; ensureTypeAndValue("initialize double", d2, 1.2);
+
+ LLUUID newUUID;
+ newUUID.generate();
+ LLSD u1(newUUID);
+ ensureTypeAndValue("construct UUID", u1, newUUID);
+ LLSD u2 = newUUID;
+ ensureTypeAndValue("initialize UUID", u2, newUUID);
+
+ LLSD ss1(std::string("abc"));
+ ensureTypeAndValue("construct std::string", ss1, "abc");
+ LLSD ss2 = std::string("abc");
+ ensureTypeAndValue("initialize std::string",ss2, "abc");
+ LLSD sl1(LLString("def"));
+ ensureTypeAndValue("construct LLString", sl1, "def");
+ LLSD sl2 = LLString("def");
+ ensureTypeAndValue("initialize LLString", sl2, "def");
+ LLSD sc1("ghi");
+ ensureTypeAndValue("construct const char*", sc1, "ghi");
+ LLSD sc2 = "ghi";
+ ensureTypeAndValue("initialize const char*",sc2, "ghi");
+
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+ LLSD t1(aDay); ensureTypeAndValue("construct LLDate", t1, aDay);
+ LLSD t2 = aDay; ensureTypeAndValue("initialize LLDate", t2, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLSD p1(path); ensureTypeAndValue("construct LLURI", p1, path);
+ LLSD p2 = path; ensureTypeAndValue("initialize LLURI", p2, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+ LLSD x1(data); ensureTypeAndValue("construct vector<U8>", x1, data);
+ LLSD x2 = data; ensureTypeAndValue("initialize vector<U8>", x2, data);
+ }
+
+ void checkConversions(const char* msg, const LLSD& v,
+ LLSD::Boolean eBoolean, LLSD::Integer eInteger,
+ LLSD::Real eReal, const LLSD::String& eString)
+ {
+ std::string s(msg);
+
+ ensure_equals(s+" to bool", v.asBoolean(), eBoolean);
+ ensure_equals(s+" to int", v.asInteger(), eInteger);
+ if (eReal == eReal)
+ {
+ ensure_equals(s+" to real", v.asReal(), eReal);
+ ensure_equals(s+" to string", v.asString(), eString);
+ }
+ else
+ {
+// TODO: Fix on windows....
+#ifndef LL_WINDOWS
+# if !defined(fpclassify) && __GNUC__ >= 3
+# define FPCLASSIFY_NAMESPACE std::
+# else
+# define FPCLASSIFY_NAMESPACE
+# endif
+ int left = FPCLASSIFY_NAMESPACE fpclassify(v.asReal());
+ int right = FPCLASSIFY_NAMESPACE fpclassify(eReal);
+
+ ensure_equals(s+" to real", left, right);
+ ensure_equals(s+" to string", v.asString(), eString);
+#endif
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<4>()
+ // conversion between undefined and basic scalar types:
+ // boolean, integer, real and string
+ {
+ SDCleanupCheck check;
+
+ LLSD v; checkConversions("untitled", v, false, 0, 0.0, "");
+
+ v = false; checkConversions("false", v, false, 0, 0.0, "");
+ v = true; checkConversions("true", v, true, 1, 1.0, "true");
+
+ v = 0; checkConversions("zero", v, false, 0, 0.0, "0");
+ v = 1; checkConversions("one", v, true, 1, 1.0, "1");
+ v = -33; checkConversions("neg33", v, true, -33, -33.0, "-33");
+
+ v = 0.0; checkConversions("0.0", v, false, 0, 0.0, "0");
+ v = 0.5; checkConversions("point5", v, true, 0, 0.5, "0.5");
+ v = 0.9; checkConversions("point9", v, true, 0, 0.9, "0.9");
+ v = -3.9; checkConversions("neg3dot9", v, true, -3, -3.9, "-3.9");
+ v = sqrt(-1.0); checkConversions("NaN", v, false, 0, sqrt(-1.0), "nan");
+
+ v = ""; checkConversions("empty", v, false, 0, 0.0, "");
+ v = "0"; checkConversions("digit0", v, true, 0, 0.0, "0");
+ v = "10"; checkConversions("digit10", v, true, 10, 10.0, "10");
+ v = "-2.345"; checkConversions("decdigits", v,
+ true, -2, -2.345, "-2.345");
+ v = "apple"; checkConversions("apple", v, true, 0, 0.0, "apple");
+ v = "33bob"; checkConversions("digialpha", v, true, 0, 0.0, "33bob");
+ v = " "; checkConversions("space", v, true, 0, 0.0, " ");
+ v = "\n"; checkConversions("newline", v, true, 0, 0.0, "\n");
+ }
+
+ template<class T>
+ void checkRoundTrip(const std::string& msg, const LLSD& actual,
+ const char* sExpected, T vExpected)
+ {
+ std::string str = actual.asString();
+
+ if (sExpected) {
+ ensure_equals(msg + " string", str, sExpected);
+ }
+
+ LLSD u(str);
+ SDTraits<T> traits;
+
+ ensure_equals(msg + " value", traits.get(u), vExpected);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<5>()
+ // conversion of String to and from UUID, Date and URI.
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ LLUUID nullUUID;
+ LLUUID someUUID;
+ someUUID.generate();
+
+ v = nullUUID; checkRoundTrip("null uuid", v,
+ "00000000-0000-0000-0000-000000000000", nullUUID);
+ v = someUUID; checkRoundTrip("random uuid", v, 0, someUUID);
+
+ LLDate epoch;
+ LLDate beta("2003-04-30T04:00:00Z");
+ LLDate oneOh("2003-06-23T04:00:00Z");
+
+ v = epoch; checkRoundTrip("epoch date", v, 0, epoch);
+ v = beta; checkRoundTrip("beta date", v,
+ "2003-04-30T04:00:00Z", beta);
+ v = oneOh; checkRoundTrip("1.0 date", v,
+ "2003-06-23T04:00:00Z", oneOh);
+
+ LLURI empty;
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLURI mail("mailto:zero.linden@secondlife.com");
+
+ v = empty; checkRoundTrip("empty URI", v, 0, empty);
+ v = path; checkRoundTrip("path URI", v,
+ "http://slurl.com/secondlife/Ambleside/57/104/26/",
+ path);
+ v = mail; checkRoundTrip("mail URI", v,
+ "mailto:zero.linden@secondlife.com", mail);
+ }
+
+ template<> template<>
+ void SDTestObject::test<6>()
+ // copy construction and assignment
+ // checking for shared values after constr. or assignment
+ // checking in both the same type and change of type case
+ {
+ SDCleanupCheck check;
+
+ {
+ LLSD v = 42;
+
+ LLSD w0(v);
+ ensureTypeAndValue("int constr.", w0, 42);
+
+ LLSD w1(v);
+ w1 = 13;
+ ensureTypeAndValue("int constr. change case 1", w1, 13);
+ ensureTypeAndValue("int constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = 7;
+ ensureTypeAndValue("int constr. change case 3", w2, 42);
+ ensureTypeAndValue("int constr. change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1(v);
+ w1 = "bob";
+ ensureTypeAndValue("string constr. change case 1", w1, "bob");
+ ensureTypeAndValue("string constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = "amy";
+ ensureTypeAndValue("string constr. change case 3", w2, 42);
+ ensureTypeAndValue("string constr. change case 4", v, "amy");
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w0;
+ w0 = v;
+ ensureTypeAndValue("int assign", w0, 42);
+
+ LLSD w1;
+ w1 = v;
+ w1 = 13;
+ ensureTypeAndValue("int assign change case 1", w1, 13);
+ ensureTypeAndValue("int assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = 7;
+ ensureTypeAndValue("int assign change case 3", w2, 42);
+ ensureTypeAndValue("int assign change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1;
+ w1 = v;
+ w1 = "bob";
+ ensureTypeAndValue("string assign change case 1", w1, "bob");
+ ensureTypeAndValue("string assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = "amy";
+ ensureTypeAndValue("string assign change case 3", w2, 42);
+ ensureTypeAndValue("string assign change case 4", v, "amy");
+ }
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<7>()
+ // Test assignment and casting to various scalar types. These
+ // assignments should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b = false;
+ b = v; ensure_equals("assign to bool", b, true);
+ b = (bool)v; ensure_equals("cast to bool", b, true);
+
+ int i = 99;
+ i = v; ensure_equals("assign to int", i, 42);
+ i = (int)v; ensure_equals("cast to int", i, 42);
+
+ double d = 3.14159;
+ d = v; ensure_equals("assign to double", d, 42.375);
+ d = (double)v; ensure_equals("cast to double", d, 42.375);
+
+ std::string s = "yo";
+// SAD s = v; ensure_equals("assign to string", s, " 42.375");
+ s = (std::string)v; ensure_equals("cast to string", s, " 42.375");
+
+ LLString t = "yo";
+// SAD t = v; ensure_equals("assign to LLString", t, " 42.375");
+ t = (LLString)v; ensure_equals("cast to LLString", t, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID u;
+ u = v;
+ ensure_equals("assign to LLUUID", u, LLUUID(uuidStr));
+// SAD u = (LLUUID)v;
+// ensure_equals("cast to LLUUID", u, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date;
+ date = v;
+ ensure_equals("assign to LLDate", date.asString(), dateStr);
+// SAD date = (LLDate)v;
+// ensure_equals("cast to LLDate", date.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri;
+ uri = v;
+ ensure_equals("assign to LLURI", uri.asString(), uriStr);
+// SAD uri = (LLURI)v;
+// ensure_equals("cast to LLURI", uri.asString(), uriStr);
+ }
+
+ template<> template<>
+ void SDTestObject::test<8>()
+ // Test construction of various scalar types from LLSD.
+ // Test both construction and initialization forms.
+ // These should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b1(v); ensure_equals("contruct bool", b1, true);
+ bool b2 = v; ensure_equals("initialize bool", b2, true);
+
+ int i1(v); ensure_equals("contruct int", i1, 42);
+ int i2 = v; ensure_equals("initialize int", i2, 42);
+
+ double d1(v); ensure_equals("contruct double", d1, 42.375);
+ double d2 = v; ensure_equals("initialize double", d2, 42.375);
+
+ std::string s1(v);
+ std::string s2 = v;
+ ensure_equals("contruct string", s1, " 42.375");
+ ensure_equals("initialize string", s2, " 42.375");
+
+ LLString t1(v);
+ LLString t2 = v.asString(); // SAD
+ ensure_equals("contruct LLString", t1, " 42.375");
+ ensure_equals("initialize LLString", t2, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID uuid1(v.asUUID()); // SAD
+ LLUUID uuid2 = v;
+ ensure_equals("contruct LLUUID", uuid1, LLUUID(uuidStr));
+ ensure_equals("initialize LLUUID", uuid2, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date1(v.asDate()); // SAD
+ LLDate date2 = v;
+ ensure_equals("contruct LLDate", date1.asString(), dateStr);
+ ensure_equals("initialize LLDate", date2.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri1(v.asURI()); // SAD
+ LLURI uri2 = v;
+ ensure_equals("contruct LLURI", uri1.asString(), uriStr);
+ ensure_equals("initialize LLURI", uri2.asString(), uriStr);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<9>()
+ // test to make sure v is interpreted as a bool in a various
+ // scenarios.
+ {
+ SDCleanupCheck check;
+
+ LLSD v = "0";
+ // magic value that is interpreted as boolean true, but integer false!
+
+ ensure_equals("trinary operator bool", (v ? true : false), true);
+ ensure_equals("convert to int, then bool",
+ ((int)v ? true : false), false);
+
+ if(v)
+ {
+ ensure("if converted to bool", true);
+ }
+ else
+ {
+ fail("bool did not convert to a bool in if statement.");
+ }
+
+ if(!v)
+ {
+ fail("bool did not convert to a bool in negated if statement.");
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<10>()
+ // map operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure("undefined has no members", !v.has("amy"));
+ ensure("undefined get() is undefined", v.get("bob").isUndefined());
+
+ v = LLSD::emptyMap();
+ ensure("empty map is a map", v.isMap());
+ ensure("empty map has no members", !v.has("cam"));
+ ensure("empty map get() is undefined", v.get("don").isUndefined());
+
+ v.clear();
+ v.insert("eli", 43);
+ ensure("insert converts to map", v.isMap());
+ ensure("inserted key is present", v.has("eli"));
+ ensureTypeAndValue("inserted value", v.get("eli"), 43);
+
+ v.insert("fra", false);
+ ensure("first key still present", v.has("eli"));
+ ensure("second key is present", v.has("fra"));
+ ensureTypeAndValue("first value", v.get("eli"), 43);
+ ensureTypeAndValue("second value", v.get("fra"), false);
+
+ v.erase("eli");
+ ensure("first key now gone", !v.has("eli"));
+ ensure("second key still present", v.has("fra"));
+ ensure("first value gone", v.get("eli").isUndefined());
+ ensureTypeAndValue("second value sill there", v.get("fra"), false);
+
+ v.erase("fra");
+ ensure("second key now gone", !v.has("fra"));
+ ensure("second value gone", v.get("fra").isUndefined());
+
+ v["gil"] = (std::string)"good morning";
+ ensure("third key present", v.has("gil"));
+ ensureTypeAndValue("third key value", v.get("gil"), "good morning");
+
+ const LLSD& cv = v; // FIX ME IF POSSIBLE
+ ensure("missing key", cv["ham"].isUndefined());
+ ensure("key not present", !v.has("ham"));
+
+ LLSD w = 43;
+ const LLSD& cw = w; // FIX ME IF POSSIBLE
+ int i = cw["ian"];
+ ensureTypeAndValue("other missing value", i, 0);
+ ensure("other missing key", !w.has("ian"));
+ ensure("no conversion", w.isInteger());
+
+ LLSD x;
+ x = v;
+ ensure("copy map type", x.isMap());
+ ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning");
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<11>()
+ // array operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure_equals("undefined has no size", v.size(), 0);
+ ensure("undefined get() is undefined", v.get(0).isUndefined());
+
+ v = LLSD::emptyArray();
+ ensure("empty array is an array", v.isArray());
+ ensure_equals("empty array has no size", v.size(), 0);
+ ensure("empty map get() is undefined", v.get(0).isUndefined());
+
+ v.clear();
+ v.append(88);
+ v.append("noodle");
+ v.append(true);
+ ensure_equals("appened array size", v.size(), 3);
+ ensure("append array is an array", v.isArray());
+ ensureTypeAndValue("append 0", v[0], 88);
+ ensureTypeAndValue("append 1", v[1], "noodle");
+ ensureTypeAndValue("append 2", v[2], true);
+
+ v.insert(0, 77);
+ v.insert(2, "soba");
+ v.insert(4, false);
+ ensure_equals("inserted array size", v.size(), 6);
+ ensureTypeAndValue("post insert 0", v[0], 77);
+ ensureTypeAndValue("post insert 1", v[1], 88);
+ ensureTypeAndValue("post insert 2", v[2], "soba");
+ ensureTypeAndValue("post insert 3", v[3], "noodle");
+ ensureTypeAndValue("post insert 4", v[4], false);
+ ensureTypeAndValue("post insert 5", v[5], true);
+
+ ensureTypeAndValue("get 1", v.get(1), 88);
+ v.set(1, "hot");
+ ensureTypeAndValue("set 1", v.get(1), "hot");
+
+ v.erase(3);
+ ensure_equals("post erase array size", v.size(), 5);
+ ensureTypeAndValue("post erase 0", v[0], 77);
+ ensureTypeAndValue("post erase 1", v[1], "hot");
+ ensureTypeAndValue("post erase 2", v[2], "soba");
+ ensureTypeAndValue("post erase 3", v[3], false);
+ ensureTypeAndValue("post erase 4", v[4], true);
+
+ v.append(34);
+ ensure_equals("size after append", v.size(), 6);
+ ensureTypeAndValue("post append 5", v[5], 34);
+
+ LLSD w;
+ w = v;
+ ensure("copy array type", w.isArray());
+ ensure_equals("copy array size", w.size(), 6);
+ ensureTypeAndValue("copy array 0", w[0], 77);
+ ensureTypeAndValue("copy array 1", w[1], "hot");
+ ensureTypeAndValue("copy array 2", w[2], "soba");
+ ensureTypeAndValue("copy array 3", w[3], false);
+ ensureTypeAndValue("copy array 4", w[4], true);
+ ensureTypeAndValue("copy array 5", w[5], 34);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<12>()
+ // no sharing
+ {
+ SDCleanupCheck check;
+
+ LLSD a = 99;
+ LLSD b = a;
+ a = 34;
+ ensureTypeAndValue("top level original changed", a, 34);
+ ensureTypeAndValue("top level copy unaltered", b, 99);
+ b = a;
+ b = 66;
+ ensureTypeAndValue("top level original unaltered", a, 34);
+ ensureTypeAndValue("top level copy changed", b, 66);
+
+ a[0] = "uno";
+ a[1] = 99;
+ a[2] = 1.414;
+ b = a;
+ a[1] = 34;
+ ensureTypeAndValue("array member original changed", a[1], 34);
+ ensureTypeAndValue("array member copy unaltered", b[1], 99);
+ b = a;
+ b[1] = 66;
+ ensureTypeAndValue("array member original unaltered", a[1], 34);
+ ensureTypeAndValue("array member copy changed", b[1], 66);
+
+ a["alpha"] = "uno";
+ a["beta"] = 99;
+ a["gamma"] = 1.414;
+ b = a;
+ a["beta"] = 34;
+ ensureTypeAndValue("map member original changed", a["beta"], 34);
+ ensureTypeAndValue("map member copy unaltered", b["beta"], 99);
+ b = a;
+ b["beta"] = 66;
+ ensureTypeAndValue("map member original unaltered", a["beta"], 34);
+ ensureTypeAndValue("map member copy changed", b["beta"], 66);
+ }
+
+ template<> template<>
+ void SDTestObject::test<13>()
+ // sharing implementation
+ {
+ SDCleanupCheck check;
+
+ {
+ SDAllocationCheck check("copy construct undefinded", 0);
+ LLSD v;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign undefined", 0);
+ LLSD v;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer value", 1);
+ LLSD v = 45;
+ v = 33;
+ v = 0;
+ }
+
+ {
+ SDAllocationCheck check("copy construct integer", 1);
+ LLSD v = 45;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer", 1);
+ LLSD v = 45;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("avoids extra clone", 2);
+ LLSD v = 45;
+ LLSD w = v;
+ w = "nice day";
+ }
+ }
+
+ /* TO DO:
+ conversion of undefined to UUID, Date, URI and Binary
+ conversion of undefined to map and array
+ test map operations
+ test array operations
+ test array extension
+
+ test copying and assign maps and arrays (clone)
+ test iteration over map
+ test iteration over array
+ test iteration over scalar
+
+ test empty map and empty array are indeed shared
+ test serializations
+ */
+}
+
diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp
new file mode 100644
index 0000000000..96aad3da58
--- /dev/null
+++ b/indra/test/lltut.cpp
@@ -0,0 +1,149 @@
+/**
+ * @file lltut.cpp
+ * @author Mark Lentczner
+ * @date 5/16/06
+ * @brief MacTester
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "lltut.h"
+#include "llsd.h"
+
+namespace tut
+{
+ template<>
+ void ensure_equals(const char* msg, const LLDate& actual,
+ const LLDate& expected)
+ {
+ ensure_equals(msg,
+ actual.secondsSinceEpoch(), expected.secondsSinceEpoch());
+ }
+
+ template<>
+ void ensure_equals(const char* msg, const LLURI& actual,
+ const LLURI& expected)
+ {
+ ensure_equals(msg,
+ actual.asString(), expected.asString());
+ }
+
+ template<>
+ void ensure_equals(const char* msg,
+ const std::vector<U8>& actual, const std::vector<U8>& expected)
+ {
+ std::string s(msg);
+
+ ensure_equals(s + " size", actual.size(), expected.size());
+
+ std::vector<U8>::const_iterator i, j;
+ int k;
+ for (i = actual.begin(), j = expected.begin(), k = 0;
+ i != actual.end();
+ ++i, ++j, ++k)
+ {
+ ensure_equals(s + " field", *i, *j);
+ }
+ }
+
+ template<>
+ void ensure_equals(const char* m, const LLSD& actual,
+ const LLSD& expected)
+ {
+ const std::string& msg = m;
+
+ ensure_equals(msg + " type", actual.type(), expected.type());
+ switch (actual.type())
+ {
+ case LLSD::TypeUndefined:
+ return;
+
+ case LLSD::TypeBoolean:
+ ensure_equals(msg + " boolean", actual.asBoolean(), expected.asBoolean());
+ return;
+
+ case LLSD::TypeInteger:
+ ensure_equals(msg + " integer", actual.asInteger(), expected.asInteger());
+ return;
+
+ case LLSD::TypeReal:
+ ensure_equals(msg + " real", actual.asReal(), expected.asReal());
+ return;
+
+ case LLSD::TypeString:
+ ensure_equals(msg + " string", actual.asString(), expected.asString());
+ return;
+
+ case LLSD::TypeUUID:
+ ensure_equals(msg + " uuid", actual.asUUID(), expected.asUUID());
+ return;
+
+ case LLSD::TypeDate:
+ ensure_equals(msg + " date", actual.asDate(), expected.asDate());
+ return;
+
+ case LLSD::TypeURI:
+ ensure_equals(msg + " uri", actual.asURI(), expected.asURI());
+ return;
+
+ case LLSD::TypeBinary:
+ ensure_equals(msg + " binary", actual.asBinary(), expected.asBinary());
+ return;
+
+ case LLSD::TypeMap:
+ {
+ ensure_equals(msg + " map size", actual.size(), expected.size());
+
+ LLSD::map_const_iterator actual_iter = actual.beginMap();
+ LLSD::map_const_iterator expected_iter = expected.beginMap();
+
+ while(actual_iter != actual.endMap())
+ {
+ ensure_equals(msg + " map keys",
+ actual_iter->first, expected_iter->first);
+ ensure_equals(msg + "[" + actual_iter->first + "]",
+ actual_iter->second, expected_iter->second);
+ ++actual_iter;
+ ++expected_iter;
+ }
+ return;
+ }
+ case LLSD::TypeArray:
+ {
+ ensure_equals(msg + " array size", actual.size(), expected.size());
+
+ for(int i = 0; i < actual.size(); ++i)
+ {
+ ensure_equals(msg + llformat("[%d]", i),
+ actual[i], expected[i]);
+ }
+ return;
+ }
+ }
+ }
+
+ void ensure_starts_with(const std::string& msg,
+ const std::string& actual, const std::string& expectedStart)
+ {
+ if( actual.find(expectedStart, 0) != 0 )
+ {
+ std::stringstream ss;
+ ss << msg << ": " << "expected to find " << expectedStart
+ << " at start of actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+
+ void ensure_contains(const std::string& msg,
+ const std::string& actual, const std::string& expectedSubString)
+ {
+ if( actual.find(expectedSubString, 0) == std::string::npos )
+ {
+ std::stringstream ss;
+ ss << msg << ": " << "expected to find " << expectedSubString
+ << " in actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+}
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
new file mode 100644
index 0000000000..c750a99b8d
--- /dev/null
+++ b/indra/test/lltut.h
@@ -0,0 +1,76 @@
+/**
+ * @file lltut.h
+ * @author Phoenix
+ * @date 2005-09-26
+ * @brief helper tut methods
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * THOROUGH_DESCRIPTION
+ *
+ */
+
+#ifndef LL_LLTUT_H
+#define LL_LLTUT_H
+
+#include <tut/tut.h>
+
+#include "lldate.h"
+#include "lluri.h"
+
+class LLSD;
+
+namespace tut
+{
+ template <class T,class Q>
+ void ensure_not_equals(const char* msg,const Q& actual,const T& expected)
+ {
+ if( expected == actual )
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected;
+ throw tut::failure(ss.str().c_str());
+ }
+ }
+
+ template <class T,class Q>
+ void ensure_not_equals(const Q& actual,const T& expected)
+ {
+ ensure_not_equals(NULL, actual, expected);
+ }
+
+
+ template <class T,class Q>
+ void ensure_equals(const std::string& msg,
+ const Q& actual,const T& expected)
+ { ensure_equals(msg.c_str(), actual, expected); }
+
+ template<>
+ void ensure_equals(const char* msg,
+ const LLDate& actual, const LLDate& expected);
+
+ template<>
+ void ensure_equals(const char* msg,
+ const LLURI& actual, const LLURI& expected);
+
+ template<>
+ void ensure_equals(const char* msg,
+ const std::vector<U8>& actual, const std::vector<U8>& expected);
+
+ template<>
+ void ensure_equals(const char* msg,
+ const LLSD& actual, const LLSD& expected);
+
+ void ensure_starts_with(const std::string& msg,
+ const std::string& actual, const std::string& expectedStart);
+
+ void ensure_contains(const std::string& msg,
+ const std::string& actual, const std::string& expectedSubString);
+}
+
+
+#endif // LL_LLTUT_H
diff --git a/indra/test/lluserrelations_tut.cpp b/indra/test/lluserrelations_tut.cpp
new file mode 100644
index 0000000000..93fc29e6ee
--- /dev/null
+++ b/indra/test/lluserrelations_tut.cpp
@@ -0,0 +1,138 @@
+/**
+ * @file lluserrelations_tut.cpp
+ * @author Phoenix
+ * @date 2006-10-12
+ * @brief Unit tests for the LLRelationship class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <tut/tut.h>
+
+#include "linden_common.h"
+#include "lluserrelations.h"
+
+namespace tut
+{
+ struct user_relationship
+ {
+ LLRelationship mRelationship;
+ };
+ typedef test_group<user_relationship> user_relationship_t;
+ typedef user_relationship_t::object user_relationship_object_t;
+ tut::user_relationship_t tut_user_relationship("relationships");
+
+ template<> template<>
+ void user_relationship_object_t::test<1>()
+ {
+ // Test the default construction
+ ensure(
+ "No granted rights to",
+ !mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "No granted rights from",
+ !mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure("No online status",!mRelationship.isOnline());
+ }
+
+ template<> template<>
+ void user_relationship_object_t::test<2>()
+ {
+ // Test some granting
+ mRelationship.grantRights(
+ LLRelationship::GRANT_ONLINE_STATUS,
+ LLRelationship::GRANT_MODIFY_OBJECTS);
+ ensure(
+ "Granted rights to has online",
+ mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights from does not have online",
+ !mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights to does not have modify",
+ !mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_MODIFY_OBJECTS));
+ ensure(
+ "Granted rights from has modify",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_MODIFY_OBJECTS));
+ }
+
+ template<> template<>
+ void user_relationship_object_t::test<3>()
+ {
+ // Test revoking
+ mRelationship.grantRights(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MAP_LOCATION,
+ LLRelationship::GRANT_ONLINE_STATUS);
+ ensure(
+ "Granted rights to has online and map",
+ mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MAP_LOCATION));
+ ensure(
+ "Granted rights from has online",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+
+ mRelationship.revokeRights(
+ LLRelationship::GRANT_MAP_LOCATION,
+ LLRelationship::GRANT_NONE);
+ ensure(
+ "Granted rights revoked map",
+ !mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MAP_LOCATION));
+ ensure(
+ "Granted rights revoked still has online",
+ mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS));
+
+ mRelationship.grantRights(
+ LLRelationship::GRANT_NONE,
+ LLRelationship::GRANT_MODIFY_OBJECTS);
+ ensure(
+ "Granted rights from still has online",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights from has full grant",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MODIFY_OBJECTS));
+ mRelationship.revokeRights(
+ LLRelationship::GRANT_NONE,
+ LLRelationship::GRANT_MODIFY_OBJECTS);
+ ensure(
+ "Granted rights from still has online",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights from no longer modify",
+ !mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_MODIFY_OBJECTS));
+ }
+
+ template<> template<>
+ void user_relationship_object_t::test<4>()
+ {
+ ensure("No online status", !mRelationship.isOnline());
+ mRelationship.online(true);
+ ensure("Online status", mRelationship.isOnline());
+ mRelationship.online(false);
+ ensure("No online status", !mRelationship.isOnline());
+ }
+
+/*
+ template<> template<>
+ void user_relationship_object_t::test<>()
+ {
+ }
+*/
+}
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
new file mode 100644
index 0000000000..f05af10110
--- /dev/null
+++ b/indra/test/test.cpp
@@ -0,0 +1,248 @@
+/**
+ * @file test.cpp
+ * @author Phoenix
+ * @date 2005-09-26
+ * @brief Entry point for the test app.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * You can add tests by creating a new cpp file in this directory, and
+ * rebuilding. There are at most 50 tests per testgroup without a
+ * little bit of template parameter and makefile tweaking.
+ *
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+
+#include <apr-1/apr_pools.h>
+#include <apr-1/apr_getopt.h>
+
+// the CTYPE_WORKAROUND is needed for linux dev stations that don't
+// have the broken libc6 packages needed by our out-of-date static
+// libs (such as libcrypto and libcurl). -- Leviathan 20060113
+#ifdef CTYPE_WORKAROUND
+# include "ctype_workaround.h"
+#endif
+
+
+namespace tut
+{
+ test_runner_singleton runner;
+}
+
+class LLTestCallback : public tut::callback
+{
+public:
+ LLTestCallback(bool verbose_mode) :
+ mVerboseMode(verbose_mode),
+ mTotalTests(0),
+ mPassedTests(0),
+ mFailedTests(0)
+ {
+ }
+
+ void run_started()
+ {
+ //std::cout << "run_started" << std::endl;
+ }
+
+ void test_completed(const tut::test_result& tr)
+ {
+ ++mTotalTests;
+ std::ostringstream out;
+ out << "[" << tr.group << ", " << tr.test << "] ";
+ switch(tr.result)
+ {
+ case tut::test_result::ok:
+ ++mPassedTests;
+ out << "ok";
+ break;
+ case tut::test_result::fail:
+ ++mFailedTests;
+ out << "fail '" << tr.message << "'";
+ break;
+ case tut::test_result::ex:
+ ++mFailedTests;
+ out << "exception";
+ break;
+ case tut::test_result::warn:
+ ++mFailedTests;
+ out << "test destructor throw";
+ break;
+ case tut::test_result::term:
+ ++mFailedTests;
+ out << "abnormal termination";
+ break;
+ default:
+ ++mFailedTests;
+ out << "unknown";
+ }
+ if(mVerboseMode || (tr.result != tut::test_result::ok))
+ {
+ std::cout << out.str() << std::endl;
+ }
+ }
+
+ void run_completed()
+ {
+ std::cout << std::endl;
+ std::cout << "Total Tests: " << mTotalTests << std::endl;
+ std::cout << "Passed Tests : " << mPassedTests << std::endl;
+ if(mFailedTests > 0)
+ {
+ std::cout << "*********************************" << std::endl;
+ std::cout << "Failed Tests: " << mFailedTests << std::endl;
+ std::cout << "Please report or fix the problem." << std::endl;
+ std::cout << "*********************************" << std::endl;
+ exit(1);
+ }
+ }
+
+protected:
+ bool mVerboseMode;
+ S32 mTotalTests;
+ S32 mPassedTests;
+ S32 mFailedTests;
+};
+
+static const apr_getopt_option_t TEST_CL_OPTIONS[] =
+{
+ {"help", 'h', 0, "Print the help message."},
+ {"list", 'l', 0, "List available test groups."},
+ {"verbose", 'v', 0, "Verbose output."},
+ {"group", 'g', 1, "Run test group specified by option argument."},
+ {"wait", 'w', 0, "Wait for input before exit."},
+ {0, 0, 0, 0}
+};
+
+void stream_usage(std::ostream& s, const char* app)
+{
+ s << "Usage: " << app << " [OPTIONS]" << std::endl
+ << std::endl;
+
+ s << "This application runs the unit tests." << std::endl << std::endl;
+
+ s << "Options: " << std::endl;
+ const apr_getopt_option_t* option = &TEST_CL_OPTIONS[0];
+ while(option->name)
+ {
+ s << " ";
+ s << " -" << (char)option->optch << ", --" << option->name
+ << std::endl;
+ s << "\t" << option->description << std::endl << std::endl;
+ ++option;
+ }
+
+ s << "Examples:" << std::endl;
+ s << " " << app << " --verbose" << std::endl;
+ s << "\tRun all the tests and report all results." << std::endl;
+ s << " " << app << " --list" << std::endl;
+ s << "\tList all available test groups." << std::endl;
+ s << " " << app << " --group=uuid" << std::endl;
+ s << "\tRun the test group 'uuid'." << std::endl;
+}
+
+void stream_groups(std::ostream& s, const char* app)
+{
+ s << "Registered test groups:" << std::endl;
+ tut::groupnames gl = tut::runner.get().list_groups();
+ tut::groupnames::const_iterator it = gl.begin();
+ tut::groupnames::const_iterator end = gl.end();
+ for(; it != end; ++it)
+ {
+ s << " " << *(it) << std::endl;
+ }
+}
+
+int main(int argc, char **argv)
+{
+#ifdef CTYPE_WORKAROUND
+ ctype_workaround();
+#endif
+
+ apr_initialize();
+ apr_pool_t* pool = NULL;
+ if(APR_SUCCESS != apr_pool_create(&pool, NULL))
+ {
+ std::cerr << "Unable to initialize pool" << std::endl;
+ return 1;
+ }
+ apr_getopt_t* os = NULL;
+ if(APR_SUCCESS != apr_getopt_init(&os, pool, argc, argv))
+ {
+ std::cerr << "Unable to pool" << std::endl;
+ return 1;
+ }
+
+ // values used for controlling application
+ bool verbose_mode = false;
+ bool wait_at_exit = false;
+ std::string test_group;
+
+ // values use for options parsing
+ apr_status_t apr_err;
+ const char* opt_arg = NULL;
+ int opt_id = 0;
+ while(true)
+ {
+ apr_err = apr_getopt_long(os, TEST_CL_OPTIONS, &opt_id, &opt_arg);
+ if(APR_STATUS_IS_EOF(apr_err)) break;
+ if(apr_err)
+ {
+ char buf[255];
+ std::cerr << "Error parsing options: "
+ << apr_strerror(apr_err, buf, 255) << std::endl;
+ return 1;
+ }
+ switch (opt_id)
+ {
+ case 'g':
+ test_group.assign(opt_arg);
+ break;
+ case 'h':
+ stream_usage(std::cout, argv[0]);
+ return 0;
+ break;
+ case 'l':
+ stream_groups(std::cout, argv[0]);
+ return 0;
+ case 'v':
+ verbose_mode = true;
+ break;
+ case 'w':
+ wait_at_exit = true;
+ break;
+ default:
+ stream_usage(std::cerr, argv[0]);
+ return 1;
+ break;
+ }
+ }
+
+ // run the tests
+ LLTestCallback callback(verbose_mode);
+ tut::runner.get().set_callback(&callback);
+
+ if(test_group.empty())
+ {
+ tut::runner.get().run_tests();
+ }
+ else
+ {
+ tut::runner.get().run_tests(test_group);
+ }
+
+ if (wait_at_exit)
+ {
+ std::cerr << "Waiting for input before exiting..." << std::endl;
+ std::cin.get();
+ }
+
+ apr_terminate();
+ return 0;
+}