www.pudn.com > ParaView3.rar > pvTestDriver.cxx


/*========================================================================= 
 
  Program:   ParaView 
  Module:    $RCSfile: pvTestDriver.cxx,v $ 
 
  Copyright (c) Kitware, Inc. 
  All rights reserved. 
  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details. 
 
     This software is distributed WITHOUT ANY WARRANTY; without even 
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
     PURPOSE.  See the above copyright notice for more information. 
 
=========================================================================*/ 
 
#include "vtkSystemIncludes.h" 
 
#include "pvTestDriver.h" 
#include "pvTestDriverConfig.h" 
 
#include  
 
#if !defined(_WIN32) || defined(__CYGWIN__) 
# include  
# include  
#endif 
 
// The main function as this class should only be used by this program 
int main(int argc, char* argv[]) 
{ 
  pvTestDriver d; 
  return d.Main(argc, argv); 
} 
 
pvTestDriver::pvTestDriver() 
{ 
  this->AllowErrorInOutput = 0; 
  this->RenderServerNumProcesses = 0; 
  this->TimeOut = 300; 
  this->ServerExitTimeOut = 60; 
  this->TestRenderServer = 0; 
  this->TestServer = 0; 
  this->TestTiledDisplay = 0; 
  this->ReverseConnection = 0; 
} 
 
pvTestDriver::~pvTestDriver() 
{ 
} 
 
// now implement the pvTestDriver class 
 
void pvTestDriver::SeparateArguments(const char* str, 
                                     vtkstd::vector& flags) 
{ 
  vtkstd::string arg = str; 
  vtkstd::string::size_type pos1 = 0; 
  vtkstd::string::size_type pos2 = arg.find_first_of(" ;"); 
  if(pos2 == arg.npos) 
    { 
    flags.push_back(str); 
    return; 
    } 
  while(pos2 != arg.npos) 
    { 
    flags.push_back(arg.substr(pos1, pos2-pos1)); 
    pos1 = pos2+1; 
    pos2 = arg.find_first_of(" ;", pos1+1); 
    } 
  flags.push_back(arg.substr(pos1, pos2-pos1)); 
} 
 
 
void pvTestDriver::CollectConfiguredOptions() 
{ 
  // try to make sure that this timesout before dart so it can kill all the processes 
  this->TimeOut = DART_TESTING_TIMEOUT - 10.0; 
  if(this->TimeOut < 0) 
    { 
    this->TimeOut = 1500; 
    } 
// set the path to the binary directory 
  this->ParaView = PARAVIEW_BINARY_DIR; 
#ifdef  CMAKE_INTDIR 
  this->ParaView  += "/" CMAKE_INTDIR; 
#endif 
  // now set the base part of the executables 
  this->ParaViewClient = this->ParaView; 
  this->ParaViewRenderServer = this->ParaView; 
  this->ParaViewServer = this->ParaView; 
  this->ParaViewDataServer = this->ParaView; 
  // now set the final execuable names 
  this->ParaView += "/" PARAVIEW_CLIENT; 
  this->ParaViewClient += "/" PARAVIEW_CLIENT; 
  this->ParaViewRenderServer += "/" PARAVIEW_RENDER_SERVER; 
  this->ParaViewServer += "/" PARAVIEW_SERVER; 
  this->ParaViewDataServer += "/" PARAVIEW_DATA_SERVER; 
 
  // now find all the mpi information if mpi run is set 
#ifdef VTK_USE_MPI 
#ifdef VTK_MPIRUN_EXE 
  this->MPIRun = VTK_MPIRUN_EXE; 
#else 
  cerr << "Error: VTK_MPIRUN_EXE must be set when VTK_USE_MPI is on.\n"; 
  return; 
#endif 
  int serverNumProc = 1; 
  int renderNumProc = 1; 
 
# ifdef VTK_MPI_MAX_NUMPROCS 
  serverNumProc = VTK_MPI_MAX_NUMPROCS; 
  renderNumProc = serverNumProc-1; 
  if ( renderNumProc <= 0 ) 
    { 
    renderNumProc = 1; 
    } 
# endif 
# ifdef VTK_MPI_NUMPROC_FLAG 
  this->MPINumProcessFlag = VTK_MPI_NUMPROC_FLAG; 
# else 
  cerr << "Error VTK_MPI_NUMPROC_FLAG must be defined to run test if MPI is on.\n"; 
  return; 
# endif 
# ifdef VTK_MPI_PREFLAGS 
  this->SeparateArguments(VTK_MPI_PREFLAGS, this->MPIPreFlags); 
# endif 
# ifdef VTK_MPI_POSTFLAGS 
  this->SeparateArguments(VTK_MPI_POSTFLAGS, this->MPIPostFlags); 
# endif 
  char buf[1024]; 
  sprintf(buf, "%d", serverNumProc); 
  this->MPIServerNumProcessFlag = buf; 
  sprintf(buf, "%d", renderNumProc); 
  this->MPIRenderServerNumProcessFlag = buf; 
 
#endif // VTK_USE_MPI 
 
# ifdef VTK_MPI_SERVER_PREFLAGS 
  this->SeparateArguments(VTK_MPI_SERVER_PREFLAGS, this->MPIServerPreFlags); 
# endif 
# ifdef VTK_MPI_SERVER_POSTFLAGS 
  this->SeparateArguments(VTK_MPI_SERVER_POSTFLAGS, this->MPIServerPostFlags); 
# endif 
# ifdef VTK_MPI_SERVER_TD_PREFLAGS 
  this->SeparateArguments(VTK_MPI_SERVER_TD_PREFLAGS, this->TDServerPreFlags); 
# endif 
# ifdef VTK_MPI_SERVER_TD_POSTFLAGS 
  this->SeparateArguments(VTK_MPI_SERVER_TD_POSTFLAGS, this->TDServerPostFlags); 
# endif 
 
// For remote testing (via ssh) 
# ifdef PV_SSH_FLAGS 
  this->SeparateArguments(PV_SSH_FLAGS, this->PVSSHFlags); 
# endif //PV_SSH_FLAGS 
 
# ifdef PV_SETUP_SCRIPT 
  this->PVSetupScript = PV_SETUP_SCRIPT; 
# endif //PV_SETUP_SCRIPT 
} 
 
