diff options
Diffstat (limited to 'indra/llcommon')
| -rw-r--r-- | indra/llcommon/llprocess.cpp | 31 | ||||
| -rw-r--r-- | indra/llcommon/llprocess.h | 25 | ||||
| -rw-r--r-- | indra/llcommon/tests/llprocess_test.cpp | 43 | 
3 files changed, 91 insertions, 8 deletions
| diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index d7c297b952..1481bf571f 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -197,6 +197,25 @@ public:  	virtual LLEventPump& getPump() { return mPump; }  	virtual void setLimit(size_t limit) { mLimit = limit; }  	virtual size_t getLimit() const { return mLimit; } +    virtual std::size_t size() { return mStreambuf.size(); } + +	virtual std::string peek(std::size_t offset=0, +							 std::size_t len=(std::numeric_limits<std::size_t>::max)()) +	{ +		// Constrain caller's offset and len to overlap actual buffer content. +		std::size_t real_offset = (std::min)(mStreambuf.size(), offset); +		std::size_t real_end	= (std::min)(mStreambuf.size(), real_offset + len); +		boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data(); +		return std::string(boost::asio::buffers_begin(cbufs) + real_offset, +						   boost::asio::buffers_begin(cbufs) + real_end); +	} + +	virtual bool contains(const std::string& seek, std::size_t offset=0) +	{ +		// There may be a more efficient way to search mStreambuf contents, +		// but this is far the easiest... +		return peek(offset).find(seek) != std::string::npos; +	}  private:  	bool tick(const LLSD&) @@ -240,12 +259,13 @@ private:  									   << mStreambuf.size() << LL_ENDL;  				// Now that we've received new data, publish it on our -				// LLEventPump as advertised. Constrain it by mLimit. +				// LLEventPump as advertised. Constrain it by mLimit. But show +				// listener the actual accumulated buffer size, regardless of +				// mLimit.  				std::size_t datasize((std::min)(mLimit, mStreambuf.size())); -				boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data(); -				mPump.post(LLSDMap("data", LLSD::String( -									   boost::asio::buffers_begin(cbufs), -									   boost::asio::buffers_begin(cbufs) + datasize))); +				mPump.post(LLSDMap +						   ("data", peek(0, datasize)) +						   ("len", LLSD::Integer(mStreambuf.size())));  			}  		}  		return false; @@ -985,5 +1005,4 @@ void LLProcess::reap(void)  	}  }  |*==========================================================================*/ -  #endif  // Posix diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h index 448a88f4c0..bf0517600d 100644 --- a/indra/llcommon/llprocess.h +++ b/indra/llcommon/llprocess.h @@ -331,6 +331,31 @@ public:  		virtual std::istream& get_istream() = 0;  		/** +		 * Get accumulated buffer length. +		 * Often we need to refrain from actually reading the std::istream +		 * returned by get_istream() until we've accumulated enough data to +		 * make it worthwhile. For instance, if we're expecting a number from +		 * the child, but the child happens to flush "12" before emitting +		 * "3\n", get_istream() >> myint could return 12 rather than 123! +		 */ +		virtual std::size_t size() = 0; + +		/** +		 * Peek at accumulated buffer data without consuming it. Optional +		 * parameters give you substr() functionality. +		 * +		 * @note You can discard buffer data using get_istream().ignore(n). +		 */ +		virtual std::string peek(std::size_t offset=0, +								 std::size_t len=(std::numeric_limits<std::size_t>::max)()) = 0; + +		/** +		 * Search accumulated buffer data without retrieving it. Optional +		 * offset allows you to start at specified position. +		 */ +		virtual bool contains(const std::string& seek, std::size_t offset=0) = 0; + +		/**  		 * Get LLEventPump& on which to listen for incoming data. The posted  		 * LLSD::Map event will contain a key "data" whose value is an  		 * LLSD::String containing (part of) the data accumulated in the diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index a901c577d6..2db17cae97 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -919,6 +919,7 @@ namespace tut                          message, "somename");      } +    /*-------------- support for "get*Pipe() validation" test --------------*/  #define TEST_getPipe(PROCESS, GETPIPE, GETOPTPIPE, VALID, NOPIPE, BADPIPE) \      do                                                                  \      {                                                                   \ @@ -985,11 +986,49 @@ namespace tut                       LLProcess::STDIN);  // BADPIPE      } +    template<> template<> +    void object::test<16>() +    { +        set_test_name("talk to stdin/stdout"); +        PythonProcessLauncher py("stdin/stdout", +                                 "import sys, time\n" +                                 "print 'ok'\n" +                                 "sys.stdout.flush()\n" +                                 "# wait for 'go' from test program\n" +                                 "go = sys.stdin.readline()\n" +                                 "if go != 'go\\n':\n" +                                 "    sys.exit('expected \"go\", saw %r' % go)\n" +                                 "print 'ack'\n"); +        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin +        py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout +        py.mPy = LLProcess::create(py.mParams); +        ensure("couldn't launch stdin/stdout script", py.mPy); +        LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT)); +        int i, timeout = 60; +        for (i = 0; i < timeout && py.mPy->isRunning() && childout.size() < 3; ++i) +        { +            yield(); +        } +        ensure("script never started", i < timeout); +        std::string line; +        std::getline(childout.get_istream(), line); +        ensure_equals("bad wakeup from stdin/stdout script", line, "ok"); +        py.mPy->getWritePipe().get_ostream() << "go" << std::endl; +        for (i = 0; i < timeout && py.mPy->isRunning() && ! childout.contains("\n"); ++i) +        { +            yield(); +        } +        ensure("script never replied", childout.contains("\n")); +        std::getline(childout.get_istream(), line); +        ensure_equals("child didn't ack", line, "ack"); +        ensure_equals("bad child termination", py.mPy->getStatus().mState, LLProcess::EXITED); +        ensure_equals("bad child exit code",   py.mPy->getStatus().mData,  0); +    } +      // TODO: -    // test pipe for stdin, stdout (etc.) -    // test getWritePipe().get_ostream(), getReadPipe().get_istream()      // test listening on getReadPipe().getPump(), disconnecting      // test setLimit(), getLimit()      // test EOF -- check logging +    // test peek() with substr  } // namespace tut | 
