diff options
Diffstat (limited to 'indra/llcommon/tests')
| -rw-r--r-- | indra/llcommon/tests/llprocesslauncher_test.cpp | 97 | 
1 files changed, 85 insertions, 12 deletions
| diff --git a/indra/llcommon/tests/llprocesslauncher_test.cpp b/indra/llcommon/tests/llprocesslauncher_test.cpp index 4d14e1be53..ca06b3164e 100644 --- a/indra/llcommon/tests/llprocesslauncher_test.cpp +++ b/indra/llcommon/tests/llprocesslauncher_test.cpp @@ -17,6 +17,7 @@  // STL headers  #include <vector>  // std headers +#include <errno.h>  // external library headers  #include "llapr.h"  #include "apr_thread_proc.h" @@ -71,11 +72,53 @@ namespace tut      typedef llprocesslauncher_group::object object;      llprocesslauncher_group llprocesslaunchergrp("llprocesslauncher"); +    struct Item +    { +        Item(): tries(0) {} +        unsigned    tries; +        std::string which; +        std::string what; +    }; +      template<> template<>      void object::test<1>()      {          set_test_name("raw APR nonblocking I/O"); +        // Create a script file in a temporary place. +        const char* tempdir = NULL; +        aprchk(apr_temp_dir_get(&tempdir, apr.pool)); + +        // Construct a temp filename template in that directory. +        char *tempname = NULL; +        aprchk(apr_filepath_merge(&tempname, tempdir, "testXXXXXX", 0, apr.pool)); + +        // Create a temp file from that template. +        apr_file_t* fp = NULL; +        aprchk(apr_file_mktemp(&fp, tempname, APR_CREATE | APR_WRITE | APR_EXCL, apr.pool)); + +        // Write it. +        const char script[] = +            "import sys\n" +            "import time\n" +            "\n" +            "time.sleep(2)\n" +            "print >>sys.stdout, \"stdout after wait\"\n" +            "sys.stdout.flush()\n" +            "time.sleep(2)\n" +            "print >>sys.stderr, \"stderr after wait\"\n" +            "sys.stderr.flush()\n" +            ; +        apr_size_t len(sizeof(script)-1); +        aprchk(apr_file_write(fp, script, &len)); +        aprchk(apr_file_close(fp)); + +        // Arrange to track the history of our interaction with child: what we +        // fetched, which pipe it came from, how many tries it took before we +        // got it. +        std::vector<Item> history; +        history.push_back(Item()); +          apr_procattr_t *procattr = NULL;          aprchk(apr_procattr_create(&procattr, apr.pool));          aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)); @@ -84,8 +127,7 @@ namespace tut          std::vector<const char*> argv;          apr_proc_t child;          argv.push_back("python"); -        argv.push_back("-c"); -        argv.push_back("raise RuntimeError('Hello from Python!')"); +        argv.push_back(tempname);          argv.push_back(NULL);          aprchk(apr_proc_create(&child, argv[0], @@ -110,24 +152,35 @@ namespace tut                  apr_status_t rv = apr_file_gets(buf, sizeof(buf), iterfiles[i].second);                  if (APR_STATUS_IS_EOF(rv))                  { -                    std::cout << "(EOF on " << iterfiles[i].first << ")\n"; +//                  std::cout << "(EOF on " << iterfiles[i].first << ")\n"; +                    history.back().which = iterfiles[i].first; +                    history.back().what  = "*eof*"; +                    history.push_back(Item());                      outfiles.erase(outfiles.begin() + i);                      continue;                  } -                if (rv != APR_SUCCESS) +                if (rv == EWOULDBLOCK)                  { -                    std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n"; +//                  std::cout << "(waiting; apr_file_gets(" << iterfiles[i].first << ") => " << rv << ": " << apr.strerror(rv) << ")\n"; +                    ++history.back().tries;                      continue;                  } +                ensure_equals(rv, APR_SUCCESS);                  // Is it even possible to get APR_SUCCESS but read 0 bytes?                  // Hope not, but defend against that anyway.                  if (buf[0])                  { -                    std::cout << iterfiles[i].first << ": " << buf; -                    // Just for pretty output... if we only read a partial -                    // line, terminate it. -                    if (buf[strlen(buf) - 1] != '\n') -                        std::cout << "...\n"; +//                  std::cout << iterfiles[i].first << ": " << buf; +                    history.back().which = iterfiles[i].first; +                    history.back().what.append(buf); +                    if (buf[strlen(buf) - 1] == '\n') +                        history.push_back(Item()); +                    else +                    { +                        // Just for pretty output... if we only read a partial +                        // line, terminate it. +//                      std::cout << "...\n"; +                    }                  }              }              sleep(1); @@ -141,9 +194,29 @@ namespace tut          apr_status_t rv;          while (! APR_STATUS_IS_CHILD_DONE(rv = apr_proc_wait(&child, &rc, &why, APR_NOWAIT)))          { -            std::cout << "child not done (" << rv << "): " << apr.strerror(rv) << '\n'; +//          std::cout << "child not done (" << rv << "): " << apr.strerror(rv) << '\n';              sleep(0.5);          } -        std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n'; +//      std::cout << "child done: rv = " << rv << " (" << apr.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n'; +        ensure_equals(rv, APR_CHILD_DONE); +        ensure_equals(why, APR_PROC_EXIT); +        ensure_equals(rc, 0); + +        // Remove temp script file +        aprchk(apr_file_remove(tempname, apr.pool)); + +        // Beyond merely executing all the above successfully, verify that we +        // obtained expected output -- and that we duly got control while +        // waiting, proving the non-blocking nature of these pipes. +        ensure("blocking I/O on child pipe (0)", history[0].tries); +        ensure_equals(history[0].which, "out"); +        ensure_equals(history[0].what,  "stdout after wait\n"); +        ensure("blocking I/O on child pipe (1)", history[1].tries); +        ensure_equals(history[1].which, "out"); +        ensure_equals(history[1].what,  "*eof*"); +        ensure_equals(history[2].which, "err"); +        ensure_equals(history[2].what,  "stderr after wait\n"); +        ensure_equals(history[3].which, "err"); +        ensure_equals(history[3].what,  "*eof*");      }  } // namespace tut | 
