summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/lleventfilter.cpp34
-rw-r--r--indra/llcommon/lleventfilter.h13
-rw-r--r--indra/llcommon/lleventtimer.h69
-rw-r--r--indra/llcommon/llrand.cpp104
-rw-r--r--indra/llcommon/llrun.h11
-rw-r--r--indra/llcommon/tests/llleap_test.cpp251
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp71
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp40
8 files changed, 243 insertions, 350 deletions
diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp
index 4cded7f88e..14c9c51830 100644
--- a/indra/llcommon/lleventfilter.cpp
+++ b/indra/llcommon/lleventfilter.cpp
@@ -33,20 +33,19 @@
// STL headers
// std headers
// external library headers
-#include <boost/bind.hpp>
// other Linden headers
+#include "lldate.h"
#include "llerror.h" // LL_ERRS
+#include "lleventtimer.h"
#include "llsdutil.h" // llsd_matches()
#include "stringize.h"
-#include "lleventtimer.h"
-#include "lldate.h"
/*****************************************************************************
* LLEventFilter
*****************************************************************************/
LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
LLEventStream(name, tweak),
- mSource(source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1)))
+ mSource(source.listen(getName(), [this](const LLSD& event){ return post(event); }))
{
}
@@ -93,7 +92,7 @@ void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
if (! mMainloop.connected())
{
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
- mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1));
+ mMainloop = mainloop.listen(getName(), [this](const LLSD& event){ return tick(event); });
}
}
@@ -185,27 +184,6 @@ bool LLEventTimeout::countdownElapsed() const
return mTimer.hasExpired();
}
-LLEventTimer* LLEventTimeout::post_every(F32 period, const std::string& pump, const LLSD& data)
-{
- return LLEventTimer::run_every(
- period,
- [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
-}
-
-LLEventTimer* LLEventTimeout::post_at(const LLDate& time, const std::string& pump, const LLSD& data)
-{
- return LLEventTimer::run_at(
- time,
- [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
-}
-
-LLEventTimer* LLEventTimeout::post_after(F32 interval, const std::string& pump, const LLSD& data)
-{
- return LLEventTimer::run_after(
- interval,
- [pump, data](){ LLEventPumps::instance().obtain(pump).post(data); });
-}
-
/*****************************************************************************
* LLEventBatch
*****************************************************************************/
@@ -311,7 +289,7 @@ bool LLEventThrottleBase::post(const LLSD& event)
// timeRemaining tells us how much longer it will be until
// mInterval seconds since the last flush() call. At that time,
// flush() deferred events.
- alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));
+ alarmActionAfter(timeRemaining, [this](){ flush(); });
}
}
return false;
@@ -349,7 +327,7 @@ void LLEventThrottleBase::setInterval(F32 interval)
// and if mAlarm is running, reset that too
if (alarmRunning())
{
- alarmActionAfter(timeRemaining, boost::bind(&LLEventThrottleBase::flush, this));
+ alarmActionAfter(timeRemaining, [this](){ flush(); });
}
}
}
diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h
index 7613850fb2..437a4826d4 100644
--- a/indra/llcommon/lleventfilter.h
+++ b/indra/llcommon/lleventfilter.h
@@ -214,19 +214,6 @@ public:
LLEventTimeout();
LLEventTimeout(LLEventPump& source);
- /// using LLEventTimeout as namespace for free functions
- /// Post event to specified LLEventPump every period seconds. Delete
- /// returned LLEventTimer* to cancel.
- static LLEventTimer* post_every(F32 period, const std::string& pump, const LLSD& data);
- /// Post event to specified LLEventPump at specified future time. Call
- /// LLEventTimer::getInstance(returned pointer) to check whether it's still
- /// pending; if so, delete the pointer to cancel.
- static LLEventTimer* post_at(const LLDate& time, const std::string& pump, const LLSD& data);
- /// Post event to specified LLEventPump after specified interval. Call
- /// LLEventTimer::getInstance(returned pointer) to check whether it's still
- /// pending; if so, delete the pointer to cancel.
- static LLEventTimer* post_after(F32 interval, const std::string& pump, const LLSD& data);
-
protected:
virtual void setCountdown(F32 seconds);
virtual bool countdownElapsed() const;
diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h
index dbbfe0c6e6..b5d40a0622 100644
--- a/indra/llcommon/lleventtimer.h
+++ b/indra/llcommon/lleventtimer.h
@@ -24,7 +24,7 @@
* $/LicenseInfo$
*/
-#ifndef LL_EVENTTIMER_H
+#ifndef LL_EVENTTIMER_H
#define LL_EVENTTIMER_H
#include "stdtypes.h"
@@ -47,76 +47,9 @@ public:
static void updateClass();
- /// Schedule recurring calls to generic callable every period seconds.
- /// Returns a pointer; if you delete it, cancels the recurring calls.
- template <typename CALLABLE>
- static LLEventTimer* run_every(F32 period, const CALLABLE& callable);
-
- /// Schedule a future call to generic callable. Returns a pointer.
- /// CAUTION: The object referenced by that pointer WILL BE DELETED once
- /// the callback has been called! LLEventTimer::getInstance(pointer) (NOT
- /// pointer->getInstance(pointer)!) can be used to test whether the
- /// pointer is still valid. If it is, deleting it will cancel the
- /// callback.
- template <typename CALLABLE>
- static LLEventTimer* run_at(const LLDate& time, const CALLABLE& callable);
-
- /// Like run_at(), but after a time delta rather than at a timestamp.
- /// Same CAUTION.
- template <typename CALLABLE>
- static LLEventTimer* run_after(F32 interval, const CALLABLE& callable);
-
protected:
LLTimer mEventTimer;
F32 mPeriod;
-
-private:
- template <typename CALLABLE>
- class Generic;
};
-template <typename CALLABLE>
-class LLEventTimer::Generic: public LLEventTimer
-{
-public:
- // making TIME generic allows engaging either LLEventTimer constructor
- template <typename TIME>
- Generic(const TIME& time, bool once, const CALLABLE& callable):
- LLEventTimer(time),
- mOnce(once),
- mCallable(callable)
- {}
- BOOL tick() override
- {
- mCallable();
- // true tells updateClass() to delete this instance
- return mOnce;
- }
-
-private:
- bool mOnce;
- CALLABLE mCallable;
-};
-
-template <typename CALLABLE>
-LLEventTimer* LLEventTimer::run_every(F32 period, const CALLABLE& callable)
-{
- // return false to schedule recurring calls
- return new Generic<CALLABLE>(period, false, callable);
-}
-
-template <typename CALLABLE>
-LLEventTimer* LLEventTimer::run_at(const LLDate& time, const CALLABLE& callable)
-{
- // return true for one-shot callback
- return new Generic<CALLABLE>(time, true, callable);
-}
-
-template <typename CALLABLE>
-LLEventTimer* LLEventTimer::run_after(F32 interval, const CALLABLE& callable)
-{
- // one-shot callback after specified interval
- return new Generic<CALLABLE>(interval, true, callable);
-}
-
#endif //LL_EVENTTIMER_H
diff --git a/indra/llcommon/llrand.cpp b/indra/llcommon/llrand.cpp
index cb28a8f5c3..33afc50cf7 100644
--- a/indra/llcommon/llrand.cpp
+++ b/indra/llcommon/llrand.cpp
@@ -58,46 +58,14 @@
* to restore uniform distribution.
*/
-// *NOTE: The system rand implementation is probably not correct.
-#define LL_USE_SYSTEM_RAND 0
+static LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed());
-#if LL_USE_SYSTEM_RAND
-#include <cstdlib>
-#endif
+// no default implementation, only specific F64 and F32 specializations
+template <typename REAL>
+inline REAL ll_internal_random();
-#if LL_USE_SYSTEM_RAND
-class LLSeedRand
-{
-public:
- LLSeedRand()
- {
-#if LL_WINDOWS
- srand(LLUUID::getRandomSeed());
-#else
- srand48(LLUUID::getRandomSeed());
-#endif
- }
-};
-static LLSeedRand sRandomSeeder;
-inline F64 ll_internal_random_double()
-{
-#if LL_WINDOWS
- return (F64)rand() / (F64)RAND_MAX;
-#else
- return drand48();
-#endif
-}
-inline F32 ll_internal_random_float()
-{
-#if LL_WINDOWS
- return (F32)rand() / (F32)RAND_MAX;
-#else
- return (F32)drand48();
-#endif
-}
-#else
-static LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed());
-inline F64 ll_internal_random_double()
+template <>
+inline F64 ll_internal_random<F64>()
{
// *HACK: Through experimentation, we have found that dual core
// CPUs (or at least multi-threaded processes) seem to
@@ -108,15 +76,35 @@ inline F64 ll_internal_random_double()
return rv;
}
+template <>
+inline F32 ll_internal_random<F32>()
+{
+ return F32(ll_internal_random<F64>());
+}
+
+/*------------------------------ F64 aliases -------------------------------*/
+inline F64 ll_internal_random_double()
+{
+ return ll_internal_random<F64>();
+}
+
+F64 ll_drand()
+{
+ return ll_internal_random_double();
+}
+
+/*------------------------------ F32 aliases -------------------------------*/
inline F32 ll_internal_random_float()
{
- // The clamping rules are described above.
- F32 rv = (F32)gRandomGenerator();
- if(!((rv >= 0.0f) && (rv < 1.0f))) return fmod(rv, 1.f);
- return rv;
+ return ll_internal_random<F32>();
+}
+
+F32 ll_frand()
+{
+ return ll_internal_random_float();
}
-#endif
+/*-------------------------- clamped random range --------------------------*/
S32 ll_rand()
{
return ll_rand(RAND_MAX);
@@ -130,42 +118,28 @@ S32 ll_rand(S32 val)
return rv;
}
-F32 ll_frand()
-{
- return ll_internal_random_float();
-}
-
-F32 ll_frand(F32 val)
+template <typename REAL>
+REAL ll_grand(REAL val)
{
// The clamping rules are described above.
- F32 rv = ll_internal_random_float() * val;
+ REAL rv = ll_internal_random<REAL>() * val;
if(val > 0)
{
- if(rv >= val) return 0.0f;
+ if(rv >= val) return REAL();
}
else
{
- if(rv <= val) return 0.0f;
+ if(rv <= val) return REAL();
}
return rv;
}
-F64 ll_drand()
+F32 ll_frand(F32 val)
{
- return ll_internal_random_double();
+ return ll_grand<F32>(val);
}
F64 ll_drand(F64 val)
{
- // The clamping rules are described above.
- F64 rv = ll_internal_random_double() * val;
- if(val > 0)
- {
- if(rv >= val) return 0.0;
- }
- else
- {
- if(rv <= val) return 0.0;
- }
- return rv;
+ return ll_grand<F64>(val);
}
diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h
index d610f86234..27e4a43a8d 100644
--- a/indra/llcommon/llrun.h
+++ b/indra/llcommon/llrun.h
@@ -34,6 +34,17 @@
class LLRunnable;
+//////////////////////////////////////////////////////////////////////////////
+// DEPRECATION WARNING
+// LLRunner is one of several mostly redundant ways to schedule future
+// callbacks on the main thread. It seems to be unused in the current viewer.
+// addRunnable() is only called by LLPumpIO::sleepChain().
+// sleepChain() is only called by LLIOSleeper and LLIOSleep.
+// LLIOSleeper is referenced only by tests.
+// LLIOSleep is only called by LLDeferredChain.
+// LLDeferredChain isn't referenced at all.
+//////////////////////////////////////////////////////////////////////////////
+
/**
* @class LLRunner
* @brief This class manages a set of LLRunnable objects.
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index 60005fc6a9..a5416d649f 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -17,8 +17,6 @@
// std headers
#include <functional>
// external library headers
-#include <boost/assign/list_of.hpp>
-#include <boost/phoenix/core/argument.hpp>
// other Linden headers
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
@@ -30,10 +28,6 @@
#include "stringize.h"
#include "StringVec.h"
-using boost::assign::list_of;
-
-StringVec sv(const StringVec& listof) { return listof; }
-
#if defined(LL_WINDOWS)
#define sleep(secs) _sleep((secs) * 1000)
@@ -104,7 +98,7 @@ namespace tut
llleap_data():
reader(".py",
// This logic is adapted from vita.viewerclient.receiveEvent()
- boost::phoenix::placeholders::arg1 <<
+ [](std::ostream& out){ out <<
"import re\n"
"import os\n"
"import sys\n"
@@ -188,7 +182,7 @@ namespace tut
"def request(pump, data):\n"
" # we expect 'data' is a dict\n"
" data['reply'] = _reply\n"
- " send(pump, data)\n"),
+ " send(pump, data)\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
@@ -213,14 +207,14 @@ namespace tut
void object::test<1>()
{
set_test_name("multiple LLLeap instances");
- NamedTempFile script("py",
- "import time\n"
- "time.sleep(1)\n");
+ NamedExtTempFile script("py",
+ "import time\n"
+ "time.sleep(1)\n");
LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName())))->getWeak());
+ StringVec{PYTHON, script.getName()})->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName())))->getWeak());
+ StringVec{PYTHON, script.getName()})->getWeak());
// In this case we're simply establishing that two LLLeap instances
// can coexist without throwing exceptions or bombing in any other
// way. Wait for them to terminate.
@@ -231,10 +225,10 @@ namespace tut
void object::test<2>()
{
set_test_name("stderr to log");
- NamedTempFile script("py",
- "import sys\n"
- "sys.stderr.write('''Hello from Python!\n"
- "note partial line''')\n");
+ NamedExtTempFile script("py",
+ "import sys\n"
+ "sys.stderr.write('''Hello from Python!\n"
+ "note partial line''')\n");
StringVec vcommand{ PYTHON, script.getName() };
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(), vcommand));
@@ -246,11 +240,11 @@ namespace tut
void object::test<3>()
{
set_test_name("bad stdout protocol");
- NamedTempFile script("py",
- "print('Hello from Python!')\n");
+ NamedExtTempFile script("py",
+ "print('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
@@ -259,13 +253,13 @@ namespace tut
void object::test<4>()
{
set_test_name("leftover stdout");
- NamedTempFile script("py",
- "import sys\n"
- // note lack of newline
- "sys.stdout.write('Hello from Python!')\n");
+ NamedExtTempFile script("py",
+ "import sys\n"
+ // note lack of newline
+ "sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
@@ -274,12 +268,12 @@ namespace tut
void object::test<5>()
{
set_test_name("bad stdout len prefix");
- NamedTempFile script("py",
- "import sys\n"
- "sys.stdout.write('5a2:something')\n");
+ NamedExtTempFile script("py",
+ "import sys\n"
+ "sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- sv(list_of(PYTHON)(script.getName()))));
+ StringVec{PYTHON, script.getName()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
@@ -381,17 +375,17 @@ namespace tut
set_test_name("round trip");
AckAPI api;
Result result;
- NamedTempFile script("py",
- boost::phoenix::placeholders::arg1 <<
- "from " << reader_module << " import *\n"
- // make a request on our little API
- "request(pump='" << api.getName() << "', data={})\n"
- // wait for its response
- "resp = get()\n"
- "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
- " else 'bad: ' + str(resp)\n"
- "send(pump='" << result.getName() << "', data=result)\n");
- waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
+ NamedExtTempFile script("py",
+ [&](std::ostream& out){ out <<
+ "from " << reader_module << " import *\n"
+ // make a request on our little API
+ "request(pump='" << api.getName() << "', data={})\n"
+ // wait for its response
+ "resp = get()\n"
+ "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
+ " else 'bad: ' + str(resp)\n"
+ "send(pump='" << result.getName() << "', data=result)\n";});
+ waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}));
result.ensure();
}
@@ -419,38 +413,38 @@ namespace tut
// iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
ReqIDAPI api;
Result result;
- NamedTempFile script("py",
- boost::phoenix::placeholders::arg1 <<
- "import sys\n"
- "from " << reader_module << " import *\n"
- // Note that since reader imports llsd, this
- // 'import *' gets us llsd too.
- "sample = llsd.format_notation(dict(pump='" <<
- api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
- // The whole packet has length prefix too: "len:data"
- "samplen = len(str(len(sample))) + 1 + len(sample)\n"
- // guess how many messages it will take to
- // accumulate BUFFERED_LENGTH
- "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
- "print('Sending %s requests' % count, file=sys.stderr)\n"
- "for i in range(count):\n"
- " request('" << api.getName() << "', dict(reqid=i))\n"
- // The assumption in this specific test that
- // replies will arrive in the same order as
- // requests is ONLY valid because the API we're
- // invoking sends replies instantly. If the API
- // had to wait for some external event before
- // sending its reply, replies could arrive in
- // arbitrary order, and we'd have to tick them
- // off from a set.
- "result = ''\n"
- "for i in range(count):\n"
- " resp = get()\n"
- " if resp['data']['reqid'] != i:\n"
- " result = 'expected reqid=%s in %s' % (i, resp)\n"
- " break\n"
- "send(pump='" << result.getName() << "', data=result)\n");
- waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
+ NamedExtTempFile script("py",
+ [&](std::ostream& out){ out <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Note that since reader imports llsd, this
+ // 'import *' gets us llsd too.
+ "sample = llsd.format_notation(dict(pump='" <<
+ api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
+ // The whole packet has length prefix too: "len:data"
+ "samplen = len(str(len(sample))) + 1 + len(sample)\n"
+ // guess how many messages it will take to
+ // accumulate BUFFERED_LENGTH
+ "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
+ "print('Sending %s requests' % count, file=sys.stderr)\n"
+ "for i in range(count):\n"
+ " request('" << api.getName() << "', dict(reqid=i))\n"
+ // The assumption in this specific test that
+ // replies will arrive in the same order as
+ // requests is ONLY valid because the API we're
+ // invoking sends replies instantly. If the API
+ // had to wait for some external event before
+ // sending its reply, replies could arrive in
+ // arbitrary order, and we'd have to tick them
+ // off from a set.
+ "result = ''\n"
+ "for i in range(count):\n"
+ " resp = get()\n"
+ " if resp['data']['reqid'] != i:\n"
+ " result = 'expected reqid=%s in %s' % (i, resp)\n"
+ " break\n"
+ "send(pump='" << result.getName() << "', data=result)\n";});
+ waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
300); // needs more realtime than most tests
result.ensure();
}
@@ -462,65 +456,62 @@ namespace tut
{
ReqIDAPI api;
Result result;
- NamedTempFile script("py",
- boost::phoenix::placeholders::arg1 <<
- "import sys\n"
- "from " << reader_module << " import *\n"
- // Generate a very large string value.
- "desired = int(sys.argv[1])\n"
- // 7 chars per item: 6 digits, 1 comma
- "count = int((desired - 50)/7)\n"
- "large = ''.join('%06d,' % i for i in range(count))\n"
- // Pass 'large' as reqid because we know the API
- // will echo reqid, and we want to receive it back.
- "request('" << api.getName() << "', dict(reqid=large))\n"
- "try:\n"
- " resp = get()\n"
- "except ParseError as e:\n"
- " # try to find where e.data diverges from expectation\n"
- // Normally we'd expect a 'pump' key in there,
- // too, with value replypump(). But Python
- // serializes keys in a different order than C++,
- // so incoming data start with 'data'.
- // Truthfully, though, if we get as far as 'pump'
- // before we find a difference, something's very
- // strange.
- " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
- " chunk = 40\n"
- " for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
- " if e.data[offset:offset+chunk] != \\\n"
- " expect[offset:offset+chunk]:\n"
- " print('Offset %06d: expect %r,\\n'\\\n"
- " ' get %r' %\\\n"
- " (offset,\n"
- " expect[offset:offset+chunk],\n"
- " e.data[offset:offset+chunk]),\n"
- " file=sys.stderr)\n"
- " break\n"
- " else:\n"
- " print('incoming data matches expect?!', file=sys.stderr)\n"
- " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
- " sys.exit(1)\n"
- "\n"
- "echoed = resp['data']['reqid']\n"
- "if echoed == large:\n"
- " send('" << result.getName() << "', '')\n"
- " sys.exit(0)\n"
- // Here we know echoed did NOT match; try to find where
- "for i in range(count):\n"
- " start = 7*i\n"
- " end = 7*(i+1)\n"
- " if end > len(echoed)\\\n"
- " or echoed[start:end] != large[start:end]:\n"
- " send('" << result.getName() << "',\n"
- " 'at offset %s, expected %r but got %r' %\n"
- " (start, large[start:end], echoed[start:end]))\n"
- "sys.exit(1)\n");
+ NamedExtTempFile script("py",
+ [&](std::ostream& out){ out <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Generate a very large string value.
+ "desired = int(sys.argv[1])\n"
+ // 7 chars per item: 6 digits, 1 comma
+ "count = int((desired - 50)/7)\n"
+ "large = ''.join('%06d,' % i for i in range(count))\n"
+ // Pass 'large' as reqid because we know the API
+ // will echo reqid, and we want to receive it back.
+ "request('" << api.getName() << "', dict(reqid=large))\n"
+ "try:\n"
+ " resp = get()\n"
+ "except ParseError as e:\n"
+ " # try to find where e.data diverges from expectation\n"
+ // Normally we'd expect a 'pump' key in there,
+ // too, with value replypump(). But Python
+ // serializes keys in a different order than C++,
+ // so incoming data start with 'data'.
+ // Truthfully, though, if we get as far as 'pump'
+ // before we find a difference, something's very
+ // strange.
+ " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
+ " chunk = 40\n"
+ " for offset in range(0, max(len(e.data), len(expect)), chunk):\n"
+ " if e.data[offset:offset+chunk] != \\\n"
+ " expect[offset:offset+chunk]:\n"
+ " print('Offset %06d: expect %r,\\n'\\\n"
+ " ' get %r' %\\\n"
+ " (offset,\n"
+ " expect[offset:offset+chunk],\n"
+ " e.data[offset:offset+chunk]),\n"
+ " file=sys.stderr)\n"
+ " break\n"
+ " else:\n"
+ " print('incoming data matches expect?!', file=sys.stderr)\n"
+ " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
+ " sys.exit(1)\n"
+ "\n"
+ "echoed = resp['data']['reqid']\n"
+ "if echoed == large:\n"
+ " send('" << result.getName() << "', '')\n"
+ " sys.exit(0)\n"
+ // Here we know echoed did NOT match; try to find where
+ "for i in range(count):\n"
+ " start = 7*i\n"
+ " end = 7*(i+1)\n"
+ " if end > len(echoed)\\\n"
+ " or echoed[start:end] != large[start:end]:\n"
+ " send('" << result.getName() << "',\n"
+ " 'at offset %s, expected %r but got %r' %\n"
+ " (start, large[start:end], echoed[start:end]))\n"
+ "sys.exit(1)\n";});
waitfor(LLLeap::create(test_name,
- sv(list_of
- (PYTHON)
- (script.getName())
- (stringize(size)))),
+ StringVec{PYTHON, script.getName(), stringize(size)}),
180); // try a longer timeout
result.ensure();
}
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 81449b4a42..56dc2e4fa9 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -151,8 +151,38 @@ struct PythonProcessLauncher
/// Launch Python script; verify that it launched
void launch()
{
- mPy = LLProcess::create(mParams);
- tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ try
+ {
+ mPy = LLProcess::create(mParams);
+ tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ }
+ catch (const tut::failure&)
+ {
+ // On Windows, if APR_LOG is set, our version of APR's
+ // apr_create_proc() logs to the specified file. If this test
+ // failed, try to report that log.
+ const char* APR_LOG = getenv("APR_LOG");
+ if (APR_LOG && *APR_LOG)
+ {
+ std::ifstream inf(APR_LOG);
+ if (! inf.is_open())
+ {
+ LL_WARNS() << "Couldn't open '" << APR_LOG << "'" << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS() << "==============================" << LL_ENDL;
+ LL_WARNS() << "From '" << APR_LOG << "':" << LL_ENDL;
+ std::string line;
+ while (std::getline(inf, line))
+ {
+ LL_WARNS() << line << LL_ENDL;
+ }
+ LL_WARNS() << "==============================" << LL_ENDL;
+ }
+ }
+ throw;
+ }
}
/// Run Python script and wait for it to complete.
@@ -214,30 +244,27 @@ static std::string python_out(const std::string& desc, const CONTENT& script)
class NamedTempDir: public boost::noncopyable
{
public:
- // Use python() function to create a temp directory: I've found
- // nothing in either Boost.Filesystem or APR quite like Python's
- // tempfile.mkdtemp().
- // Special extra bonus: on Mac, mkdtemp() reports a pathname
- // starting with /var/folders/something, whereas that's really a
- // symlink to /private/var/folders/something. Have to use
- // realpath() to compare properly.
NamedTempDir():
- mPath(python_out("mkdtemp()",
- "from __future__ import with_statement\n"
- "import os.path, sys, tempfile\n"
- "with open(sys.argv[1], 'w') as f:\n"
- " f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n"))
- {}
+ mPath(NamedTempFile::temp_path()),
+ mCreated(boost::filesystem::create_directories(mPath))
+ {
+ mPath = boost::filesystem::canonical(mPath);
+ }
~NamedTempDir()
{
- aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
+ if (mCreated)
+ {
+ boost::filesystem::remove_all(mPath);
+ }
}
- std::string getName() const { return mPath; }
+ std::string getName() const { return mPath.string(); }
+ std::string getNormalName() const { return mPath.lexically_normal().string(); }
private:
- std::string mPath;
+ boost::filesystem::path mPath;
+ bool mCreated;
};
/*****************************************************************************
@@ -565,7 +592,13 @@ namespace tut
" f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n");
// Before running, call setWorkingDirectory()
py.mParams.cwd = tempdir.getName();
- ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
+ std::string expected{ tempdir.getNormalName() };
+#if LL_WINDOWS
+ // SIGH, don't get tripped up by "C:" != "c:" --
+ // but on the Mac, using tolower() fails because "/users" != "/Users"!
+ expected = utf8str_tolower(expected);
+#endif
+ ensure_equals("os.getcwd()", py.run_read(), expected);
}
template<> template<>
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index d7c11c5021..335c25fcc9 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -44,18 +44,10 @@ typedef U32 uint32_t;
#include "llstring.h"
#endif
-#include "boost/range.hpp"
-#include "boost/foreach.hpp"
-#include "boost/function.hpp"
-#include "boost/bind.hpp"
-#include "boost/phoenix/bind/bind_function.hpp"
-#include "boost/phoenix/core/argument.hpp"
-using namespace boost::phoenix;
-
-#include "../llsd.h"
-#include "../llsdserialize.h"
+#include "llsd.h"
+#include "llsdserialize.h"
#include "llsdutil.h"
-#include "../llformat.h"
+#include "llformat.h"
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
@@ -1697,13 +1689,6 @@ namespace tut
struct TestPythonCompatible
{
TestPythonCompatible():
- // Note the peculiar insertion of __FILE__ into this string. Since
- // this script is being written into a platform-dependent temp
- // directory, we can't locate indra/lib/python relative to
- // Python's __file__. Use __FILE__ instead, navigating relative
- // to this C++ source file. Use Python raw-string syntax so
- // Windows pathname backslashes won't mislead Python's string
- // scanner.
import_llsd("import os.path\n"
"import sys\n"
"import llsd\n")
@@ -1801,7 +1786,7 @@ namespace tut
// helper for test<3>
static void writeLLSDArray(std::ostream& out, const LLSD& array)
{
- BOOST_FOREACH(LLSD item, llsd::inArray(array))
+ for (LLSD item: llsd::inArray(array))
{
LLSDSerialize::toNotation(item, out);
// It's important to separate with newlines because Python's llsd
@@ -1841,21 +1826,22 @@ namespace tut
// Create an llsdXXXXXX file containing 'data' serialized to
// notation.
NamedTempFile file("llsd",
- // NamedTempFile's boost::function constructor
+ // NamedTempFile's std::function constructor
// takes a callable. To this callable it passes the
// std::ostream with which it's writing the
// NamedTempFile.
- boost::bind(writeLLSDArray, _1, cdata));
+ [cdata](std::ostream& out){ writeLLSDArray(out, cdata); });
python("read C++ notation",
- placeholders::arg1 <<
+ [this, pydata, &file](std::ostream& out) {
+ out <<
import_llsd <<
"def parse_each(iterable):\n"
" for item in iterable:\n"
" yield llsd.parse(item)\n" <<
pydata <<
// Don't forget raw-string syntax for Windows pathnames.
- "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n");
+ "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n"; });
}
template<> template<>
@@ -1869,7 +1855,8 @@ namespace tut
NamedTempFile file("llsd", "");
python("write Python notation",
- placeholders::arg1 <<
+ [this, &file](std::ostream& out) {
+ out <<
import_llsd <<
"DATA = [\n"
" 17,\n"
@@ -1880,10 +1867,9 @@ namespace tut
"lines.''',\n"
"]\n"
// Don't forget raw-string syntax for Windows pathnames.
- // N.B. Using 'print' implicitly adds newlines.
- "with open(r'" << file.getName() << "', 'w') as f:\n"
+ "with open(r'" << file.getName() << "', 'wb') as f:\n"
" for item in DATA:\n"
- " print(llsd.format_notation(item).decode(), file=f)\n");
+ " f.writelines((llsd.format_notation(item), b'\\n'))\n"; });
std::ifstream inf(file.getName().c_str());
LLSD item;