llhttpnode_tut.cpp

Go to the documentation of this file.
00001 
00033 #include "linden_common.h"
00034 #include "lltut.h"
00035 #include "llhttpnode.h"
00036 #include "llsdhttpserver.h"
00037 
00038 namespace tut
00039 {
00040         struct HTTPNodeTestData
00041         {
00042                 LLHTTPNode mRoot;
00043                 LLSD mContext;
00044         
00045                 const LLSD& context() { return mContext; }
00046                 
00047                 std::string remainderPath()
00048                 {
00049                         std::ostringstream pathOutput;
00050                         bool addSlash = false;
00051                         
00052                         LLSD& remainder = mContext["request"]["remainder"];
00053                         for (LLSD::array_const_iterator i = remainder.beginArray();
00054                                 i != remainder.endArray();
00055                                 ++i)
00056                         {
00057                                 if (addSlash) { pathOutput << '/'; }
00058                                 pathOutput << i->asString();
00059                                 addSlash = true;
00060                         }
00061                         
00062                         return pathOutput.str();
00063                 }
00064                 
00065                 void ensureRootTraversal(const std::string& path,
00066                         const LLHTTPNode* expectedNode,
00067                         const char* expectedRemainder)
00068                 {
00069                         mContext.clear();
00070                         
00071                         const LLHTTPNode* actualNode = mRoot.traverse(path, mContext);
00072                         
00073                         ensure_equals("traverse " + path + " node",
00074                                 actualNode, expectedNode);
00075                         ensure_equals("traverse " + path + " remainder",
00076                                 remainderPath(), expectedRemainder);
00077                 }
00078 
00079                 class Response : public LLHTTPNode::Response
00080                 {
00081                 public:
00082                         static LLPointer<Response> create() {return new Response();}
00083 
00084                         LLSD mResult;
00085 
00086                         void result(const LLSD& result) { mResult = result; }
00087                         void status(S32 code, const std::string& message) { }
00088 
00089                 private:
00090                         Response() {;} // Must be accessed through LLPointer.
00091                 };
00092 
00093                 typedef LLPointer<Response> ResponsePtr;
00094 
00095                 LLSD get(const std::string& path)
00096                 {
00097                         mContext.clear();
00098                         const LLHTTPNode* node = mRoot.traverse(path, mContext);
00099                         ensure(path + " found", node != NULL);
00100 
00101                         ResponsePtr response = Response::create();
00102                         node->get(LLHTTPNode::ResponsePtr(response), mContext);
00103                         return response->mResult;
00104                 }
00105                 
00106                 LLSD post(const std::string& path, const LLSD& input)
00107                 {
00108                         mContext.clear();
00109                         const LLHTTPNode* node = mRoot.traverse(path, mContext);
00110                         ensure(path + " found", node != NULL);
00111 
00112                         ResponsePtr response = Response::create();
00113                         node->post(LLHTTPNode::ResponsePtr(response), mContext, input);
00114                         return response->mResult;
00115                 }
00116                 
00117                 void ensureMemberString(const std::string& name,
00118                         const LLSD& actualMap, const std::string& member,
00119                         const std::string& expectedValue)
00120                 {
00121                         ensure_equals(name + " " + member,
00122                                 actualMap[member].asString(), expectedValue);
00123                 }
00124                 
00125                 
00126                 void ensureInArray(const LLSD& actualArray,
00127                         const std::string& expectedValue)
00128                 {
00129                         LLSD::array_const_iterator i = actualArray.beginArray();
00130                         LLSD::array_const_iterator end = actualArray.endArray();
00131                 
00132                         for (; i != end; ++i)
00133                         {
00134                                 std::string path = i->asString();
00135 
00136                                 if (path == expectedValue)
00137                                 {
00138                                         return;
00139                                 }
00140                         }
00141                         
00142                         fail("didn't find " + expectedValue);
00143                 }
00144                         
00145         };
00146         
00147         typedef test_group<HTTPNodeTestData>    HTTPNodeTestGroup;
00148         typedef HTTPNodeTestGroup::object               HTTPNodeTestObject;
00149         HTTPNodeTestGroup httpNodeTestGroup("http node");
00150 
00151         template<> template<>
00152         void HTTPNodeTestObject::test<1>()
00153         {
00154                 // traversal of the lone node
00155                 
00156                 ensureRootTraversal("", &mRoot, "");
00157                 ensureRootTraversal("/", &mRoot, "");
00158                 ensureRootTraversal("foo", NULL, "foo");
00159                 ensureRootTraversal("foo/bar", NULL, "foo/bar");
00160                 
00161                 ensure_equals("root of root", mRoot.rootNode(), &mRoot);
00162         }
00163         
00164         template<> template<>
00165         void HTTPNodeTestObject::test<2>()
00166         {
00167                 // simple traversal of a single node
00168                 
00169                 LLHTTPNode* helloNode = new LLHTTPNode;
00170                 mRoot.addNode("hello", helloNode);
00171 
00172                 ensureRootTraversal("hello", helloNode, "");
00173                 ensureRootTraversal("/hello", helloNode, "");
00174                 ensureRootTraversal("hello/", helloNode, "");
00175                 ensureRootTraversal("/hello/", helloNode, "");
00176 
00177                 ensureRootTraversal("hello/there", NULL, "there");
00178                 
00179                 ensure_equals("root of hello", helloNode->rootNode(), &mRoot);
00180         }
00181 
00182         template<> template<>
00183         void HTTPNodeTestObject::test<3>()
00184         {
00185                 // traversal of mutli-branched tree
00186                 
00187                 LLHTTPNode* greekNode = new LLHTTPNode;
00188                 LLHTTPNode* alphaNode = new LLHTTPNode;
00189                 LLHTTPNode* betaNode = new LLHTTPNode;
00190                 LLHTTPNode* gammaNode = new LLHTTPNode;
00191                 
00192                 greekNode->addNode("alpha", alphaNode);
00193                 greekNode->addNode("beta", betaNode);
00194                 greekNode->addNode("gamma", gammaNode);
00195                 mRoot.addNode("greek", greekNode);
00196                 
00197                 LLHTTPNode* hebrewNode = new LLHTTPNode;
00198                 LLHTTPNode* alephNode = new LLHTTPNode;
00199                 
00200                 hebrewNode->addNode("aleph", alephNode);
00201                 mRoot.addNode("hebrew", hebrewNode);
00202 
00203                 ensureRootTraversal("greek/alpha", alphaNode, "");
00204                 ensureRootTraversal("greek/beta", betaNode, "");
00205                 ensureRootTraversal("greek/delta", NULL, "delta");
00206                 ensureRootTraversal("greek/gamma", gammaNode, "");
00207                 ensureRootTraversal("hebrew/aleph", alephNode, "");
00208 
00209                 ensure_equals("root of greek", greekNode->rootNode(), &mRoot);
00210                 ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
00211                 ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
00212                 ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
00213                 ensure_equals("root of hebrew", hebrewNode->rootNode(), &mRoot);
00214                 ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
00215         }
00216 
00217         template<> template<>
00218         void HTTPNodeTestObject::test<4>()
00219         {
00220                 // automatic creation of parent nodes and not overriding existing nodes
00221                 
00222                 LLHTTPNode* alphaNode = new LLHTTPNode;
00223                 LLHTTPNode* betaNode = new LLHTTPNode;
00224                 LLHTTPNode* gammaNode = new LLHTTPNode;
00225                 LLHTTPNode* gamma2Node = new LLHTTPNode;
00226                 
00227                 mRoot.addNode("greek/alpha", alphaNode);
00228                 mRoot.addNode("greek/beta", betaNode);
00229                 
00230                 mRoot.addNode("greek/gamma", gammaNode);
00231                 mRoot.addNode("greek/gamma", gamma2Node);
00232                 
00233                 LLHTTPNode* alephNode = new LLHTTPNode;
00234                 
00235                 mRoot.addNode("hebrew/aleph", alephNode);
00236 
00237                 ensureRootTraversal("greek/alpha", alphaNode, "");
00238                 ensureRootTraversal("greek/beta", betaNode, "");
00239                 ensureRootTraversal("greek/delta", NULL, "delta");
00240                 ensureRootTraversal("greek/gamma", gammaNode, "");
00241                 ensureRootTraversal("hebrew/aleph", alephNode, "");
00242 
00243                 ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
00244                 ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
00245                 ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
00246                 ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
00247         }
00248         
00249         class IntegerNode : public LLHTTPNode
00250         {
00251         public:
00252                 virtual void get(ResponsePtr response, const LLSD& context) const
00253                 {
00254                         int n = context["extra"]["value"];
00255                         
00256                         LLSD info;
00257                         info["value"] = n;
00258                         info["positive"] = n > 0;
00259                         info["zero"] = n == 0;
00260                         info["negative"] = n < 0;
00261                 
00262                         response->result(info);
00263                 }
00264                 
00265                 virtual bool validate(const std::string& name, LLSD& context) const
00266                 {
00267                         int n;
00268                         std::istringstream i_stream(name);
00269                         i_stream >> n;
00270 
00271                         if (i_stream.fail()  ||  i_stream.get() != EOF)
00272                         {
00273                                 return false;
00274                         }
00275 
00276                         context["extra"]["value"] = n;
00277                         return true;
00278                 }
00279         };
00280         
00281         class SquareNode : public LLHTTPNode
00282         {
00283         public:
00284                 virtual void get(ResponsePtr response, const LLSD& context) const
00285                 {
00286                         int n = context["extra"]["value"];              
00287                         response->result(n*n);
00288                 }
00289         };
00290         
00291         template<> template<>
00292         void HTTPNodeTestObject::test<5>()
00293         {
00294                 // wildcard nodes
00295                 
00296                 LLHTTPNode* miscNode = new LLHTTPNode;
00297                 LLHTTPNode* iNode = new IntegerNode;
00298                 LLHTTPNode* sqNode = new SquareNode;
00299                 
00300                 mRoot.addNode("test/misc", miscNode);
00301                 mRoot.addNode("test/<int>", iNode);
00302                 mRoot.addNode("test/<int>/square", sqNode);
00303                 
00304                 ensureRootTraversal("test/42", iNode, "");
00305                 ensure_equals("stored integer",
00306                         context()["extra"]["value"].asInteger(), 42);
00307                 
00308                 ensureRootTraversal("test/bob", NULL, "bob");
00309                 ensure("nothing stored",
00310                         context()["extra"]["value"].isUndefined());
00311                         
00312                 ensureRootTraversal("test/3/square", sqNode, "");
00313                 ResponsePtr response = Response::create();
00314                 sqNode->get(LLHTTPNode::ResponsePtr(response), context());
00315                 ensure_equals("square result", response->mResult.asInteger(), 9);
00316         }
00317         
00318         class AlphaNode : public LLHTTPNode
00319         {
00320         public:
00321                 virtual bool handles(const LLSD& remainder, LLSD& context) const
00322                 {
00323                         LLSD::array_const_iterator i = remainder.beginArray();
00324                         LLSD::array_const_iterator end = remainder.endArray();
00325                         
00326                         for (; i != end; ++i)
00327                         {
00328                                 std::string s = i->asString();
00329                                 if (s.empty() || s[0] != 'a')
00330                                 {
00331                                         return false;
00332                                 }
00333                         }
00334                         
00335                         return true;
00336                 }
00337         };
00338         
00339         template<> template<>
00340         void HTTPNodeTestObject::test<6>()
00341         {
00342                 // nodes that handle remainders
00343                 
00344                 LLHTTPNode* miscNode = new LLHTTPNode;
00345                 LLHTTPNode* aNode = new AlphaNode;
00346                 LLHTTPNode* zNode = new LLHTTPNode;
00347                 
00348                 mRoot.addNode("test/misc", miscNode);
00349                 mRoot.addNode("test/alpha", aNode);
00350                 mRoot.addNode("test/alpha/zebra", zNode);
00351                 
00352                 ensureRootTraversal("test/alpha", aNode, "");
00353                 ensureRootTraversal("test/alpha/abe", aNode, "abe");
00354                 ensureRootTraversal("test/alpha/abe/amy", aNode, "abe/amy");
00355                 ensureRootTraversal("test/alpha/abe/bea", NULL, "abe/bea");
00356                 ensureRootTraversal("test/alpha/bob", NULL, "bob");
00357                 ensureRootTraversal("test/alpha/zebra", zNode, "");
00358         }
00359         
00360         template<> template<>
00361         void HTTPNodeTestObject::test<7>()
00362         {
00363                 // test auto registration
00364                 
00365                 LLHTTPStandardServices::useServices();
00366                 LLHTTPRegistrar::buildAllServices(mRoot);
00367                 
00368                 {
00369                         LLSD result = get("web/hello");
00370                         ensure_equals("hello result", result.asString(), "hello");
00371                 }
00372                 {
00373                         LLSD stuff = 3.14159;
00374                         LLSD result = post("web/echo", stuff);
00375                         ensure_equals("echo result", result, stuff);
00376                 }
00377         }
00378 
00379         template<> template<>
00380         void HTTPNodeTestObject::test<8>()
00381         {
00382                 // test introspection
00383                 
00384                 LLHTTPRegistrar::buildAllServices(mRoot);
00385                 
00386                 mRoot.addNode("test/misc", new LLHTTPNode);
00387                 mRoot.addNode("test/<int>", new IntegerNode);
00388                 mRoot.addNode("test/<int>/square", new SquareNode);
00389 
00390                 const LLSD result = get("web/server/api");
00391                 
00392                 ensure("result is array", result.isArray());
00393                 ensure("result size", result.size() >= 2);
00394                 
00395                 ensureInArray(result, "web/echo");
00396                 ensureInArray(result, "web/hello");
00397                 ensureInArray(result, "test/misc");
00398                 ensureInArray(result, "test/<int>");
00399                 ensureInArray(result, "test/<int>/square");
00400         }
00401         
00402         template<> template<>
00403         void HTTPNodeTestObject::test<9>()
00404         {
00405                 // test introspection details
00406 
00407                 LLHTTPRegistrar::buildAllServices(mRoot);
00408 
00409                 const LLSD helloDetails = get("web/server/api/web/hello");
00410                 
00411                 ensure_contains("hello description",
00412                         helloDetails["description"].asString(), "hello");
00413                 ensure_equals("method name", helloDetails["api"][0].asString(), std::string("GET"));
00414                 ensureMemberString("hello", helloDetails, "output", "\"hello\"");
00415                 ensure_contains("hello __file__",
00416                         helloDetails["__file__"].asString(), "llsdhttpserver.cpp");
00417                 ensure("hello line", helloDetails["__line__"].isInteger());
00418 
00419 
00420                 const LLSD echoDetails = get("web/server/api/web/echo");
00421                 
00422                 ensure_contains("echo description",
00423                         echoDetails["description"].asString(), "echo");
00424                 ensure_equals("method name", echoDetails["api"][0].asString(), std::string("POST"));
00425                 ensureMemberString("echo", echoDetails, "input", "<any>");
00426                 ensureMemberString("echo", echoDetails, "output", "<the input>");
00427                 ensure_contains("echo __file__",
00428                         echoDetails["__file__"].asString(), "llsdhttpserver.cpp");
00429                 ensure("echo", echoDetails["__line__"].isInteger());
00430         }
00431 }

Generated on Fri May 16 08:34:29 2008 for SecondLife by  doxygen 1.5.5