int pvTestDriver::ProcessCommandLine(int argc, char* argv[]) 
{ 
  this->ArgStart = 1; 
  int i; 
  for(i =1; i < argc - 1; ++i) 
    { 
    if(strcmp(argv[i], "--test-render-server") == 0) 
      { 
      this->ArgStart = i+1; 
      this->TestRenderServer = 1; 
      this->TestServer = 1; 
      fprintf(stderr, "Test Render Server.\n"); 
      } 
    if(strcmp(argv[i], "--test-r2d") == 0) 
      { 
      this->ArgStart = i+1; 
      this->TestRenderServer = 2; 
      this->TestServer = 1; 
      fprintf(stderr, "Test Render Server.\n"); 
      } 
    if(strcmp(argv[i], "--test-server") == 0) 
      { 
      this->ArgStart = i+1; 
      this->TestServer = 1; 
      fprintf(stderr, "Test Server.\n"); 
      } 
    if(strcmp(argv[i], "--test-tiled") == 0) 
      { 
      this->ArgStart = i+1; 
      this->TestServer = 1; 
      this->TestTiledDisplay = 1; 
      fprintf(stderr, "Test Tiled Display.\n"); 
      } 
    if(strcmp(argv[i], "--one-mpi-np") == 0) 
      { 
      this->MPIServerNumProcessFlag =  
        this->MPIRenderServerNumProcessFlag = "1"; 
      this->ArgStart = i+1; 
      fprintf(stderr, "Test with one mpi process.\n"); 
      } 
    if(strcmp(argv[i], "--test-rc") == 0) 
      { 
      this->ArgStart = i+1; 
      this->ReverseConnection = 1; 
      fprintf(stderr, "Test reverse connection.\n"); 
      } 
    if(strcmp(argv[i], "--timeout") == 0) 
      { 
      this->ArgStart = i+2; 
      this->TimeOut = atoi(argv[i+1]); 
      fprintf(stderr, "The timeout was set to %f.\n", this->TimeOut); 
      } 
    if (strncmp(argv[i], "--server-exit-timeout", 
        strlen("--server-exit-timeout")) == 0) 
      { 
      this->ArgStart = i+2; 
      this->ServerExitTimeOut = atoi(argv[i+1]); 
      fprintf(stderr, "The server exit timeout was set to %f.\n",  
        this->ServerExitTimeOut); 
      } 
    if(strncmp(argv[i], "--server-preflags",17) == 0) 
      { 
      this->SeparateArguments(argv[i+1], this->MPIServerPreFlags); 
      this->ArgStart = i+2; 
      fprintf(stderr, "Extras server preflags were specified: %s\n", argv[i+1]); 
      } 
    if(strncmp(argv[i], "--server-postflags",18 ) == 0) 
      { 
      this->SeparateArguments(argv[i+1], this->MPIServerPostFlags); 
      this->ArgStart = i+2; 
      fprintf(stderr, "Extras server postflags were specified: %s\n", argv[i+1]); 
      } 
    if (strncmp(argv[i], "--allow-errors", strlen("--allow-errors"))==0) 
      { 
      this->ArgStart =i+1; 
      this->AllowErrorInOutput = 1; 
      fprintf(stderr, "The allow erros in output flag was set to %d.\n",  
        this->AllowErrorInOutput); 
      } 
    } 
 
  return 1; 
} 
 
