60#include <boost/program_options.hpp>
61#include <boost/thread.hpp>
63#cmakedefine NEKTAR_TEST_FORCEMPIEXEC 1
69namespace po = boost::program_options;
73 boost::filesystem::path temp = path;
74 temp.make_preferred();
78int main(
int argc,
char *argv[])
84 po::options_description desc(
"Available options");
86 (
"help,h",
"Produce this help message.")
87 (
"verbose,v",
"Turn on verbosity.")
88 (
"generate-metric,g", po::value<vector<int>>(),
89 "Generate a single metric.")
90 (
"generate-all-metrics,a",
"Generate all metrics.")
91 (
"executable,e", po::value<string>(),
92 "Use specified executable.");
94 po::options_description hidden(
"Hidden options");
95 hidden.add_options()(
"input-file", po::value<string>(),
"Input filename");
97 po::options_description cmdline_options(
"Command-line options");
98 cmdline_options.add(hidden).add(desc);
100 po::options_description visible(
"Allowed options");
103 po::positional_options_description
p;
104 p.add(
"input-file", -1);
106 po::variables_map vm;
110 po::store(po::command_line_parser(argc, argv)
111 .options(cmdline_options)
117 catch (
const exception &e)
119 cerr << e.what() << endl;
124 if (vm.count(
"help") || vm.count(
"input-file") != 1)
126 cerr <<
"Usage: Tester [options] input-file.tst" << endl;
131 bool verbose = vm.count(
"verbose");
134 vector<int> metricGenVec;
135 if (vm.count(
"generate-metric"))
137 metricGenVec = vm[
"generate-metric"].as<vector<int>>();
139 set<int> metricGen(metricGenVec.begin(), metricGenVec.end());
142 const fs::path specFile(vm[
"input-file"].as<string>());
145 fs::path specPath = specFile.parent_path();
147 if (specPath.empty())
149 specPath = fs::current_path();
152 string specFileStem = specFile.stem().string();
156 const fs::path masterDir =
157 fs::current_path() / fs::path(
"tmp_" + specFileStem +
"_" +
158 fs::unique_path(
"%%%%%%").
string());
161 const fs::path startDir = fs::current_path();
167 cerr <<
"Reading test file definition: " << specFile << endl;
175 cerr <<
"Creating metrics:" << endl;
179 vector<MetricSharedPtr> metrics;
182 set<int>::iterator it = metricGen.find(file.
GetMetricId(i));
184 it != metricGen.end() || (vm.count(
"generate-all-metrics") > 0);
191 cerr <<
" - ID " << metrics.back()->GetID() <<
": "
192 << metrics.back()->GetType() << endl;
195 if (it != metricGen.end())
201 if (metricGen.size() != 0)
203 string s = metricGen.size() == 1 ?
"s" :
"";
204 set<int>::iterator it;
205 cerr <<
"Unable to find metric" + s +
" with ID" + s +
" ";
206 for (it = metricGen.begin(); it != metricGen.end(); ++it)
215 if (fs::exists(masterDir))
217 fs::remove_all(masterDir);
222 cerr <<
"Creating master directory: " << masterDir << endl;
226 fs::create_directory(masterDir);
229 fs::current_path(masterDir);
233 fstream masterOut(
"master.out", ios::out | ios::in | ios::trunc);
234 fstream masterErr(
"master.err", ios::out | ios::in | ios::trunc);
236 if (masterOut.bad() || masterErr.bad())
238 cerr <<
"One or more master output files are unreadable." << endl;
243 vector<fs::path> tmpWorkingDirs;
246 for (
unsigned int i = 0; i < file.
GetNumRuns(); ++i)
252 cerr <<
"Starting run " << i <<
"." << endl;
256 const fs::path tmpDir =
257 masterDir / fs::path(
"run" + std::to_string(i));
258 tmpWorkingDirs.push_back(tmpDir);
262 cerr <<
"Creating working directory: " << tmpDir << endl;
266 fs::create_directory(tmpDir);
269 fs::current_path(tmpDir);
273 cerr <<
"Copying required files: " << endl;
282 fs::path source = specPath / source_file;
283 fs::path dest = tmpDir / source_file.filename();
286 cerr <<
" - " << source <<
" -> " << dest << endl;
289 if (fs::is_directory(source))
291 fs::copy_directory(source, dest);
294 for (
const auto &dirEnt :
295 fs::recursive_directory_iterator{source})
297 fs::path newdest = dest / dirEnt.path().filename();
298 fs::copy_file(dirEnt.path(), newdest);
303 fs::copy_file(source, dest);
308 fs::path source_file(
"test.opt");
309 fs::path source = specPath / source_file;
310 bool HaveOptFile =
false;
311 if (fs::exists(source))
313 fs::path dest = tmpDir / source_file.filename();
316 cerr <<
" - " << source <<
" -> " << dest << endl;
319 if (fs::is_directory(source))
321 fs::copy_directory(source, dest);
324 for (
const auto &dirEnt :
325 fs::recursive_directory_iterator{source})
327 fs::path newdest = dest / dirEnt.path().filename();
328 fs::copy_file(dirEnt.path(), newdest);
333 fs::copy_file(source, dest);
344 bool pythonAdded =
false, mpiAdded =
false;
351 command =
"PYTHONPATH=\"@CMAKE_BINARY_DIR@\" " + command;
355#ifdef NEKTAR_TEST_FORCEMPIEXEC
365 command +=
"\"@MPIEXEC@\" ";
366 if (std::string(
"@NEKTAR_TEST_USE_HOSTFILE@") ==
"ON")
368 command +=
"-hostfile hostfile ";
369 if (system(
"echo 'localhost slots=12' > hostfile"))
371 cerr <<
"Unable to write 'hostfile' in path '"
372 << fs::current_path() << endl;
379 command +=
"--tag-output ";
401 command +=
"@MPIEXEC_NUMPROC_FLAG@ ";
407 if (!fs::exists(execPath))
417 command +=
"@PYTHON_EXECUTABLE@ ";
423 command +=
" --useoptfile test.opt ";
428 command +=
" 1>output.out 2>output.err";
435 cerr <<
"Running command: " << command << endl;
439 if (system(command.c_str()))
441 cerr <<
"Error occurred running test:" << endl;
442 cerr <<
"Command: " << command << endl;
447 if (!(fs::exists(
"output.out") && fs::exists(
"output.err")))
449 cerr <<
"One or more test output files are missing." << endl;
454 ifstream vStdout(
"output.out");
455 ifstream vStderr(
"output.err");
456 if (vStdout.bad() || vStderr.bad())
458 cerr <<
"One or more test output files are unreadable." << endl;
465 cerr <<
"Appending run " << i <<
" output and error to master."
469 while (getline(vStdout, line))
471 masterOut << line << endl;
474 while (getline(vStderr, line))
476 masterErr << line << endl;
484 for (
int i = 0; i < metrics.size(); ++i)
486 if (!metrics[i]->SupportsAverage() && file.
GetNumRuns() > 1)
488 cerr <<
"WARNING: Metric " << metrics[i]->GetType()
489 <<
" does not support multiple runs. Test may yield "
490 "unexpected results."
498 if (verbose && metrics.size())
500 cerr <<
"Checking metrics:" << endl;
503 for (
int i = 0; i < metrics.size(); ++i)
506 metricGen.find(metrics[i]->GetID()) != metricGen.end() ||
507 (vm.count(
"generate-all-metrics") > 0);
511 masterOut.seekg(0, ios::beg);
512 masterErr.seekg(0, ios::beg);
516 cerr <<
" - " << (gen ?
"generating" :
"checking")
517 <<
" metric " << metrics[i]->GetID() <<
" ("
518 << metrics[i]->GetType() <<
")... ";
521 if (!metrics[i]->Test(masterOut, masterErr))
526 cerr <<
"failed!" << endl;
531 cerr <<
"passed" << endl;
538 cerr << endl << endl;
542 if (status == 1 || verbose)
546 masterOut.seekg(0, ios::beg);
547 masterErr.seekg(0, ios::beg);
549 cout <<
"=== Output ===" << endl;
550 while (masterOut.good())
552 getline(masterOut, line);
553 cout << line << endl;
555 cout <<
"=== Errors ===" << endl;
556 while (masterErr.good())
558 getline(masterErr, line);
559 cout << line << endl;
568 fs::current_path(startDir);
572 cerr <<
"Removing working directory" << endl;
585 fs::remove_all(masterDir);
588 catch (
const fs::filesystem_error &e)
591 boost::this_thread::sleep(boost::posix_time::milliseconds(1));
595 cout <<
"Locked files encountered. "
596 <<
"Retrying after 1ms..." << endl;
608 if (vm.count(
"generate-metric") > 0 ||
609 vm.count(
"generate-all-metrics") > 0)
617 catch (
const fs::filesystem_error &e)
619 cerr <<
"Filesystem operation error occurred:" << endl;
620 cerr <<
" " << e.what() << endl;
621 cerr <<
" Files left in " << masterDir.string() << endl;
625 cerr <<
"Error occurred during test:" << endl;
626 cerr <<
" " << e.what() << endl;
627 cerr <<
" Files left in " << masterDir.string() << endl;
629 catch (
const std::exception &e)
631 cerr <<
"Unhandled exception during test:" << endl;
632 cerr <<
" " << e.what() << endl;
633 cerr <<
" Files left in " << masterDir.string() << endl;
637 cerr <<
"Unknown error during test" << endl;
638 cerr <<
" Files left in " << masterDir.string() << endl;
#define ASSERTL0(condition, msg)
int main(int argc, char *argv[])
std::string PortablePath(const boost::filesystem::path &path)
MetricSharedPtr CreateInstance(std::string key, TiXmlElement *elmt, bool generate)
The TestData class is responsible for parsing a test XML file and storing the data.
DependentFile GetDependentFile(unsigned int pId) const
unsigned int GetMetricId(unsigned int pId)
Returns the ID of the metric for a given metric ID.
unsigned int GetNumDependentFiles() const
Returns the number of dependent files required for the test.
unsigned int GetNumMetrics() const
Returns the number of metrics to be collected for the test.
unsigned int GetNumRuns() const
Returns the number of runs to be performed for the test.
TiXmlElement * GetMetric(unsigned int pId)
Returns a pointer to the TiXmlElement object representing the metric for a given metric ID.
unsigned int GetNumCommands() const
std::string GetMetricType(unsigned int pId) const
Returns the type of metric to be collected for a given metric ID.
const Command & GetCommand(unsigned int pId) const
The above copyright notice and this permission notice shall be included.
MetricFactory & GetMetricFactory()
Subclass of std::runtime_error to handle exceptions raised by Tester.