Commit Graph

176 Commits (ca7abc4c3be9310f4e5fec00b7d6ffadaba58ff0)

Author SHA1 Message Date
Nat Goodspeed eb1bea2223 IQA-463: LLError::addRecorder() claims ownership of passed Recorder*.
That is, when the underlying LLError::Settings object is destroyed -- possibly
at termination, possibly on LLError::restoreSettings() -- the passed Recorder*
is deleted.
There was much existing code that seemed as unaware of this alarming fact as I
was myself. Passing to addRecorder() a pointer to a stack object, or to a
member of some other object, is just Bad. It might be preferable to make
addRecorder() accept std::auto_ptr<Recorder> to make the ownership transfer
more explicit -- or even boost::shared_ptr<Recorder> instead, which would
allow the caller to either forget or retain the passed Recorder.
This preliminary pass retains the Recorder* dumb pointer API, but documents
the ownership issue, and eliminates known instances of passing pointers to
anything but a standalone heap Recorder subclass object.
2012-04-23 11:26:18 -04:00
Nat Goodspeed be669d4a1f On Windows, make "very large message" test ridiculously small.
This test must not be subject to spurious environmental failures, else some
kind soul will disable it entirely. We observe that APR specifies a hard-coded
buffer size of 64Kbytes for pipe creation -- use that and cross fingers.
2012-03-15 16:51:34 -04:00
Nat Goodspeed 4e889ee98e Backed out changeset 22664c76b59e (reinstate Windows pipe workaround)
Sigh, the rejoicing was premature.
2012-03-14 14:38:47 -04:00
Nat Goodspeed a16f2faa8d Backed out changeset 51205a909e2c (Windows APR pipe bug workaround)
If in fact we've managed to fix the APR bug writing to a Windows named pipe,
it should no longer be necessary to try to work around it by testing with a
much smaller data volume on Windows!
2012-03-14 13:14:24 -04:00
Nat Goodspeed b669b6262a On Windows, try cutting down the size of a "very large message."
Ideally we'd love to be able to nail the underlying bug, but log output
suggests it may actually go all the way down to the OS level. To move forward,
try to bypass it.
2012-03-14 09:57:52 -04:00
Nat Goodspeed bdc27815ca If very-large-message test fails, search for a size that works.
We want to write a robust test that consistently works. On Windows, that
appears to require constraining the max message size. I, the coder, could try
submitting test runs of varying sizes to TC until I found a size that works...
but that could take quite a while. If I were clever, I might even use a manual
binary search. But computers are good at binary searching; there are even
prepackaged algorithms in the STL. If I were cleverer still, I could make the
test program itself search for size that works.
2012-03-13 17:17:20 -04:00
Nat Goodspeed 1bdc876b79 Increase timeout for very-large-message test.
Apparently, at least on Mac, there are circumstances in which the very-large-
message test can take several times longer than normal, yet still complete
successfully. This is always the problem with timeouts: does timeout
expiration mean that the code in question is actually hung, or would it
complete if given a bit longer?
If very-large-message test fails, retry a few times with smaller sizes to try
to find a size at which the test runs reliably. The default size, ca 1MB, is
intended to be substantially larger than anything we'll encounter in the wild.
Is that "unreasonably" large? Is there a "reasonable" size at which the test
could consistently pass? Is that "reasonable" size still larger than what we
expect to encounter in practice? Need more information, hence this code.
2012-03-13 14:28:19 -04:00
Nat Goodspeed 7d3cf544c7 Add timeout functionality to waitfor() helper functions.
Otherwise, a stuck child process could potentially hang the test, and thus the
whole viewer build.
2012-03-13 14:15:11 -04:00
Nat Goodspeed 2491e2bda5 Additional diagnostic code to track down strange Windows pipe error.
It seems that under certain circumstances, write logic was duplicating a chunk
of the data being streamed down our pipe. But as this condition is only driven
with a very large data stream, eyeballing that data stream is tedious. Add
code to compare the raw received data with the expected stream, reporting
where and how they first differ.
2012-03-05 18:55:48 -05:00
Nat Goodspeed b3b51f012f Move std::ostream << CaptureLog logic into CaptureLog::streamto().
That lets us reliably declare the operator<<() free function inline, which
permits multiple translation units in the same executable to #include
"wrapllerrs.h".
2012-03-05 13:03:23 -05:00
Nat Goodspeed 30e8e23d7c Simplify llleap_test.cpp plugin by reading individual characters.
While we're accumulating the 'length:' prefix, the present socket-based logic
reads 20 characters, then reads 'length' more, then discards any excess (in
case the whole 'length:data' packet ends up being less than 20 characters).
That's probably a bug: whatever characters follow that packet, however short
it may be, are probably the 'length:' prefix of the next packet. We probably
only get away with it because we probably never send packets that short.
Earlier llleap_test.cpp plugin logic still read 20 characters, then, if there
were any left after the present packet, cached them as the start of the next
packet. This is probably more correct, but complicated. Easier just to read
individual characters until we've seen 'length:', then try for exactly the
specified length over however many reads that requires.
2012-03-04 21:27:42 -05:00
Nat Goodspeed ca703b2b9c Make llleap_test.cpp avoid hard limit on MSVC std::ostringstream max.
In load testing, we have observed intermittent failures on Windows in which
LLSDNotationStreamer into std::ostringstream seems to bump into a hard limit
of 1048590 bytes. ostringstream reports that much buffered data and returns
that much -- even though, on examination, the notation-serialized stream is
incomplete at that point. It's our intention to load-test LLLeap and
LLProcess, not the local iostream implementation; we hope that this kind of
data volume is comfortably greater than actual usage. Back off the
load-testing max size a bit.
2012-03-04 16:59:48 -05:00
Nat Goodspeed d72d9f73b6 Add debugging output in case LLLeap writes corrupt data to plugin.
New llleap_test.cpp load testing turned up Windows issue in which plugin
process received corrupt packet, producing LLSDParseError. Add code to dump
the bad packet in that case -- but if LLSDParseError is willing to state the
offset of the problem, not ALL of the packet.
Quiet MSVC warning about little internal base class needing virtual destructor.
2012-03-03 06:37:05 -05:00
Nat Goodspeed 674f9fb111 Add LLLeap unit test for invalid length prefix from child stdout. 2012-03-02 14:56:00 -05:00
Nat Goodspeed d09d4e1a7e Add LLLeap unit tests for strange data on child stdout. 2012-03-02 13:43:13 -05:00
Nat Goodspeed c6c7cabd1d Add "load test" LLLeap unit tests: many small messages, one large.
These tests rule out corruption as we cross buffer boundaries in OS pipes and
the LLLeap implementation itself.
2012-03-02 12:00:38 -05:00
Nat Goodspeed d16ce6bf04 Drag in Python llsd module, which greatly simplifies tests.
It only took a few examples of trying to wrangle notation LLSD as string data
to illustrate how clumsy that is. I'd forgotten that a couple other TUT tests
already invoke Python code that depends on the llsd module. The trick is to
recognize that at least as of now, there's still an obsolete version of the
module in the viewer's own source tree. Python code is careful to try
importing llbase.llsd before indra.base.llsd, so that if/when we finally do
clear indra/lib/python from the viewer repo, we need only require that llbase
be installed on every build machine.
2012-03-02 09:47:27 -05:00
Nat Goodspeed 9b5fcb78e7 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.
2012-03-01 23:01:37 -05:00
Nat Goodspeed cfe37cbfb5 Break out std::ostream << CaptureLog routine for general use. 2012-03-01 22:45:16 -05:00
Nat Goodspeed 0ef99cd33b Add LLLeap class, initial implementation, initial unit tests.
Instantiating LLLeap with a command to execute a particular child process sets
up machinery to speak LLSD Event API Plugin protocol with that child process.
LLLeap is an LLInstanceTracker subclass, so the code that instantiates need
not hold the pointer. LLLeap monitors child-process termination and deletes
itself when done.
2012-03-01 17:46:44 -05:00
Nat Goodspeed f0612f6fc4 Allow CaptureLog's consumer to specify desired log level.
Of course, given the way the log machinery works, it's really "everything at
that level or stronger."
2012-03-01 17:30:50 -05:00
Nat Goodspeed f904867720 Make CaptureLog::withMessage() raise tut::failure if not found.
All known callers were using ensure(! withMessage(...).empty()). Centralize
that logic. Make failure message report the string being sought and the log
messages in which it wasn't found.
In case someone does want to permit the search to fail, add an optional
'required' parameter, default true.
Leverage new functionality in llprocess_test.cpp.
2012-03-01 14:33:23 -05:00
Nat Goodspeed 2596816f31 Break out TestRecorder class as CaptureLog into wrapllerrs.h.
Giving more unit tests the ability to capture and examine log output is
generally useful. Renaming the class just makes it less ambiguous: what's a
TestRecorder? Something that records tests?
2012-03-01 13:43:52 -05:00
Nat Goodspeed 3649eda62a Guarantee LLProcess::Params::postend listener any ReadPipe data.
Previously one might get process-terminated notification but still have to
wait for the child process's final data to arrive on one or more ReadPipes.
That required complex consumer timing logic to handle incomplete pending
ReadPipe data, e.g. a partial last line with no terminating newline. New code
guarantees that by the time LLProcess sends process-terminated notification,
all pending pipe data will have been buffered in ReadPipes.
Document LLProcess::ReadPipe::getPump() notification event; add "eof" key.
Add LLProcess::ReadPipe::getline() and read() convenience methods.
Add static LLProcess::getline() and basename() convenience methods, publishing
logic already present elsewhere.
Use ReadPipe::getline() and read() in unit tests.
Add unit test for "eof" event on ReadPipe::getPump().
Add unit test verifying that final data have been buffered by termination
notification event.
2012-02-29 17:10:19 -05:00
Nat Goodspeed 7fd281ac99 Reduce redundancy in llprocess_test.cpp using get_test_name(). 2012-02-27 15:24:14 -05:00
Nat Goodspeed 063edac43f Automated merge with file:///Users/nat/linden/viewer-leap-daggy 2012-02-27 14:48:39 -05:00
Nat Goodspeed 6f53796ccf Add LLInstanceTracker test for exception in subclass constructor.
We want to verify the sequence:
LLInstanceTracker constructor adds instance to underlying container
Subclass constructor throws exception
LLInstanceTracker destructor removes instance from underlying container.
2012-02-27 14:47:40 -05:00
Nat Goodspeed e7ab3da7a7 Automated merge with file:///Users/nat/linden/viewer-leap-daggy 2012-02-27 11:51:57 -05:00
Nat Goodspeed c0318d1bf9 Make LLInstanceTracker<T, T*>::getInstance(T*) validate passed T*.
For the T* specialization (no string, or whatever, key), the original
getInstance() method simply returned the passed-in T* value. It was defined,
as the comments noted, for completeness of the analogy with the keyed
LLInstanceTracker specialization.
It turns out, though, that getInstance(T*) can still be useful to ask whether
the T* you have in hand still references a valid T instance. Support that
usage.
2012-02-27 11:50:47 -05:00
Nat Goodspeed bf7c215692 Add LLStringUtil::getTokens() test for quoted empty string.
This is an important differentiator between getTokens() and the present
LLCommandLineParser::parseCommandLineString() logic: you cannot currently
--set SomeVar to an empty string value because parseCommandLineString()
discards empty strings.
2012-02-26 07:17:08 -05:00
Nat Goodspeed d2faf5d25a Get rid of indra/llcommon/tests/setpython.py.
run_build_test.py already has the capability to set environment variables, and
we may as well direct it to set PYTHON to the running Python interpreter. That
completely eliminates one level of process wrapper.
2012-02-24 17:14:07 -05:00
Nat Goodspeed 025329b6a2 Add LLStringUtil::getTokens() overload handling quoted substrings.
We didn't have any tokenizer suitable for scanning something like a bash
command line. We do have a couple hacks, e.g. LLExternalEditor::tokenize() and
LLCommandLineParser::parseCommandLineString(). Both try to work around
boost::tokenizer limitations; but existing boost::tokenizer support just
doesn't address this case. Neither of the above is available as a general
scanner anyway, and parseCommandLineString() fails outright when passed "".
New getTokens() also distinguishes between "drop delimiters" (e.g. space,
return, newline) to be discarded from the token stream, versus "keep
delimiters" (e.g. "+-*/") to be returned as tokens in their own right.
There's an overload that honors escapes and a more efficient one that doesn't;
each has a convenience overload that returns the scanned string vector rather
than requiring a separate declaration.
Tweak and comment older getTokens() implementation.
Add unit tests for both old and new getTokens() implementations.
Break out StringVec and std::ostream << StringVec from
indra/llcommon/tests/listener.h to StringVec.h: that's coming in handy for a
number of different TUT test sources.
2012-02-24 15:06:44 -05:00
Nat Goodspeed 14ddc6474a Tighten up LLProcess pipe support, per Richard's code review.
Clarify wording in some of the doc comments; be a bit more explicit about some
of the parameter fields.
Make some query methods 'const'.
Change default LLProcess::ReadPipe::getLimit() value to 0: don't post any
incoming data with notification event unless caller requests it. But do post
pertinent FILESLOT in case caller reuses same listener for both stdout and
stderr.
Use more idiomatic, readable syntax for accessing LLProcess::Params data.
2012-02-23 13:41:26 -05:00
Nat Goodspeed 999484a608 Let LLProcess consumer specify desired description for logging.
If caller runs (e.g.) a Python script, it's not very helpful to a human log
reader to keep seeing LLProcess instances logged as /pathname/to/python (pid).
If caller is aware, the code can at least use the script name as the desc --
or maybe even a hint as to the script's purpose.
If caller doesn't explicitly pass a desc, at least shorten to just the
basename of the executable.
2012-02-20 14:22:32 -05:00
Nat Goodspeed 8b5d5f9652 Make LLProcess post termination event to specified pump if desired.
This way a caller need not spin on isRunning(); we can just listen for the
requested termination event.
Post a similar event containing error message if for any reason
LLProcess::create() failed to launch the child.
Add unit tests for both cases.
2012-02-20 12:40:38 -05:00
Nat Goodspeed f52cf4be70 Fix typos in a few LLProcess::ReadPipe::find() unit tests.
The typos didn't make for invalid tests, but they made a few tests redundant
while leaving other (subtly different) cases untested.
2012-02-16 21:10:06 -05:00
Nat Goodspeed a06ba836c7 Fix bug in LLProcess::ReadPipe::peek() substring computation.
Add unit tests for peek() with substring args, reimplemented contains(),
various forms of find().
(yay unit tests)
2012-02-16 17:35:34 -05:00
Nat Goodspeed 4ecf9d6a2d Add unit test for LLProcess::ReadPipe::setLimit(). 2012-02-16 16:40:14 -05:00
Nat Goodspeed e92c311354 Add LLProcess::ReadPipe::find() methods, with corresponding npos.
If it's useful to have contains() to tell you whether incoming data contains a
particular substring, and if it's useful for contains() and peek() to accept
an offset within that data, then it's useful to allow you to get the offset of
a desired substring within that data. But of course a find() returning offset
needs something like std::string::npos for "not found"; borrow that
convention.
Support both find(const std::string&) and find(char); the latter permits a
more efficient implementation. In fact, make find(string) recognize a string
of length 1 and leverage the find(char) implementation.
Given that, reimplement contains(mumble) as shorthand for find(mumble) != npos.
Implement find() overloads using std::search() and std::find() on
boost::asio::streambuf character iterators, rather than copying to std::string
and then using string search like previous contains() implementation.
Reimplement WritePipeImpl::tick() and ReadPipeImpl::tick() to write/read
directly from/to boost::asio::streambuf data, instead of copying to/from a
temporary flat buffer.
As long as ReadPipeImpl::tick() keeps successfully filling buffers, keep
reading. Previous implementation would only handle a long child write over
successive tick() calls. Stop on read error or when we come up short.
2012-02-16 16:05:04 -05:00
Nat Goodspeed 85057908c3 Add unit test for listening on LLProcess::ReadPipe::getPump(). 2012-02-15 21:55:53 -05:00
Nat Goodspeed fc6d70db87 Don't be confused by "\r\n" line endings on pipe on Windows.
These are all very well when we just want to dump the output to a log, or
whatever, but in a unit-test context it matters for comparison.
2012-02-15 20:57:25 -05:00
Nat Goodspeed 56d931216e Add LLProcess::ReadPipe::size(), peek(), contains().
Also add "len" key to event data on LLProcess::getPump(). If you've used
setLimit(), event["data"].length() may not reflect the length of the
accumulated data in the ReadPipe.
Add unit test with stdin/stdout handshake with child process.
2012-02-15 15:47:03 -05:00
Nat Goodspeed 10ab4adc86 Fix llprocess_test.cpp's exception catching for Linux.
In the course of re-enabling the indra/test tests last year, Log generalized a
workaround I'd introduced in llsdmessage_test.cpp. In Linux viewer land, a
test program trying to catch an expected exception can't seem to catch it by
its specific class (across the libllcommon.so boundary), but must instead
catch std::runtime_error and validate the typeid().name() string. Log added a
macro for this idiom in llevents_tut.cpp. Generalize that macro further for
normal-case processing as well, move it to a header file of its own and use it
in all known places -- plus the new exception-catching tests in
llprocess_test.cpp.
2012-02-15 13:44:43 -05:00
Nat Goodspeed c6ccdb5b50 Add tests for LLProcess::get[Opt][Read|Write]Pipe() validations. 2012-02-15 12:11:38 -05:00
Nat Goodspeed e239cad1f5 Preliminary pipe support for LLProcess.
Add LLProcess::FileParam to specify how to construct each child's standard
file slot, with lots of comments about features designed but not yet
implemented. The point is to design it with enough flexibility to be able to
extend to foreseeable use cases.
Add LLProcess::Params::files to collect up to 3 FileParam items. Naturally
this extends the accepted LLSD syntax as well.
Implement type="" (child inherits parent file descriptor) and "pipe" (parent
constructs anonymous pipe to pass to child).
Add LLProcess::FILESLOT enum, plus methods:
getReadPipe(FILESLOT), getOptReadPipe(FILESLOT)
getWritePipe(), getOptWritePipe()
getPipeName(FILESLOT): placeholder implementation for now
Add LLProcess::ReadPipe and WritePipe classes, as returned by get*Pipe().
WritePipe supports get_ostream() method for streaming to child stdin.
ReadPipe supports get_istream() method for reading from child stdout/stderr.
It also provides getPump() returning LLEventPump& so interested parties can
listen for arrival of new data on the aforementioned std::istream.
For "pipe" slots, instantiate appropriate *Pipe class.
ReadPipe and WritePipe classes are pure virtual bases for ReadPipeImpl and
WritePipeImpl, respectively: all implementation data are hidden in the latter
classes, visible only in llprocess.cpp. In fact each *PipeImpl class registers
itself for "mainloop" ticks, attempting nonblocking I/O to the underlying
apr_file_t on each tick. Data are buffered in a boost::asio::streambuf, which
bridges between std::[io]stream and the APR I/O calls.
Sanity-test ReadPipeImpl by using a pipe to absorb the Python "SyntaxError"
output from the successful syntax_error test, rather than alarming the user.
Add first few unit tests for validating FileParam. More tests coming!
2012-02-15 10:07:09 -05:00
Nat Goodspeed aae61392be Use per-frame ticks on "mainloop" LLEventPump to update LLProcess.
When we reimplemented LLProcess on APR, necessitating APR's funny callback
mechanism to sense child-process status, every isRunning() or getStatus() call
called the APR poll function that calls ALL registered LLProcess callbacks. In
other words, every time any consumer called any LLProcess::isRunning() method,
all LLProcess callbacks were redundantly fired. Change that so that the single
APR poll function is called once per frame, courtesy of the "mainloop"
LLEventPump. Once per viewer frame should be well within the realtime duration
in which it's reasonable to expect child-process status to change.
In effect, this changes LLProcess's public API to introduce a dependency on
"mainloop" ticks. Add such ticks to llprocess_test.cpp as well.
2012-02-13 17:38:25 -05:00
Nat Goodspeed d4f887e43c Add unit tests for LLProcess::Status functionality. 2012-02-13 16:18:46 -05:00
Nat Goodspeed 32e11494ff Use os.path.normcase(os.path.normpath()) when comparing directories.
Once again we've been bitten by comparison failure between "c:\somepath" and
"C:\somepath". Normalize paths in both Python helper scripts to make that
comparison more robust.
2012-02-07 12:28:57 -05:00
Nat Goodspeed aafb03b29f Convert LLProcess implementation from platform-specific to using APR.
Include logic to engage Linden apr_procattr_autokill_set() extension: on
Windows, magic CreateProcess() flag must be pushed down into apr_proc_create()
level. When using an APR package without that extension, present
implementation should lock (e.g.) SLVoice.exe lifespan to viewer's on Windows
XP but probably won't on Windows 7: need magic flag on CreateProcess().
Using APR child-termination callback requires us to define state (e.g.
LLProcess::RUNNING). Take the opportunity to present Status, capturing state
and (if terminated) rc or signal number; but since most of the time all caller
really wants is to log the outcome, also present status string, encapsulating
logic to examine state and describe exited-with-rc vs. killed-by-signal.
New Status logic may report clearer results in the case of a Windows child
process killed by exception.
Clarify that static LLProcess::isRunning(handle) overload is only for use when
the original LLProcess object has been destroyed: really only for unit tests.
We necessarily retain our original platform-specific implementations for just
that one method. (Nonstatic isRunning() no longer calls static method.)
Clarify log output from llprocess_test.cpp in a couple places.
2012-02-07 10:53:23 -05:00
Nat Goodspeed 85581eefa6 Expose 'handle' as well as 'id' on LLProcess objects.
On Posix, these and the corresponding getProcessID()/getProcessHandle()
accessors produce the same pid_t value; but on Windows, it's useful to
distinguish an int-like 'id' useful to human log readers versus an opaque
'handle' for passing to platform-specific API functions. So make the
distinction in a platform-independent way.
2012-01-30 12:11:44 -05:00