void 
pvTestDriver::CreateCommandLine(vtksys_stl::vector& commandLine, 
                                const char* paraView, 
                                pvTestDriver::ProcessType type, 
                                const char* numProc, 
                                int argStart, 
                                int argCount, 
                                char* argv[]) 
{ 
  if(this->MPIRun.size() && type != CLIENT) 
    { 
    commandLine.push_back(this->MPIRun.c_str()); 
    commandLine.push_back(this->MPINumProcessFlag.c_str()); 
    commandLine.push_back(numProc); 
    if (!this->TestTiledDisplay) 
      { 
      for(unsigned int i = 0; i < this->MPIPreFlags.size(); ++i) 
        { 
        commandLine.push_back(this->MPIPreFlags[i].c_str()); 
        } 
      // If there is specific flags for the server to pass to mpirun, add them 
      if( type == SERVER ) 
        { 
        for(unsigned int i = 0; i < this->MPIServerPreFlags.size(); ++i) 
          { 
          commandLine.push_back(this->MPIServerPreFlags[i].c_str()); 
          } 
        } 
      } 
    else ///  When tile display is enabled. 
      { 
      // If there is specific flags for the server to pass to mpirun, add them 
      if( type == SERVER) 
        { 
        for(unsigned int i = 0; i < this->TDServerPreFlags.size(); ++i) 
          { 
          commandLine.push_back(this->TDServerPreFlags[i].c_str()); 
          } 
        } 
      } 
    } 
 
  if(this->PVSSHFlags.size() && type == SERVER) 
    { 
      { 
      // First add the ssh command: 
      for(unsigned int i = 0; i < this->PVSSHFlags.size(); ++i) 
        { 
        commandLine.push_back(this->PVSSHFlags[i].c_str()); 
        } 
      // then the paraview intialization: 
      if( this->PVSetupScript.size() ) 
        { 
        commandLine.push_back(this->PVSetupScript.c_str()); 
        } 
      } 
    } 
  else 
    { 
    commandLine.push_back(paraView); 
    if (type == CLIENT) 
      { 
      for (unsigned int i=0; i < this->ClientPostFlags.size(); ++i) 
        { 
        commandLine.push_back(this->ClientPostFlags[i].c_str()); 
        } 
      } 
 
    if(this->ReverseConnection && type != CLIENT) 
      { 
      commandLine.push_back("-rc"); 
      } 
 
    if (!this->TestTiledDisplay) 
      { 
      for(unsigned int i = 0; i < this->MPIPostFlags.size(); ++i) 
        { 
        commandLine.push_back(MPIPostFlags[i].c_str()); 
        } 
      // If there is specific flags for the server to pass to mpirun, add them 
      if( type == SERVER ) 
        { 
        for(unsigned int i = 0; i < this->MPIServerPostFlags.size(); ++i) 
          { 
          commandLine.push_back(this->MPIServerPostFlags[i].c_str()); 
          } 
        } 
      } 
    else 
      { 
      // If there is specific flags for the server to pass to mpirun, add them 
      if( type == SERVER) 
        { 
        for(unsigned int i = 0; i < this->TDServerPostFlags.size(); ++i) 
          { 
          commandLine.push_back(this->TDServerPostFlags[i].c_str()); 
          } 
        } 
      } 
 
    // remaining flags for the test 
    for(int ii = argStart; ii < argCount; ++ii) 
      { 
      commandLine.push_back(argv[ii]); 
      } 
    } 
  commandLine.push_back(0); 
} 
 
