Skip to content
Snippets Groups Projects
Commit 9b5fcb78 authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

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.
parent cfe37cbf
No related branches found
No related tags found
No related merge requests found
...@@ -83,6 +83,7 @@ namespace tut ...@@ -83,6 +83,7 @@ namespace tut
llleap_data(): llleap_data():
reader(".py", reader(".py",
// This logic is adapted from vita.viewerclient.receiveEvent() // This logic is adapted from vita.viewerclient.receiveEvent()
"import re\n"
"import sys\n" "import sys\n"
"LEFTOVER = ''\n" "LEFTOVER = ''\n"
"class ProtocolError(Exception):\n" "class ProtocolError(Exception):\n"
...@@ -112,7 +113,29 @@ namespace tut ...@@ -112,7 +113,29 @@ namespace tut
" parts[-1] = parts[-1][:excess]\n" " parts[-1] = parts[-1][:excess]\n"
" data = ''.join(parts)\n" " data = ''.join(parts)\n"
" assert len(data) == length\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 // Get the actual pathname of the NamedExtTempFile and trim off
// the ".py" extension. (We could cache reader.getName() in a // the ".py" extension. (We could cache reader.getName() in a
// separate member variable, but I happen to know getName() just // separate member variable, but I happen to know getName() just
...@@ -203,23 +226,64 @@ namespace tut ...@@ -203,23 +226,64 @@ namespace tut
ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false)); 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 // Mimic a dummy little LLEventAPI that merely sends a reply back to its
// requester on the "reply" pump. // requester on the "reply" pump.
struct API struct API: public ListenerBase
{ {
API(): API(): ListenerBase("API") {}
mPump("API", true)
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; 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<> template<> template<>
...@@ -227,35 +291,27 @@ namespace tut ...@@ -227,35 +291,27 @@ namespace tut
{ {
set_test_name("round trip"); set_test_name("round trip");
API api; API api;
Result result;
NamedTempFile script("py", NamedTempFile script("py",
boost::lambda::_1 << boost::lambda::_1 <<
"import re\n"
"import sys\n" "import sys\n"
"from " << reader_module << " import get\n" "from " << reader_module << " import *\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"
// make a request on our little API // make a request on our little API
"sys.stdout.write(':'.join((str(len(req)), req)))\n" "put(\"{'pump':'" << api.getName() << "','data':{'reply':'%s'}}\" %\n"
"sys.stdout.flush()\n" " replypump())\n"
// wait for its response // wait for its response
"resp = get()\n" "resp = get()\n"
// it would be cleverer to be order-insensitive // We expect "{'data':'ack','pump':'%s'}", but
// about 'data' and 'pump'; hopefully the C++ // don't depend on the order of the keys.
// serializer doesn't change its rules soon "result = 'bad: ' + resp\n"
"result = 'good' if (resp == \"{'data':'ack','pump':'%s'}\" % reply)\\\n" "if resp.startswith('{') and resp.endswith('}'):\n"
" else 'bad: ' + resp\n" " expect = set((\"'data':'ack'\", \"'pump':'%s'\" % replypump()))\n"
// write 'good' or 'bad' to the log so we can observe " actual = set(resp[1:-1].split(','))\n"
"sys.stderr.write(result)\n"); " if actual == expect:\n"
CaptureLog log(LLError::LEVEL_INFO); " result = ''\n"
"put(\"{'pump':'" << result.getName() << "','data':%s}\" %\n"
" quote(result))\n");
waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName())))); waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
log.messageWith("good"); result.ensure();
} }
} // namespace tut } // namespace tut
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment