94 set<int> &metricGen,
const fs::path &specPath,
95 const fs::path &masterDir,
const fs::path &startDir)
100 if (verbose && test->GetNumMetrics() > 0)
102 cerr <<
"Creating metrics:" << endl;
106 vector<MetricSharedPtr> metrics;
107 for (
unsigned int i = 0; i < test->GetNumMetrics(); ++i)
109 set<int>::iterator it = metricGen.find(test->GetMetricId(i));
111 it != metricGen.end() || (vm.count(
"generate-all-metrics") > 0);
114 test->GetMetricType(i), test->GetMetric(i), genMetric));
118 cerr <<
" - ID " << metrics.back()->GetID() <<
": "
119 << metrics.back()->GetType() << endl;
122 if (it != metricGen.end())
128 if (metricGen.size() != 0)
130 string s = metricGen.size() == 1 ?
"s" :
"";
131 set<int>::iterator it;
132 cerr <<
"Unable to find metric" + s +
" with ID" + s +
" ";
133 for (it = metricGen.begin(); it != metricGen.end(); ++it)
142 if (fs::exists(masterDir))
144 fs::remove_all(masterDir);
149 cerr <<
"Creating master directory: " << masterDir << endl;
153 fs::create_directory(masterDir);
156 fs::current_path(masterDir);
160 fstream masterOut(
"master.out", ios::out | ios::in | ios::trunc);
161 fstream masterErr(
"master.err", ios::out | ios::in | ios::trunc);
163 if (masterOut.bad() || masterErr.bad())
165 cerr <<
"One or more master output files are unreadable." << endl;
170 vector<fs::path> tmpWorkingDirs;
173 for (
unsigned int i = 0; i < test->GetNumRuns(); ++i)
179 cerr <<
"Starting run " << i <<
"." << endl;
183 const fs::path tmpDir = masterDir / fs::path(
"run" + std::to_string(i));
184 tmpWorkingDirs.push_back(tmpDir);
188 cerr <<
"Creating working directory: " << tmpDir << endl;
192 fs::create_directory(tmpDir);
195 fs::current_path(tmpDir);
197 if (verbose && test->GetNumDependentFiles())
199 cerr <<
"Copying required files: " << endl;
204 for (
unsigned int j = 0; j < test->GetNumDependentFiles(); ++j)
206 fs::path source_file(test->GetDependentFile(j).m_filename);
208 fs::path source = specPath / source_file;
209 fs::path dest = tmpDir / source_file.filename();
212 cerr <<
" - " << source <<
" -> " << dest << endl;
215 if (fs::is_directory(source))
217 fs::create_directory(dest);
220 for (
const auto &dirEnt :
221 fs::recursive_directory_iterator{source})
223 fs::path newdest = dest / dirEnt.path().filename();
224 fs::copy_file(dirEnt.path(), newdest);
229 fs::copy_file(source, dest);
234 fs::path source_file(
"test.opt");
235 fs::path source = specPath / source_file;
236 bool HaveOptFile =
false;
237 if (fs::exists(source))
239 fs::path dest = tmpDir / source_file.filename();
242 cerr <<
" - " << source <<
" -> " << dest << endl;
245 if (fs::is_directory(source))
247 fs::create_directory(dest);
250 for (
const auto &dirEnt :
251 fs::recursive_directory_iterator{source})
253 fs::path newdest = dest / dirEnt.path().filename();
254 fs::copy_file(dirEnt.path(), newdest);
259 fs::copy_file(source, dest);
269 setenv(
"PYTHONPATH",
"@NEKPY_BASE_DIR@",
true);
274 bool mpiAdded =
false;
275 for (
unsigned int j = 0; j < test->GetNumCommands(); ++j)
277 Command cmd = test->GetCommand(j);
279#ifdef NEKTAR_TEST_FORCEMPIEXEC
290 command +=
"\"@MPIEXEC@\" ";
291 if (std::string(
"@NEKTAR_TEST_USE_HOSTFILE@") ==
"ON")
293 command +=
"-hostfile hostfile ";
294#if (NEKTAR_MPI_TYPE == 1)
295 if (system(
"echo 'localhost:12' > hostfile"))
297 if (system(
"echo 'localhost slots=12' > hostfile"))
300 cerr <<
"Unable to write 'hostfile' in path '"
301 << fs::current_path() << endl;
306 if (test->GetNumCommands() > 1)
308#if (NEKTAR_MPI_TYPE == 1)
316 command +=
"--tag-output ";
325 for (
unsigned int j = 0; j < test->GetNumCommands(); ++j)
327 Command cmd = test->GetCommand(j);
340 command +=
"\"@MPIEXEC@\" ";
341 if (std::string(
"@NEKTAR_TEST_USE_HOSTFILE@") ==
"ON")
343 command +=
"-hostfile hostfile ";
352 command +=
"@MPIEXEC_NUMPROC_FLAG@ ";
358 if (!fs::exists(execPath))
368 command +=
"@Python3_EXECUTABLE@ ";
372 command += pathString;
373 if (HaveOptFile && cmd.
m_executable.filename().string().find(
374 "FieldConvert") == std::string::npos)
376 command +=
" --use-opt-file test.opt ";
381 command +=
" 1>output.out 2>output.err";
388 cerr <<
"Running command: " << command << endl;
392 if (system(command.c_str()))
394 cerr <<
"Error occurred running test:" << endl;
395 cerr <<
"Command: " << command << endl;
400 if (!(fs::exists(
"output.out") && fs::exists(
"output.err")))
402 cerr <<
"One or more test output files are missing." << endl;
407 ifstream vStdout(
"output.out");
408 ifstream vStderr(
"output.err");
409 if (vStdout.bad() || vStderr.bad())
411 cerr <<
"One or more test output files are unreadable." << endl;
418 cerr <<
"Appending run " << i <<
" output and error to master."
422 while (getline(vStdout, line))
424 masterOut << line << endl;
427 while (getline(vStderr, line))
429 masterErr << line << endl;
437 for (
int i = 0; i < metrics.size(); ++i)
439 if (!metrics[i]->SupportsAverage() && test->GetNumRuns() > 1)
441 cerr <<
"WARNING: Metric " << metrics[i]->GetType()
442 <<
" does not support multiple runs. Test may yield "
443 "unexpected results."
451 if (verbose && metrics.size())
453 cerr <<
"Checking metrics:" << endl;
456 for (
int i = 0; i < metrics.size(); ++i)
458 bool gen = metricGen.find(metrics[i]->GetID()) != metricGen.end() ||
459 (vm.count(
"generate-all-metrics") > 0);
463 masterOut.seekg(0, ios::beg);
464 masterErr.seekg(0, ios::beg);
468 cerr <<
" - " << (gen ?
"generating" :
"checking")
469 <<
" metric " << metrics[i]->GetID() <<
" ("
470 << metrics[i]->GetType() <<
")... ";
473 if (!metrics[i]->Test(masterOut, masterErr))
478 cerr <<
"failed!" << endl;
483 cerr <<
"passed" << endl;
490 cerr << endl << endl;
494 if (status == 1 || verbose)
498 masterOut.seekg(0, ios::beg);
499 masterErr.seekg(0, ios::beg);
501 cout <<
"Output from test: " << test->GetDescription() << endl << endl;
503 cout <<
"=== Output ===" << endl;
504 while (masterOut.good())
506 getline(masterOut, line);
507 cout << line << endl;
509 cout <<
"=== Errors ===" << endl;
510 while (masterErr.good())
512 getline(masterErr, line);
513 cout << line << endl;
522 fs::current_path(startDir);
526 cerr <<
"Removing working directory" << endl;
539 fs::remove_all(masterDir);
542 catch (
const fs::filesystem_error &e)
544 using namespace std::chrono_literals;
545 std::this_thread::sleep_for(1ms);
549 cout <<
"Locked files encountered. "
550 <<
"Retrying after 1ms..." << endl;
564int main(
int argc,
char *argv[])
569 po::options_description desc(
"Available options");
570 desc.add_options()(
"help,h",
"Produce this help message.")(
571 "verbose,v",
"Turn on verbosity.")(
"generate-metric,g",
572 po::value<vector<int>>(),
573 "Generate a single metric.")(
574 "generate-all-metrics,a",
"Generate all metrics.")(
575 "executable,e", po::value<string>(),
"Use specified executable.");
577 po::options_description hidden(
"Hidden options");
578 hidden.add_options()(
"input-file", po::value<string>(),
"Input filename");
580 po::options_description cmdline_options(
"Command-line options");
581 cmdline_options.add(hidden).add(desc);
583 po::options_description visible(
"Allowed options");
586 po::positional_options_description p;
587 p.add(
"input-file", -1);
589 po::variables_map vm;
593 po::store(po::command_line_parser(argc, argv)
594 .options(cmdline_options)
600 catch (
const exception &e)
602 cerr << e.what() << endl;
607 if (vm.count(
"help") || vm.count(
"input-file") != 1)
609 cerr <<
"Usage: Tester [options] input-file.tst" << endl;
614 bool verbose = vm.count(
"verbose");
617 vector<int> metricGenVec;
618 if (vm.count(
"generate-metric"))
620 metricGenVec = vm[
"generate-metric"].as<vector<int>>();
622 set<int> metricGen(metricGenVec.begin(), metricGenVec.end());
625 const fs::path specFile(vm[
"input-file"].as<string>());
628 fs::path specPath = specFile.parent_path();
630 if (specPath.empty())
632 specPath = fs::current_path();
635 string specFileStem = specFile.stem().string();
639 const fs::path masterDir =
643 const fs::path startDir = fs::current_path();
649 cerr <<
"Reading test file definition: " << specFile << endl;
655 std::vector<TestData *> tests = testFile.
GetTests();
657 for (
int i = 0; i < tests.size(); ++i)
663 std::cerr <<
"Running test # " << i <<
":"
664 << test->GetDescription() << std::endl
668 status =
RunTest(test, verbose, vm, metricGen, specPath, masterDir,
678 if (vm.count(
"generate-metric") > 0 ||
679 vm.count(
"generate-all-metrics") > 0)
686 catch (
const fs::filesystem_error &e)
688 cerr <<
"Filesystem operation error occurred:" << endl;
689 cerr <<
" " << e.what() << endl;
690 cerr <<
" Files left in " << masterDir.string() << endl;
694 cerr <<
"Error occurred during test:" << endl;
695 cerr <<
" " << e.what() << endl;
696 cerr <<
" Files left in " << masterDir.string() << endl;
698 catch (
const std::exception &e)
700 cerr <<
"Unhandled exception during test:" << endl;
701 cerr <<
" " << e.what() << endl;
702 cerr <<
" Files left in " << masterDir.string() << endl;
706 cerr <<
"Unknown error during test" << endl;
707 cerr <<
" Files left in " << masterDir.string() << endl;