int pvTestDriver::StartServer(vtksysProcess* server, const char* name, 
                              vtkstd::vector& out, 
                              vtkstd::vector& err) 
{ 
  if(!server) 
    { 
    return 1; 
    } 
  cerr << "pvTestDriver: starting process " << name << "\n"; 
  vtksysProcess_SetTimeout(server, this->TimeOut); 
  vtksysProcess_Execute(server); 
  int foundWaiting = 0; 
  vtkstd::string output; 
  while(!foundWaiting) 
    { 
    int pipe = this->WaitForAndPrintLine(name, server, output, 100.0, out, err, 
                                         &foundWaiting); 
    if(pipe == vtksysProcess_Pipe_None || 
       pipe == vtksysProcess_Pipe_Timeout) 
      { 
      break; 
      } 
    } 
  if(foundWaiting) 
    { 
    cerr << "pvTestDriver: " << name << " sucessfully started.\n"; 
    return 1; 
    } 
  else 
    { 
    cerr << "pvTestDriver: " << name << " never started.\n"; 
    vtksysProcess_Kill(server); 
    return 0; 
    } 
} 
 
int pvTestDriver::StartClient(vtksysProcess* client, const char* name) 
{ 
  if(!client) 
    { 
    return 1; 
    } 
  cerr << "pvTestDriver: starting process " << name << "\n"; 
  vtksysProcess_SetTimeout(client, this->TimeOut); 
  vtksysProcess_Execute(client); 
  if(vtksysProcess_GetState(client) == vtksysProcess_State_Executing) 
    { 
    cerr << "pvTestDriver: " << name << " sucessfully started.\n"; 
    return 1; 
    } 
  else 
    { 
    this->ReportStatus(client, name); 
    vtksysProcess_Kill(client); 
    return 0; 
    } 
} 
 
void pvTestDriver::Stop(vtksysProcess* p, const char* name) 
{ 
  if(p) 
    { 
    cerr << "pvTestDriver: killing process " << name << "\n"; 
    vtksysProcess_Kill(p); 
    vtksysProcess_WaitForExit(p, 0); 
    } 
} 
 
int pvTestDriver::OutputStringHasError(const char* pname, vtkstd::string& output) 
{ 
  const char* possibleMPIErrors[] = { 
    "error", 
    "Missing:", 
    "core dumped", 
    "process in local group is dead", 
    "Segmentation fault", 
    "erroneous", 
    "ERROR:", 
    "Error:", 
    "mpirun can *only* be used with MPI programs", 
    "due to signal", 
    "failure", 
    "bnormal termination", 
    "failed", 
    "FAILED", 
    "Failed", 
    0 
  }; 
 
  const char* nonErrors[] = { 
    "Memcheck, a memory error detector for x86-linux",  //valgrind 
    "error in locking authority file",  //Ice-T 
    "WARNING: Far depth failed sanity check, resetting.", //Ice-T 
    0 
  }; 
 
  if(this->AllowErrorInOutput) 
    { 
    return 0; 
    } 
 
  vtkstd::vector lines; 
  vtkstd::vector::iterator it; 
  vtksys::SystemTools::Split(output.c_str(), lines); 
 
  int i, j; 
 
  for ( it = lines.begin(); it != lines.end(); ++ it ) 
    { 
    for(i =0; possibleMPIErrors[i]; ++i) 
      { 
      if(it->find(possibleMPIErrors[i]) != it->npos) 
        { 
        int found = 0; 
        for (j = 0; nonErrors[j]; ++ j) 
          { 
          if ( it->find(nonErrors[j]) != it->npos ) 
            { 
            found = 1; 
            } 
          } 
        if ( !found ) 
          { 
          cerr << "pvTestDriver: ***** Test will fail, because the string: \"" 
            << possibleMPIErrors[i] 
            << "\"\npvTestDriver: ***** was found in the following output from the " 
            << pname << ":\n\"" 
            << it->c_str() << "\"\n"; 
          return 1; 
          } 
        } 
      } 
    } 
  return 0; 
} 
 
