96{
97 int status = 0;
98 string command;
99
100 if (verbose &&
test->GetNumMetrics() > 0)
101 {
102 cerr << "Creating metrics:" << endl;
103 }
104
105
106 vector<MetricSharedPtr> metrics;
107 for (
unsigned int i = 0; i <
test->GetNumMetrics(); ++i)
108 {
109 set<int>::iterator it = metricGen.find(
test->GetMetricId(i));
110 bool genMetric =
111 it != metricGen.end() || (vm.count("generate-all-metrics") > 0);
112
114 test->GetMetricType(i),
test->GetMetric(i), genMetric));
115
116 if (verbose)
117 {
118 cerr << " - ID " << metrics.back()->GetID() << ": "
119 << metrics.back()->GetType() << endl;
120 }
121
122 if (it != metricGen.end())
123 {
124 metricGen.erase(it);
125 }
126 }
127
128 if (metricGen.size() != 0)
129 {
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)
134 {
135 cerr << *it << " ";
136 }
137 cerr << endl;
138 return 1;
139 }
140
141
142 if (fs::exists(masterDir))
143 {
144 fs::remove_all(masterDir);
145 }
146
147 if (verbose)
148 {
149 cerr << "Creating master directory: " << masterDir << endl;
150 }
151
152
153 fs::create_directory(masterDir);
154
155
156 fs::current_path(masterDir);
157
158
159
160 fstream masterOut("master.out", ios::out | ios::in | ios::trunc);
161 fstream masterErr("master.err", ios::out | ios::in | ios::trunc);
162
163 if (masterOut.bad() || masterErr.bad())
164 {
165 cerr << "One or more master output files are unreadable." << endl;
166 throw 1;
167 }
168
169
170 vector<fs::path> tmpWorkingDirs;
171 string line;
172
173 for (
unsigned int i = 0; i <
test->GetNumRuns(); ++i)
174 {
175 command = "";
176
177 if (verbose)
178 {
179 cerr << "Starting run " << i << "." << endl;
180 }
181
182
183 const fs::path tmpDir = masterDir / fs::path("run" + std::to_string(i));
184 tmpWorkingDirs.push_back(tmpDir);
185
186 if (verbose)
187 {
188 cerr << "Creating working directory: " << tmpDir << endl;
189 }
190
191
192 fs::create_directory(tmpDir);
193
194
195 fs::current_path(tmpDir);
196
197 if (verbose &&
test->GetNumDependentFiles())
198 {
199 cerr << "Copying required files: " << endl;
200 }
201
202
203
204 for (
unsigned int j = 0; j <
test->GetNumDependentFiles(); ++j)
205 {
206 fs::path source_file(
test->GetDependentFile(j).m_filename);
207
208 fs::path source = specPath / source_file;
209 fs::path dest = tmpDir / source_file.filename();
210 if (verbose)
211 {
212 cerr << " - " << source << " -> " << dest << endl;
213 }
214
215 if (fs::is_directory(source))
216 {
217 fs::create_directory(dest);
218
219
220 for (const auto &dirEnt :
221 fs::recursive_directory_iterator{source})
222 {
223 fs::path newdest = dest / dirEnt.path().filename();
224 fs::copy_file(dirEnt.path(), newdest);
225 }
226 }
227 else
228 {
229 fs::copy_file(source, dest);
230 }
231 }
232
233
234 fs::path source_file("test.opt");
235 fs::path source = specPath / source_file;
236 bool HaveOptFile = false;
237 if (fs::exists(source))
238 {
239 fs::path dest = tmpDir / source_file.filename();
240 if (verbose)
241 {
242 cerr << " - " << source << " -> " << dest << endl;
243 }
244
245 if (fs::is_directory(source))
246 {
247 fs::create_directory(dest);
248
249
250 for (const auto &dirEnt :
251 fs::recursive_directory_iterator{source})
252 {
253 fs::path newdest = dest / dirEnt.path().filename();
254 fs::copy_file(dirEnt.path(), newdest);
255 }
256 }
257 else
258 {
259 fs::copy_file(source, dest);
260 }
261
262 HaveOptFile = true;
263 }
264
265
266
267
268
269 setenv("PYTHONPATH", "@NEKPY_BASE_DIR@", true);
270
271
272
273
274 bool mpiAdded = false;
275 for (
unsigned int j = 0; j <
test->GetNumCommands(); ++j)
276 {
278
279#ifdef NEKTAR_TEST_FORCEMPIEXEC
280#else
283#endif
284 {
285 if (mpiAdded)
286 {
287 continue;
288 }
289
290 command += "\"@MPIEXEC@\" ";
291 if (std::string("@NEKTAR_TEST_USE_HOSTFILE@") == "ON")
292 {
293 command += "-hostfile hostfile ";
294#if (NEKTAR_MPI_TYPE == 1)
295 if (system("echo 'localhost:12' > hostfile"))
296#else
297 if (system("echo 'localhost slots=12' > hostfile"))
298#endif
299 {
300 cerr << "Unable to write 'hostfile' in path '"
301 << fs::current_path() << endl;
302 status = 1;
303 }
304 }
305
306 if (
test->GetNumCommands() > 1)
307 {
308#if (NEKTAR_MPI_TYPE == 1)
309
310
311
312
313
314
315#else
316 command += "--tag-output ";
317#endif
318 }
319
320 mpiAdded = true;
321 }
322 }
323
324
325 for (
unsigned int j = 0; j <
test->GetNumCommands(); ++j)
326 {
328
329
330
332 {
333 command += " : ";
334 }
336 {
337 command += " && ";
339 {
340 command += "\"@MPIEXEC@\" ";
341 if (std::string("@NEKTAR_TEST_USE_HOSTFILE@") == "ON")
342 {
343 command += "-hostfile hostfile ";
344 }
345 }
346 }
347
348
351 {
352 command += "@MPIEXEC_NUMPROC_FLAG@ ";
354 }
355
356
358 if (!fs::exists(execPath))
359 {
362 }
363
364
365
367 {
368 command += "@Python3_EXECUTABLE@ ";
369 }
370
372 command += pathString;
373 if (HaveOptFile && cmd.
m_executable.filename().string().find(
374 "FieldConvert") == std::string::npos)
375 {
376 command += " --use-opt-file test.opt ";
377 }
378
379 command += " ";
381 command += " 1>output.out 2>output.err";
382 }
383
384 status = 0;
385
386 if (verbose)
387 {
388 cerr << "Running command: " << command << endl;
389 }
390
391
392 if (system(command.c_str()))
393 {
394 cerr << "Error occurred running test:" << endl;
395 cerr << "Command: " << command << endl;
396 status = 1;
397 }
398
399
400 if (!(fs::exists("output.out") && fs::exists("output.err")))
401 {
402 cerr << "One or more test output files are missing." << endl;
403 throw 1;
404 }
405
406
407 ifstream vStdout("output.out");
408 ifstream vStderr("output.err");
409 if (vStdout.bad() || vStderr.bad())
410 {
411 cerr << "One or more test output files are unreadable." << endl;
412 throw 1;
413 }
414
415
416 if (verbose)
417 {
418 cerr << "Appending run " << i << " output and error to master."
419 << endl;
420 }
421
422 while (getline(vStdout, line))
423 {
424 masterOut << line << endl;
425 }
426
427 while (getline(vStderr, line))
428 {
429 masterErr << line << endl;
430 }
431
432 vStdout.close();
433 vStderr.close();
434 }
435
436
437 for (int i = 0; i < metrics.size(); ++i)
438 {
439 if (!metrics[i]->SupportsAverage() &&
test->GetNumRuns() > 1)
440 {
441 cerr << "WARNING: Metric " << metrics[i]->GetType()
442 << " does not support multiple runs. Test may yield "
443 "unexpected results."
444 << endl;
445 }
446 }
447
448
449 if (status == 0)
450 {
451 if (verbose && metrics.size())
452 {
453 cerr << "Checking metrics:" << endl;
454 }
455
456 for (int i = 0; i < metrics.size(); ++i)
457 {
458 bool gen = metricGen.find(metrics[i]->GetID()) != metricGen.end() ||
459 (vm.count("generate-all-metrics") > 0);
460
461 masterOut.clear();
462 masterErr.clear();
463 masterOut.seekg(0, ios::beg);
464 masterErr.seekg(0, ios::beg);
465
466 if (verbose)
467 {
468 cerr << " - " << (gen ? "generating" : "checking")
469 << " metric " << metrics[i]->GetID() << " ("
470 << metrics[i]->GetType() << ")... ";
471 }
472
473 if (!metrics[i]->Test(masterOut, masterErr))
474 {
475 status = 1;
476 if (verbose)
477 {
478 cerr << "failed!" << endl;
479 }
480 }
481 else if (verbose)
482 {
483 cerr << "passed" << endl;
484 }
485 }
486 }
487
488 if (verbose)
489 {
490 cerr << endl << endl;
491 }
492
493
494 if (status == 1 || verbose)
495 {
496 masterOut.clear();
497 masterErr.clear();
498 masterOut.seekg(0, ios::beg);
499 masterErr.seekg(0, ios::beg);
500
501 cout <<
"Output from test: " <<
test->GetDescription() << endl << endl;
502
503 cout << "=== Output ===" << endl;
504 while (masterOut.good())
505 {
506 getline(masterOut, line);
507 cout << line << endl;
508 }
509 cout << "=== Errors ===" << endl;
510 while (masterErr.good())
511 {
512 getline(masterErr, line);
513 cout << line << endl;
514 }
515 }
516
517
518 masterOut.close();
519 masterErr.close();
520
521
522 fs::current_path(startDir);
523
524 if (verbose)
525 {
526 cerr << "Removing working directory" << endl;
527 }
528
529
530
531
532
533 int i = 1000;
534 while (i > 0)
535 {
536 try
537 {
538
539 fs::remove_all(masterDir);
540 break;
541 }
542 catch (const fs::filesystem_error &e)
543 {
544 using namespace std::chrono_literals;
545 std::this_thread::sleep_for(1ms);
546 i--;
547 if (i > 0)
548 {
549 cout << "Locked files encountered. "
550 << "Retrying after 1ms..." << endl;
551 }
552 else
553 {
554
555
556 throw e;
557 }
558 }
559 }
560
561 return status;
562}
#define ASSERTL0(condition, msg)
MetricSharedPtr CreateInstance(std::string key, TiXmlElement *elmt, bool generate)
static std::string PortablePath(const fs::path &path)
create portable path on different platforms for std::filesystem path.
MetricFactory & GetMetricFactory()
CommandType m_commandType