diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2012-03-01 23:01:37 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2012-03-01 23:01:37 -0500 |
commit | 9b5fcb78e7692c82328bd4ff6d178b68b4b47636 (patch) | |
tree | 4944742c043f66127493aec562780b8c60585611 /indra/llcommon | |
parent | cfe37cbfb5bdec0aa01c86a608ecc6cdc3df8a2a (diff) |
Refactor llleap_test.cpp to streamline adding more unit tests.
Migrate logic from specific test to common reader module, notably parsing the
wakeup message containing the reply-pump name.
Make test script post to Result struct to communicate success/failure to C++
TUT test, rather than just writing to log.
Make test script insensitive to key order in serialized LLSD::Map.
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/tests/llleap_test.cpp | 118 |
1 files changed, 87 insertions, 31 deletions
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index 0264442437..0ca8c9b684 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -83,6 +83,7 @@ namespace tut llleap_data(): reader(".py", // This logic is adapted from vita.viewerclient.receiveEvent() + "import re\n" "import sys\n" "LEFTOVER = ''\n" "class ProtocolError(Exception):\n" @@ -112,7 +113,29 @@ namespace tut " parts[-1] = parts[-1][:excess]\n" " data = ''.join(parts)\n" " assert len(data) == length\n" - " return data\n"), + " return data\n" + "\n" + "def put(req):\n" + " sys.stdout.write(':'.join((str(len(req)), req)))\n" + " sys.stdout.flush()\n" + "\n" + "# deal with initial stdin message\n" + // this will throw if the initial write to stdin + // doesn't follow len:data protocol + "_initial = get()\n" + "_match = re.search(r\"'pump':'(.*?)'\", _initial)\n" + // this will throw if we couldn't find + // 'pump':'etc.' in the initial write + "_reply = _match.group(1)\n" + "\n" + "def replypump():\n" + " return _reply\n" + "\n" + "def escape(str):\n" + " return ''.join(('\\\\'+c if c in r\"\\'\" else c) for c in str)\n" + "\n" + "def quote(str):\n" + " return \"'%s'\" % escape(str)\n"), // Get the actual pathname of the NamedExtTempFile and trim off // the ".py" extension. (We could cache reader.getName() in a // separate member variable, but I happen to know getName() just @@ -203,23 +226,64 @@ namespace tut ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false)); } + // Generic self-contained listener: derive from this and override its + // call() method, then tell somebody to post on the pump named getName(). + // Control will reach your call() override. + struct ListenerBase + { + // Pass the pump name you want; will tweak for uniqueness. + ListenerBase(const std::string& name): + mPump(name, true) + { + mPump.listen(name, boost::bind(&ListenerBase::call, this, _1)); + } + + virtual bool call(const LLSD& request) + { + return false; + } + + LLEventPump& getPump() { return mPump; } + const LLEventPump& getPump() const { return mPump; } + + std::string getName() const { return mPump.getName(); } + void post(const LLSD& data) { mPump.post(data); } + + LLEventStream mPump; + }; + // Mimic a dummy little LLEventAPI that merely sends a reply back to its // requester on the "reply" pump. - struct API + struct API: public ListenerBase { - API(): - mPump("API", true) + API(): ListenerBase("API") {} + + virtual bool call(const LLSD& request) { - mPump.listen("API", boost::bind(&API::entry, this, _1)); + LLEventPumps::instance().obtain(request["reply"]).post("ack"); + return false; } + }; + + // Give LLLeap script a way to post success/failure. + struct Result: public ListenerBase + { + Result(): ListenerBase("Result") {} - bool entry(const LLSD& request) + virtual bool call(const LLSD& request) { - LLEventPumps::instance().obtain(request["reply"]).post("ack"); + mData = request; return false; } - LLEventStream mPump; + void ensure() const + { + tut::ensure(std::string("never posted to ") + getName(), mData.isDefined()); + // Post an empty string for success, non-empty string is failure message. + tut::ensure(mData, mData.asString().empty()); + } + + LLSD mData; }; template<> template<> @@ -227,35 +291,27 @@ namespace tut { set_test_name("round trip"); API api; + Result result; NamedTempFile script("py", boost::lambda::_1 << - "import re\n" "import sys\n" - "from " << reader_module << " import get\n" - // this will throw if the initial write to stdin - // doesn't follow len:data protocol - "initial = get()\n" - "match = re.search(r\"'pump':'(.*?)'\", initial)\n" - // this will throw if we couldn't find - // 'pump':'etc.' in the initial write - "reply = match.group(1)\n" - "req = '''\\\n" - "{'pump':'" << api.mPump.getName() << "','data':{'reply':'%s'}}\\\n" - "''' % reply\n" + "from " << reader_module << " import *\n" // make a request on our little API - "sys.stdout.write(':'.join((str(len(req)), req)))\n" - "sys.stdout.flush()\n" + "put(\"{'pump':'" << api.getName() << "','data':{'reply':'%s'}}\" %\n" + " replypump())\n" // wait for its response "resp = get()\n" - // it would be cleverer to be order-insensitive - // about 'data' and 'pump'; hopefully the C++ - // serializer doesn't change its rules soon - "result = 'good' if (resp == \"{'data':'ack','pump':'%s'}\" % reply)\\\n" - " else 'bad: ' + resp\n" - // write 'good' or 'bad' to the log so we can observe - "sys.stderr.write(result)\n"); - CaptureLog log(LLError::LEVEL_INFO); + // We expect "{'data':'ack','pump':'%s'}", but + // don't depend on the order of the keys. + "result = 'bad: ' + resp\n" + "if resp.startswith('{') and resp.endswith('}'):\n" + " expect = set((\"'data':'ack'\", \"'pump':'%s'\" % replypump()))\n" + " actual = set(resp[1:-1].split(','))\n" + " if actual == expect:\n" + " result = ''\n" + "put(\"{'pump':'" << result.getName() << "','data':%s}\" %\n" + " quote(result))\n"); waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName())))); - log.messageWith("good"); + result.ensure(); } } // namespace tut |