//---------------------------------------------------------------------------- 
int pvTestDriver::Main(int argc, char* argv[]) 
{ 
  // temporary hack 
  // when pvserver supports -rc option then this can be removed 
  // until then, ReverseConnection must be set before CollectConfiguredOptions 
  // is called. 
  for(int k =0; k < argc; k++) 
    { 
    if(strcmp(argv[k],"--test-rc") == 0) 
      { 
      this->ReverseConnection = 1; 
      } 
    } 
  this->CollectConfiguredOptions(); 
  if(!this->ProcessCommandLine(argc, argv)) 
    { 
    return 1; 
    } 
 
  // mpi code 
  // Allocate process managers. 
  vtksysProcess* renderServer = 0; 
  if(this->TestRenderServer) 
    { 
    renderServer = vtksysProcess_New(); 
    if(!renderServer) 
      { 
      cerr << "pvTestDriver: Cannot allocate vtksysProcess to run the render server.\n"; 
      return 1; 
      } 
    } 
  vtksysProcess* server = 0; 
  if(this->TestServer) 
    { 
    server = vtksysProcess_New(); 
    if(!server) 
      { 
      cerr << "pvTestDriver: Cannot allocate vtksysProcess to run the server.\n"; 
      return 1; 
      } 
    } 
  vtksysProcess* client = vtksysProcess_New(); 
  if(!client) 
    { 
    vtksysProcess_Delete(server); 
    cerr << "pvTestDriver: Cannot allocate vtksysProcess to run the client.\n"; 
    return 1; 
    } 
 
  vtkstd::vector ClientStdOut; 
  vtkstd::vector ClientStdErr; 
  vtkstd::vector ServerStdOut; 
  vtkstd::vector ServerStdErr; 
  vtkstd::vector RenderServerStdOut; 
  vtkstd::vector RenderServerStdErr; 
 
  // Construct the render server process command line 
  vtksys_stl::vector renderServerCommand; 
  if(renderServer) 
    { 
    this->CreateCommandLine(renderServerCommand, 
                            this->ParaViewRenderServer.c_str(), 
                            RENDER_SERVER, 
                            this->MPIRenderServerNumProcessFlag.c_str()); 
    this->ReportCommand(&renderServerCommand[0], "renderserver"); 
    vtksysProcess_SetCommand(renderServer, &renderServerCommand[0]); 
    } 
 
  vtksys_stl::vector serverCommand; 
  if(server) 
    { 
    const char* serverExe = this->ParaViewServer.c_str(); 
    if(this->TestRenderServer) 
      { 
      serverExe = this->ParaViewDataServer.c_str(); 
      } 
 
 
    this->CreateCommandLine(serverCommand, 
                            serverExe, 
                            SERVER, 
                            this->MPIServerNumProcessFlag.c_str()); 
    this->ReportCommand(&serverCommand[0], "server"); 
    vtksysProcess_SetCommand(server, &serverCommand[0]); 
    } 
 
  // Construct the client process command line. 
  vtksys_stl::vector clientCommand; 
   
  if (renderServer) 
    { 
#ifdef CONNECT_TO_RS_DS_SCRIPT 
    vtkstd::string temp = CONNECT_TO_RS_DS_SCRIPT; 
    this->ClientPostFlags.push_back("--run-test-init=" + temp); 
#else 
    cerr << "CONNECT_TO_RS_DS_SCRIPT must be specified." << endl; 
    return 1; 
#endif 
    } 
  else if (server) 
    { 
#ifdef CONNECT_TO_SERVER_SCRIPT 
    vtkstd::string temp = CONNECT_TO_SERVER_SCRIPT; 
    this->ClientPostFlags.push_back("--run-test-init=" + temp); 
#else 
    cerr << "CONNECT_TO_SERVER_SCRIPT must be specified." << endl; 
    return 1; 
#endif 
    } 
  else 
    { 
#ifdef CONNECT_TO_BUILTIN_SCRIPT 
    vtkstd::string temp = CONNECT_TO_BUILTIN_SCRIPT; 
    this->ClientPostFlags.push_back("--run-test-init=" + temp); 
#else 
    cerr << "CONNECT_TO_BUILTIN_SCRIPT must be specified." << endl; 
    return 1; 
#endif 
    } 
  // default to paraview for tests 
  const char* pv = this->ParaView.c_str(); 
  // if server or render server then use ParaViewClient 
  if(server || renderServer) 
    { 
    pv = this->ParaViewClient.c_str(); 
    } 
 
  this->CreateCommandLine(clientCommand, 
                          pv, 
                          CLIENT, 
                          "", 
                          this->ArgStart, argc, argv); 
  this->ReportCommand(&clientCommand[0], "client"); 
  vtksysProcess_SetCommand(client, &clientCommand[0]); 
 
  // Kill the processes if they are taking too long. 
  if(this->ReverseConnection) 
    { 
    if(!this->StartServer(client, "client", 
                          ClientStdOut, ClientStdErr)) 
      { 
      cerr << "pvTestDriver: Reverse connection client never started.\n"; 
      return -1; 
      } 
    // Now run the server 
    if(!this->StartClient(server, "server")) 
      { 
      this->Stop(client, "client"); 
      return -1; 
      } 
    // Now run the render server 
    if(!this->StartClient(renderServer, "renderserver")) 
      { 
      this->Stop(client, "client"); 
      this->Stop(server, "server"); 
      return -1; 
      } 
    } 
  else 
    { 
    // Start the render server if there is one 
    if(!this->StartServer(renderServer, "renderserver", 
                          RenderServerStdOut, RenderServerStdErr)) 
      { 
      cerr << "pvTestDriver: Render server never started.\n"; 
      return -1; 
      } 
    // Start the data server if there is one 
    if(!this->StartServer(server, "server", 
                          ServerStdOut, ServerStdErr)) 
      { 
      this->Stop(renderServer, "renderserver"); 
      cerr << "pvTestDriver: Server never started.\n"; 
      return -1; 
      } 
    // Now run the client 
    if(!this->StartClient(client, "client")) 
      { 
      this->Stop(server, "server"); 
      this->Stop(renderServer, "renderserver"); 
      return -1; 
      } 
    } 
 
  // Report the output of the processes. 
  int clientPipe = 1; 
  int serverPipe = 1; 
  int renderServerPipe = 1; 
  vtkstd::string output; 
  int mpiError = 0; 
  while(clientPipe) 
    { 
    clientPipe = this->WaitForAndPrintLine("client", client, output, 0.1, 
                                           ClientStdOut, ClientStdErr, 0); 
    if(!mpiError && this->OutputStringHasError("client", output)) 
      { 
      mpiError = 1; 
      } 
    // If client has died, we wait for output from the server processess 
    // for this->ServerExitTimeOut, then we'll kill the servers, if needed. 
    double timeout = (clientPipe)? 0.1 : this->ServerExitTimeOut; 
    output = ""; 
    serverPipe = this->WaitForAndPrintLine("server", server, output, timeout, 
                                           ServerStdOut, ServerStdErr, 0); 
    if(!mpiError && this->OutputStringHasError("server", output)) 
      { 
      mpiError = 1; 
      } 
    output = ""; 
    renderServerPipe = 
      this->WaitForAndPrintLine("renderserver", renderServer, output, timeout, 
                                RenderServerStdOut, RenderServerStdErr, 0); 
    if(!mpiError && this->OutputStringHasError("renderserver", output)) 
      { 
      mpiError = 1; 
      } 
    output = ""; 
    } 
 
  // Wait for the client and server to exit. 
  vtksysProcess_WaitForExit(client, 0); 
 
  // Once client is finished, the servers 
  // must finish quickly. If not, is usually is a sign that 
  // the client crashed/exited before it attempted to connect to  
  // the server. 
  if(server) 
    { 
    vtksysProcess_WaitForExit(server, &this->ServerExitTimeOut); 
    } 
  if(renderServer) 
    { 
    vtksysProcess_WaitForExit(renderServer, &this->ServerExitTimeOut); 
    } 
#ifdef PV_TEST_CLEAN_COMMAND 
  // If any executable did not exit properly, run a user-specified 
  // command to cleanup leftover processes.  This is needed for tests 
  // that fail when running in MPI mode. 
  // 
  // For example: "killall -9 rsh paraview" 
  // 
  if(vtksysProcess_GetState(client) != vtksysProcess_State_Exited || 
     vtksysProcess_GetState(server) != vtksysProcess_State_Exited || 
     vtksysProcess_GetState(renderServer) != vtksysProcess_State_Exited) 
    { 
    if(strlen(PV_TEST_CLEAN_COMMAND) > 0) 
      { 
      system(PV_TEST_CLEAN_COMMAND); 
      } 
    } 
#endif 
  // Get the results. 
  int clientResult = this->ReportStatus(client, "client"); 
  int serverResult = 0; 
  if(server) 
    { 
    this->ReportStatus(server, "server"); 
    vtksysProcess_Kill(server); 
    } 
  int renderServerResult = 0; 
  if(renderServer) 
    { 
    renderServerResult = this->ReportStatus(renderServer, "renderserver"); 
    vtksysProcess_Kill(renderServer); 
    } 
 
  // Free process managers. 
  vtksysProcess_Delete(client); 
  if(server) 
    { 
    vtksysProcess_Delete(server); 
    } 
  if(renderServer) 
    { 
    vtksysProcess_Delete(renderServer); 
    } 
  // Report the server return code if it is nonzero.  Otherwise report 
  // the client return code. 
  if(serverResult) 
    { 
    return serverResult; 
    } 
  // if renderServer return code is nonzero then return it 
  if(renderServerResult) 
    { 
    return renderServerResult; 
    } 
  if(mpiError) 
    { 
    cerr << "pvTestDriver: Error string found in ouput, pvTestDriver returning " 
         << mpiError << "\n"; 
    return mpiError; 
    } 
  // if both servers are fine return the client result 
  return clientResult; 
} 
 
