diff options
author | jhendersonHDF <jhenderson@hdfgroup.org> | 2023-05-02 19:52:39 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-02 19:52:39 (GMT) |
commit | f8a1b3ceec485829ccdd3034ef2be68029f1a66e (patch) | |
tree | 5563ca3059b8cbda43f7f1e0ffedf7985f71a4bc /test/API/driver/h5_api_test_driver.cxx | |
parent | 41fd8e66a9f837a1adf36a0253e29440d82ff522 (diff) | |
download | hdf5-f8a1b3ceec485829ccdd3034ef2be68029f1a66e.zip hdf5-f8a1b3ceec485829ccdd3034ef2be68029f1a66e.tar.gz hdf5-f8a1b3ceec485829ccdd3034ef2be68029f1a66e.tar.bz2 |
Add initial version of HDF5 API tests (#2877)
Diffstat (limited to 'test/API/driver/h5_api_test_driver.cxx')
-rw-r--r-- | test/API/driver/h5_api_test_driver.cxx | 910 |
1 files changed, 910 insertions, 0 deletions
diff --git a/test/API/driver/h5_api_test_driver.cxx b/test/API/driver/h5_api_test_driver.cxx new file mode 100644 index 0000000..b5d9821 --- /dev/null +++ b/test/API/driver/h5_api_test_driver.cxx @@ -0,0 +1,910 @@ +#include "h5_api_test_driver.hxx" + +#include "H5_api_test_config.h" + +#include <cstdio> +#include <sstream> +#include <iostream> +#include <cstring> +#include <cstdlib> + +#if !defined(_WIN32) || defined(__CYGWIN__) +# include <unistd.h> +# include <sys/wait.h> +#endif + +#include <h5_api_test_sys/RegularExpression.hxx> +#include <h5_api_test_sys/SystemTools.hxx> + +using std::vector; +using std::string; +using std::cerr; + +// The main function as this class should only be used by this program +int +main(int argc, char *argv[]) +{ + H5APITestDriver d; + return d.Main(argc, argv); +} + +//---------------------------------------------------------------------------- +H5APITestDriver::H5APITestDriver() +{ + this->ClientArgStart = 0; + this->ClientArgCount = 0; + this->ClientHelperArgStart = 0; + this->ClientHelperArgCount = 0; + this->ClientInitArgStart = 0; + this->ClientInitArgCount = 0; + this->ServerArgStart = 0; + this->ServerArgCount = 0; + this->AllowErrorInOutput = false; + // try to make sure that this times out before dart so it can kill all the processes + this->TimeOut = DART_TESTING_TIMEOUT - 10.0; + this->ServerExitTimeOut = 2; /* 2 seconds timeout for server to exit */ + this->ClientHelper = false; + this->ClientInit = false; + this->TestServer = false; + this->TestSerial = false; + this->IgnoreServerResult = false; +} + +//---------------------------------------------------------------------------- +H5APITestDriver::~H5APITestDriver() +{ +} + +//---------------------------------------------------------------------------- +void +H5APITestDriver::SeparateArguments(const char *str, vector<string> &flags) +{ + string arg = str; + string::size_type pos1 = 0; + 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 +H5APITestDriver::CollectConfiguredOptions() +{ + if (this->TimeOut < 0) + this->TimeOut = 1500; + +#ifdef H5_API_TEST_ENV_VARS + this->SeparateArguments(H5_API_TEST_ENV_VARS, this->ClientEnvVars); +#endif + + // now find all the mpi information if mpi run is set +#ifdef MPIEXEC_EXECUTABLE + this->MPIRun = MPIEXEC_EXECUTABLE; +#else + return; +#endif + int maxNumProc = 1; + +# ifdef MPIEXEC_MAX_NUMPROCS + if (!this->TestSerial) + maxNumProc = MPIEXEC_MAX_NUMPROCS; +# endif +# ifdef MPIEXEC_NUMPROC_FLAG + this->MPINumProcessFlag = MPIEXEC_NUMPROC_FLAG; +# endif +# ifdef MPIEXEC_PREFLAGS + this->SeparateArguments(MPIEXEC_PREFLAGS, this->MPIClientPreFlags); +# endif +# ifdef MPIEXEC_POSTFLAGS + this->SeparateArguments(MPIEXEC_POSTFLAGS, this->MPIClientPostFlags); +# endif +# ifdef MPIEXEC_SERVER_PREFLAGS + this->SeparateArguments(MPIEXEC_SERVER_PREFLAGS, this->MPIServerPreFlags); +#else + this->MPIServerPreFlags = this->MPIClientPreFlags; +# endif +# ifdef MPIEXEC_SERVER_POSTFLAGS + this->SeparateArguments(MPIEXEC_SERVER_POSTFLAGS, this->MPIServerPostFlags); +#else + this->MPIServerPostFlags = this->MPIClientPostFlags; +# endif + std::stringstream ss; + ss << maxNumProc; + this->MPIServerNumProcessFlag = "1"; + this->MPIClientNumProcessFlag = ss.str(); +} + +//---------------------------------------------------------------------------- +/// This adds the debug/build configuration crap for the executable on windows. +static string +FixExecutablePath(const string &path) +{ +#ifdef CMAKE_INTDIR + string parent_dir = + h5_api_test_sys::SystemTools::GetFilenamePath(path.c_str()); + + string filename = + h5_api_test_sys::SystemTools::GetFilenameName(path); + + if (!h5_api_test_sys::SystemTools::StringEndsWith(parent_dir.c_str(), CMAKE_INTDIR)) { + parent_dir += "/" CMAKE_INTDIR; + } + return parent_dir + "/" + filename; +#endif + + return path; +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::ProcessCommandLine(int argc, char *argv[]) +{ + int *ArgCountP = NULL; + int i; + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--client") == 0) { + this->ClientExecutable = ::FixExecutablePath(argv[i + 1]); + ++i; /* Skip executable */ + this->ClientArgStart = i + 1; + this->ClientArgCount = this->ClientArgStart; + ArgCountP = &this->ClientArgCount; + continue; + } + if (strcmp(argv[i], "--client-helper") == 0) { + std::cerr << "Client Helper" << std::endl; + this->ClientHelper = true; + this->ClientHelperExecutable = ::FixExecutablePath(argv[i + 1]); + ++i; /* Skip executable */ + this->ClientHelperArgStart = i + 1; + this->ClientHelperArgCount = this->ClientHelperArgStart; + ArgCountP = &this->ClientHelperArgCount; + continue; + } + if (strcmp(argv[i], "--client-init") == 0) { + std::cerr << "Client Init" << std::endl; + this->ClientInit = true; + this->ClientInitExecutable = ::FixExecutablePath(argv[i + 1]); + ++i; /* Skip executable */ + this->ClientInitArgStart = i + 1; + this->ClientInitArgCount = this->ClientInitArgStart; + ArgCountP = &this->ClientInitArgCount; + continue; + } + if (strcmp(argv[i], "--server") == 0) { + std::cerr << "Test Server" << std::endl; + this->TestServer = true; + this->ServerExecutable = ::FixExecutablePath(argv[i + 1]); + ++i; /* Skip executable */ + this->ServerArgStart = i + 1; + this->ServerArgCount = this->ServerArgStart; + ArgCountP = &this->ServerArgCount; + continue; + } + if (strcmp(argv[i], "--timeout") == 0) { + this->TimeOut = atoi(argv[i + 1]); + std::cerr << "The timeout was set to " << this->TimeOut << std::endl; + ArgCountP = NULL; + continue; + } + if (strncmp(argv[i], "--allow-errors", strlen("--allow-errors")) == 0) { + this->AllowErrorInOutput = true; + std::cerr << "The allow errors in output flag was set to " << + this->AllowErrorInOutput << std::endl; + ArgCountP = NULL; + continue; + } + if (strncmp(argv[i], "--allow-server-errors", strlen("--allow-server-errors")) == 0) { + this->IgnoreServerResult = true; + std::cerr << "The allow server errors in output flag was set to " << + this->IgnoreServerResult << std::endl; + ArgCountP = NULL; + continue; + } + if (strcmp(argv[i], "--serial") == 0) { + this->TestSerial = true; + std::cerr << "This is a serial test" << std::endl; + ArgCountP = NULL; + continue; + } + if (ArgCountP) + (*ArgCountP)++; + } + + return 1; +} + +//---------------------------------------------------------------------------- +void +H5APITestDriver::CreateCommandLine(vector<const char*> &commandLine, + const char *cmd, int isServer, int isHelper, const char *numProc, int argStart, + int argCount, char *argv[]) +{ + if (!isServer && this->ClientEnvVars.size()) { + for (unsigned int i = 0; i < this->ClientEnvVars.size(); ++i) + commandLine.push_back(this->ClientEnvVars[i].c_str()); +#ifdef H5_API_TEST_CLIENT_INIT_TOKEN_VAR + if (this->ClientTokenVar.size()) + commandLine.push_back(this->ClientTokenVar.c_str()); +#endif + } + + if (!isHelper && this->MPIRun.size()) { + commandLine.push_back(this->MPIRun.c_str()); + commandLine.push_back(this->MPINumProcessFlag.c_str()); + commandLine.push_back(numProc); + + if (isServer) + for (unsigned int i = 0; i < this->MPIServerPreFlags.size(); ++i) + commandLine.push_back(this->MPIServerPreFlags[i].c_str()); + else + for (unsigned int i = 0; i < this->MPIClientPreFlags.size(); ++i) + commandLine.push_back(this->MPIClientPreFlags[i].c_str()); + } + + commandLine.push_back(cmd); + + if (isServer) + for (unsigned int i = 0; i < this->MPIServerPostFlags.size(); ++i) + commandLine.push_back(MPIServerPostFlags[i].c_str()); + else + for (unsigned int i = 0; i < this->MPIClientPostFlags.size(); ++i) + commandLine.push_back(MPIClientPostFlags[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 +H5APITestDriver::StartServer(h5_api_test_sysProcess *server, const char *name, + vector<char> &out, vector<char> &err) +{ + if (!server) + return 1; + + cerr << "H5APITestDriver: starting process " << name << "\n"; + h5_api_test_sysProcess_SetTimeout(server, this->TimeOut); + h5_api_test_sysProcess_Execute(server); + int foundWaiting = 0; + string output; + while (!foundWaiting) { + int pipe = this->WaitForAndPrintLine(name, server, output, 100.0, out, + err, H5_API_TEST_SERVER_START_MSG, &foundWaiting); + if (pipe == h5_api_test_sysProcess_Pipe_None + || pipe == h5_api_test_sysProcess_Pipe_Timeout) { + break; + } + } + if (foundWaiting) { + cerr << "H5APITestDriver: " << name << " successfully started.\n"; + return 1; + } else { + cerr << "H5APITestDriver: " << name << " never started.\n"; + h5_api_test_sysProcess_Kill(server); + return 0; + } +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::StartClientHelper(h5_api_test_sysProcess *client, + const char *name, vector<char> &out, vector<char> &err) +{ + if (!client) + return 1; + + cerr << "H5APITestDriver: starting process " << name << "\n"; + h5_api_test_sysProcess_SetTimeout(client, this->TimeOut); + h5_api_test_sysProcess_Execute(client); + int foundWaiting = 0; + string output; + while (!foundWaiting) { + int pipe = this->WaitForAndPrintLine(name, client, output, 100.0, out, + err, H5_API_TEST_CLIENT_HELPER_START_MSG, &foundWaiting); + if (pipe == h5_api_test_sysProcess_Pipe_None + || pipe == h5_api_test_sysProcess_Pipe_Timeout) { + break; + } + } + if (foundWaiting) { + cerr << "H5APITestDriver: " << name << " successfully started.\n"; + return 1; + } else { + cerr << "H5APITestDriver: " << name << " never started.\n"; + h5_api_test_sysProcess_Kill(client); + return 0; + } +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::StartClientInit(h5_api_test_sysProcess *client, + const char *name, vector<char> &out, vector<char> &err) +{ + if (!client) + return 1; + + cerr << "H5APITestDriver: starting process " << name << "\n"; + h5_api_test_sysProcess_SetTimeout(client, this->TimeOut); + h5_api_test_sysProcess_Execute(client); + int foundToken = 0; + string output, token; + while (!foundToken) { + int pipe = this->WaitForAndPrintLine(name, client, output, 100.0, out, + err, NULL, NULL); + if (pipe == h5_api_test_sysProcess_Pipe_None + || pipe == h5_api_test_sysProcess_Pipe_Timeout) { + break; + } + if (this->OutputStringHasToken(name, H5_API_TEST_CLIENT_INIT_TOKEN_REGEX, output, token)) { + foundToken = 1; + this->ClientTokenVar = std::string(H5_API_TEST_CLIENT_INIT_TOKEN_VAR) + + std::string("=") + std::string(token); + break; + } + } + + if (foundToken) { + cerr << "H5APITestDriver: " << name << " token: " << token << " was found.\n"; + return 1; + } else { + cerr << "H5APITestDriver: " << name << " token was not found.\n"; + return 0; + } +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::StartClient(h5_api_test_sysProcess *client, const char *name) +{ + if (!client) + return 1; + + cerr << "H5APITestDriver: starting process " << name << "\n"; + h5_api_test_sysProcess_SetTimeout(client, this->TimeOut); + h5_api_test_sysProcess_Execute(client); + if (h5_api_test_sysProcess_GetState(client) + == h5_api_test_sysProcess_State_Executing) { + cerr << "H5APITestDriver: " << name << " successfully started.\n"; + return 1; + } else { + this->ReportStatus(client, name); + h5_api_test_sysProcess_Kill(client); + return 0; + } +} + +//---------------------------------------------------------------------------- +void +H5APITestDriver::Stop(h5_api_test_sysProcess *p, const char *name) +{ + if (p) { + cerr << "H5APITestDriver: killing process " << name << "\n"; + h5_api_test_sysProcess_Kill(p); + h5_api_test_sysProcess_WaitForExit(p, 0); + } +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::OutputStringHasError(const char *pname, string &output) +{ + const char* possibleMPIErrors[] = {"error", "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", "abnormal termination", "failed", "FAILED", "Failed", 0}; + + const char* nonErrors[] = { + "Memcheck, a memory error detector", //valgrind + 0}; + + if (this->AllowErrorInOutput) + return 0; + + vector<string> lines; + vector<string>::iterator it; + h5_api_test_sys::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 = 1; + for (j = 0; nonErrors[j]; ++j) { + if (it->find(nonErrors[j]) != it->npos) { + found = 0; + cerr << "Non error \"" << it->c_str() + << "\" suppressed " << std::endl; + } + } + if (found) { + cerr + << "H5APITestDriver: ***** Test will fail, because the string: \"" + << possibleMPIErrors[i] + << "\"\nH5APITestDriver: ***** was found in the following output from the " + << pname << ":\n\"" << it->c_str() << "\"\n"; + return 1; + } + } + } + } + return 0; +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::OutputStringHasToken(const char *pname, const char *regex, + string &output, string &token) +{ + vector<string> lines; + vector<string>::iterator it; + h5_api_test_sys::SystemTools::Split(output.c_str(), lines); + h5_api_test_sys::RegularExpression re(regex); + + for (it = lines.begin(); it != lines.end(); ++it) { + if (re.find(*it)) { + token = re.match(1); + return 1; + } + } + + return 0; +} + +//---------------------------------------------------------------------------- +#define H5_API_CLEAN_PROCESSES do { \ + h5_api_test_sysProcess_Delete(client); \ + h5_api_test_sysProcess_Delete(client_helper); \ + h5_api_test_sysProcess_Delete(client_init); \ + h5_api_test_sysProcess_Delete(server); \ +} while (0) + +#define H5_API_EXECUTE_CMD(cmd) do { \ + if (strlen(cmd) > 0) { \ + std::vector<std::string> commands = \ + h5_api_test_sys::SystemTools::SplitString(cmd, ';'); \ + for (unsigned int cc = 0; cc < commands.size(); cc++) { \ + std::string command = commands[cc]; \ + if (command.size() > 0) { \ + std::cout << command.c_str() << std::endl; \ + system(command.c_str()); \ + } \ + } \ + } \ +} while (0) + +//---------------------------------------------------------------------------- +int +H5APITestDriver::Main(int argc, char* argv[]) +{ +#ifdef H5_API_TEST_INIT_COMMAND + // run user-specified commands before initialization. + // For example: "killall -9 rsh test;" + H5_API_EXECUTE_CMD(H5_API_TEST_INIT_COMMAND); +#endif + + if (!this->ProcessCommandLine(argc, argv)) + return 1; + this->CollectConfiguredOptions(); + + // mpi code + // Allocate process managers. + h5_api_test_sysProcess *server = 0; + h5_api_test_sysProcess *client = 0; + h5_api_test_sysProcess *client_helper = 0; + h5_api_test_sysProcess *client_init = 0; + + if (this->TestServer) { + server = h5_api_test_sysProcess_New(); + if (!server) { + H5_API_CLEAN_PROCESSES; + cerr << "H5APITestDriver: Cannot allocate h5_api_test_sysProcess to " + "run the server.\n"; + return 1; + } + } + if (this->ClientHelper) { + client_helper = h5_api_test_sysProcess_New(); + if (!client_helper) { + H5API_CLEAN_PROCESSES; + cerr << "H5APITestDriver: Cannot allocate h5_api_test_sysProcess to " + "run the client helper.\n"; + return 1; + } + } + if (this->ClientInit) { + client_init = h5_api_test_sysProcess_New(); + if (!client_init) { + H5_API_CLEAN_PROCESSES; + cerr << "H5APITestDriver: Cannot allocate h5_api_test_sysProcess to " + "run the client init.\n"; + return 1; + } + } + client = h5_api_test_sysProcess_New(); + if (!client) { + H5_API_CLEAN_PROCESSES; + cerr << "H5APITestDriver: Cannot allocate h5_api_test_sysProcess to " + "run the client.\n"; + return 1; + } + + vector<char> ClientStdOut; + vector<char> ClientStdErr; + vector<char> ClientHelperStdOut; + vector<char> ClientHelperStdErr; + vector<char> ClientInitStdOut; + vector<char> ClientInitStdErr; + vector<char> ServerStdOut; + vector<char> ServerStdErr; + + vector<const char *> serverCommand; + if (server) { + const char* serverExe = this->ServerExecutable.c_str(); + + this->CreateCommandLine(serverCommand, serverExe, 1, 0, + this->MPIServerNumProcessFlag.c_str(), this->ServerArgStart, + this->ServerArgCount, argv); + this->ReportCommand(&serverCommand[0], "server"); + h5_api_test_sysProcess_SetCommand(server, &serverCommand[0]); + h5_api_test_sysProcess_SetWorkingDirectory(server, + this->GetDirectory(serverExe).c_str()); + } + + vector<const char *> clientHelperCommand; + if (client_helper) { + // Construct the client helper process command line. + const char *clientHelperExe = this->ClientHelperExecutable.c_str(); + this->CreateCommandLine(clientHelperCommand, clientHelperExe, 0, 1, + "1", this->ClientHelperArgStart, + this->ClientHelperArgCount, argv); + this->ReportCommand(&clientHelperCommand[0], "client_helper"); + h5_api_test_sysProcess_SetCommand(client_helper, &clientHelperCommand[0]); + h5_api_test_sysProcess_SetWorkingDirectory(client_helper, + this->GetDirectory(clientHelperExe).c_str()); + } + + vector<const char *> clientInitCommand; + if (client_init) { + // Construct the client helper process command line. + const char *clientInitExe = this->ClientInitExecutable.c_str(); + this->CreateCommandLine(clientInitCommand, clientInitExe, 0, 1, + "1", this->ClientInitArgStart, this->ClientInitArgCount, argv); + this->ReportCommand(&clientInitCommand[0], "client_init"); + h5_api_test_sysProcess_SetCommand(client_init, &clientInitCommand[0]); + h5_api_test_sysProcess_SetWorkingDirectory(client_init, + this->GetDirectory(clientInitExe).c_str()); + } + + // Start the server if there is one + if (!this->StartServer(server, "server", ServerStdOut, ServerStdErr)) { + cerr << "H5APITestDriver: Server never started.\n"; + H5_API_CLEAN_PROCESSES; + return -1; + } + + // Start the client helper here if there is one + if (!this->StartClientHelper(client_helper, "client_helper", + ClientHelperStdOut, ClientHelperStdErr)) { + cerr << "H5APITestDriver: Client Helper never started.\n"; + this->Stop(server, "server"); +#ifdef H5_API_TEST_SERVER_EXIT_COMMAND + H5_API_EXECUTE_CMD(H5_API_TEST_SERVER_EXIT_COMMAND); +#endif + H5_API_CLEAN_PROCESSES; + return -1; + } + + // Start the client init here if there is one + if (!this->StartClientInit(client_init, "client_init", + ClientInitStdOut, ClientInitStdErr)) { + cerr << "H5APITestDriver: Client Init never started.\n"; + this->Stop(server, "server"); +#ifdef H5_API_TEST_SERVER_EXIT_COMMAND + H5_API_EXECUTE_CMD(H5_API_TEST_SERVER_EXIT_COMMAND); +#endif + this->Stop(client_helper, "client_helper"); +#ifdef H5_API_TEST_CLIENT_HELPER_EXIT_COMMAND + H5_API_EXECUTE_CMD(H5_API_TEST_CLIENT_HELPER_EXIT_COMMAND); +#endif + H5_API_CLEAN_PROCESSES; + return -1; + } + + // Construct the client process command line. + vector<const char *> clientCommand; + const char *clientExe = this->ClientExecutable.c_str(); + this->CreateCommandLine(clientCommand, clientExe, 0, 0, + this->MPIClientNumProcessFlag.c_str(), this->ClientArgStart, + this->ClientArgCount, argv); + this->ReportCommand(&clientCommand[0], "client"); + h5_api_test_sysProcess_SetCommand(client, &clientCommand[0]); + h5_api_test_sysProcess_SetWorkingDirectory(client, + this->GetDirectory(clientExe).c_str()); + + // Now run the client + if (!this->StartClient(client, "client")) { + this->Stop(server, "server"); + this->Stop(client_helper, "client_helper"); + this->Stop(client_init, "client_init"); + H5_API_CLEAN_PROCESSES; + return -1; + } + + // Report the output of the processes. + int clientPipe = 1; + + string output; + int mpiError = 0; + while (clientPipe) { + clientPipe = this->WaitForAndPrintLine("client", client, output, 0.1, + ClientStdOut, ClientStdErr, NULL, NULL); + if (!mpiError && this->OutputStringHasError("client", output)) { + mpiError = 1; + } + // If client has died, we wait for output from the server processes + // for this->ServerExitTimeOut, then we'll kill the servers, if needed. + double timeout = (clientPipe) ? 0 : this->ServerExitTimeOut; + output = ""; + this->WaitForAndPrintLine("server", server, output, timeout, + ServerStdOut, ServerStdErr, NULL, NULL); + if (!mpiError && this->OutputStringHasError("server", output)) { + mpiError = 1; + } + output = ""; + } + + // Wait for the client and server to exit. + h5_api_test_sysProcess_WaitForExit(client, 0); + + // Once client is finished, the servers + // must finish quickly. If not, it usually is a sign that + // the client crashed/exited before it attempted to connect to + // the server. + if (server) { +#ifdef H5_API_TEST_SERVER_EXIT_COMMAND + H5_API_EXECUTE_CMD(H5_API_TEST_SERVER_EXIT_COMMAND); +#endif + h5_api_test_sysProcess_WaitForExit(server, &this->ServerExitTimeOut); + } + + if (client_helper) { +#ifdef H5_API_TEST_CLIENT_HELPER_EXIT_COMMAND + H5_API_EXECUTE_CMD(H5_API_TEST_CLIENT_HELPER_EXIT_COMMAND); +#endif + h5_api_test_sysProcess_WaitForExit(client_helper, 0); + } + + // Get the results. + int clientResult = this->ReportStatus(client, "client"); + int serverResult = 0; + if (server) { + serverResult = this->ReportStatus(server, "server"); + h5_api_test_sysProcess_Kill(server); + } + + // Free process managers. + H5_API_CLEAN_PROCESSES; + + // Report the server return code if it is nonzero. Otherwise report + // the client return code. + if (serverResult && !this->IgnoreServerResult) + return serverResult; + + if (mpiError) { + cerr + << "H5VLTestDriver: Error string found in output, H5APITestDriver returning " + << mpiError << "\n"; + return mpiError; + } + + // if server is fine return the client result + return clientResult; +} + +//---------------------------------------------------------------------------- +void +H5APITestDriver::ReportCommand(const char * const *command, const char *name) +{ + cerr << "H5APITestDriver: " << name << " command is:\n"; + for (const char * const *c = command; *c; ++c) + cerr << " \"" << *c << "\""; + cerr << "\n"; +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::ReportStatus(h5_api_test_sysProcess *process, const char *name) +{ + int result = 1; + switch (h5_api_test_sysProcess_GetState(process)) { + case h5_api_test_sysProcess_State_Starting: { + cerr << "H5APITestDriver: Never started " << name << " process.\n"; + } + break; + case h5_api_test_sysProcess_State_Error: { + cerr << "H5APITestDriver: Error executing " << name << " process: " + << h5_api_test_sysProcess_GetErrorString(process) << "\n"; + } + break; + case h5_api_test_sysProcess_State_Exception: { + cerr << "H5APITestDriver: " << name + << " process exited with an exception: "; + switch (h5_api_test_sysProcess_GetExitException(process)) { + case h5_api_test_sysProcess_Exception_None: { + cerr << "None"; + } + break; + case h5_api_test_sysProcess_Exception_Fault: { + cerr << "Segmentation fault"; + } + break; + case h5_api_test_sysProcess_Exception_Illegal: { + cerr << "Illegal instruction"; + } + break; + case h5_api_test_sysProcess_Exception_Interrupt: { + cerr << "Interrupted by user"; + } + break; + case h5_api_test_sysProcess_Exception_Numerical: { + cerr << "Numerical exception"; + } + break; + case h5_api_test_sysProcess_Exception_Other: { + cerr << "Unknown"; + } + break; + } + cerr << "\n"; + } + break; + case h5_api_test_sysProcess_State_Executing: { + cerr << "H5APITestDriver: Never terminated " << name + << " process.\n"; + } + break; + case h5_api_test_sysProcess_State_Exited: { + result = h5_api_test_sysProcess_GetExitValue(process); + cerr << "H5APITestDriver: " << name << " process exited with code " + << result << "\n"; + } + break; + case h5_api_test_sysProcess_State_Expired: { + cerr << "H5APITestDriver: killed " << name + << " process due to timeout.\n"; + } + break; + case h5_api_test_sysProcess_State_Killed: { + cerr << "H5APITestDriver: killed " << name << " process.\n"; + } + break; + } + return result; +} + +//---------------------------------------------------------------------------- +int +H5APITestDriver::WaitForLine(h5_api_test_sysProcess *process, string &line, + double timeout, vector<char> &out, vector<char> &err) +{ + line = ""; + vector<char>::iterator outiter = out.begin(); + vector<char>::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 h5_api_test_sysProcess_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 h5_api_test_sysProcess_Pipe_STDERR; + } + } + + // No newlines found. Wait for more data from the process. + int length; + char *data; + int pipe = h5_api_test_sysProcess_WaitForData(process, &data, &length, + &timeout); + if (pipe == h5_api_test_sysProcess_Pipe_Timeout) { + // Timeout has been exceeded. + return pipe; + } else if (pipe == h5_api_test_sysProcess_Pipe_STDOUT) { + // Append to the stdout buffer. + vector<char>::size_type size = out.size(); + out.insert(out.end(), data, data + length); + outiter = out.begin() + size; + } else if (pipe == h5_api_test_sysProcess_Pipe_STDERR) { + // Append to the stderr buffer. + vector<char>::size_type size = err.size(); + err.insert(err.end(), data, data + length); + erriter = err.begin() + size; + } else if (pipe == h5_api_test_sysProcess_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 h5_api_test_sysProcess_Pipe_STDOUT; + } else if (!err.empty()) { + line.append(&err[0], erriter - err.begin()); + err.erase(err.begin(), err.end()); + return h5_api_test_sysProcess_Pipe_STDERR; + } else { + return h5_api_test_sysProcess_Pipe_None; + } + } + } +} + +//---------------------------------------------------------------------------- +void +H5APITestDriver::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 +H5APITestDriver::WaitForAndPrintLine(const char *pname, + h5_api_test_sysProcess *process, string &line, double timeout, + vector<char> &out, vector<char> &err, const char *waitMsg, + int *foundWaiting) +{ + int pipe = this->WaitForLine(process, line, timeout, out, err); + if (pipe == h5_api_test_sysProcess_Pipe_STDOUT + || pipe == h5_api_test_sysProcess_Pipe_STDERR) { + this->PrintLine(pname, line.c_str()); + if (foundWaiting && (line.find(waitMsg) != line.npos)) + *foundWaiting = 1; + } + return pipe; +} + +//---------------------------------------------------------------------------- +string +H5APITestDriver::GetDirectory(string location) +{ + return h5_api_test_sys::SystemTools::GetParentDirectory(location.c_str()); +} |