summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2012-03-01 23:01:37 -0500
committerNat Goodspeed <nat@lindenlab.com>2012-03-01 23:01:37 -0500
commit9b5fcb78e7692c82328bd4ff6d178b68b4b47636 (patch)
tree4944742c043f66127493aec562780b8c60585611 /indra/llcommon
parentcfe37cbfb5bdec0aa01c86a608ecc6cdc3df8a2a (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.cpp118
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