//---------------------------------------------------------------------------- 
void pvTestDriver::ReportCommand(const char* const* command, const char* name) 
{ 
  cerr << "pvTestDriver: " << name << " command is:\n"; 
  for(const char* const * c = command; *c; ++c) 
    { 
    cerr << " \"" << *c << "\""; 
    } 
  cerr << "\n"; 
} 
 
//---------------------------------------------------------------------------- 
int pvTestDriver::ReportStatus(vtksysProcess* process, const char* name) 
{ 
  int result = 1; 
  switch(vtksysProcess_GetState(process)) 
    { 
    case vtksysProcess_State_Starting: 
      { 
      cerr << "pvTestDriver: Never started " << name << " process.\n"; 
      } break; 
    case vtksysProcess_State_Error: 
      { 
      cerr << "pvTestDriver: Error executing " << name << " process: " 
           << vtksysProcess_GetErrorString(process) 
           << "\n"; 
      } break; 
    case vtksysProcess_State_Exception: 
      { 
      cerr << "pvTestDriver: " << name 
                      << " process exited with an exception: "; 
      switch(vtksysProcess_GetExitException(process)) 
        { 
        case vtksysProcess_Exception_None: 
          { 
          cerr << "None"; 
          } break; 
        case vtksysProcess_Exception_Fault: 
          { 
          cerr << "Segmentation fault"; 
          } break; 
        case vtksysProcess_Exception_Illegal: 
          { 
          cerr << "Illegal instruction"; 
          } break; 
        case vtksysProcess_Exception_Interrupt: 
          { 
          cerr << "Interrupted by user"; 
          } break; 
        case vtksysProcess_Exception_Numerical: 
          { 
          cerr << "Numerical exception"; 
          } break; 
        case vtksysProcess_Exception_Other: 
          { 
          cerr << "Unknown"; 
          } break; 
        } 
      cerr << "\n"; 
      } break; 
    case vtksysProcess_State_Executing: 
      { 
      cerr << "pvTestDriver: Never terminated " << name << " process.\n"; 
      } break; 
    case vtksysProcess_State_Exited: 
      { 
      result = vtksysProcess_GetExitValue(process); 
      cerr << "pvTestDriver: " << name << " process exited with code " 
                      << result << "\n"; 
      } break; 
    case vtksysProcess_State_Expired: 
      { 
      cerr << "pvTestDriver: killed " << name << " process due to timeout.\n"; 
      } break; 
    case vtksysProcess_State_Killed: 
      { 
      cerr << "pvTestDriver: killed " << name << " process.\n"; 
      } break; 
    } 
  return result; 
} 
 
//---------------------------------------------------------------------------- 
int pvTestDriver::WaitForLine(vtksysProcess* process, vtkstd::string& line, 
                              double timeout, 
                              vtkstd::vector& out, 
                              vtkstd::vector& err) 
{ 
  line = ""; 
  vtkstd::vector::iterator outiter = out.begin(); 
  vtkstd::vector::iterator erriter = err.begin(); 
  while(1) 
    { 
    // Check for a newline in stdout. 
    for(;outiter != out.end(); ++outiter) 
      { 
      if((*outiter == '\r') && ((outiter+1) == out.end())) 
        { 
        break; 
        } 
      else if(*outiter == '\n' || *outiter == '\0') 
        { 
        int length = outiter-out.begin(); 
        if(length > 1 && *(outiter-1) == '\r') 
          { 
          --length; 
          } 
        if(length > 0) 
          { 
          line.append(&out[0], length); 
          } 
        out.erase(out.begin(), outiter+1); 
        return vtksysProcess_Pipe_STDOUT; 
        } 
      } 
 
    // Check for a newline in stderr. 
    for(;erriter != err.end(); ++erriter) 
      { 
      if((*erriter == '\r') && ((erriter+1) == err.end())) 
        { 
        break; 
        } 
      else if(*erriter == '\n' || *erriter == '\0') 
        { 
        int length = erriter-err.begin(); 
        if(length > 1 && *(erriter-1) == '\r') 
          { 
          --length; 
          } 
        if(length > 0) 
          { 
          line.append(&err[0], length); 
          } 
        err.erase(err.begin(), erriter+1); 
        return vtksysProcess_Pipe_STDERR; 
        } 
      } 
 
    // No newlines found.  Wait for more data from the process. 
    int length; 
    char* data; 
    int pipe = vtksysProcess_WaitForData(process, &data, &length, &timeout); 
    if(pipe == vtksysProcess_Pipe_Timeout) 
      { 
      // Timeout has been exceeded. 
      return pipe; 
      } 
    else if(pipe == vtksysProcess_Pipe_STDOUT) 
      { 
      // Append to the stdout buffer. 
      vtkstd::vector::size_type size = out.size(); 
      out.insert(out.end(), data, data+length); 
      outiter = out.begin()+size; 
      } 
    else if(pipe == vtksysProcess_Pipe_STDERR) 
      { 
      // Append to the stderr buffer. 
      vtkstd::vector::size_type size = err.size(); 
      err.insert(err.end(), data, data+length); 
      erriter = err.begin()+size; 
      } 
    else if(pipe == vtksysProcess_Pipe_None) 
      { 
      // Both stdout and stderr pipes have broken.  Return leftover data. 
      if(!out.empty()) 
        { 
        line.append(&out[0], outiter-out.begin()); 
        out.erase(out.begin(), out.end()); 
        return vtksysProcess_Pipe_STDOUT; 
        } 
      else if(!err.empty()) 
        { 
        line.append(&err[0], erriter-err.begin()); 
        err.erase(err.begin(), err.end()); 
        return vtksysProcess_Pipe_STDERR; 
        } 
      else 
        { 
        return vtksysProcess_Pipe_None; 
        } 
      } 
    } 
} 
 
//---------------------------------------------------------------------------- 
void pvTestDriver::PrintLine(const char* pname, const char* line) 
{ 
  // if the name changed then the line is output from a different process 
  if(this->CurrentPrintLineName != pname) 
    { 
    cerr << "-------------- " << pname 
         << " output --------------\n"; 
    // save the current pname 
    this->CurrentPrintLineName = pname; 
    } 
  cerr << line << "\n"; 
  cerr.flush(); 
} 
 
//---------------------------------------------------------------------------- 
int pvTestDriver::WaitForAndPrintLine(const char* pname, vtksysProcess* process, 
                                      vtkstd::string& line, double timeout, 
                                      vtkstd::vector& out, 
                                      vtkstd::vector& err, 
                                      int* foundWaiting) 
{ 
  int pipe = this->WaitForLine(process, line, timeout, out, err); 
  if(pipe == vtksysProcess_Pipe_STDOUT || pipe == vtksysProcess_Pipe_STDERR) 
    { 
    this->PrintLine(pname, line.c_str()); 
    if(foundWaiting && (line.find("Waiting") != line.npos)) 
      { 
      *foundWaiting = 1; 
      } 
    } 
  return pipe; 
}