OGS 6.2.0-97-g4a610c866
TestDefinition.cpp
Go to the documentation of this file.
1 
12 #include "TestDefinition.h"
13 
14 #include <cmath>
15 #include <cstdlib>
16 
17 #include "BaseLib/ConfigTree.h"
18 #include "BaseLib/Error.h"
19 #include "BaseLib/FileTools.h"
20 #ifdef USE_PETSC
21 #include "MeshLib/IO/VtkIO/VtuInterface.h" // For petsc file name conversion.
22 #include <petsc.h>
23 #endif
24 
25 namespace
26 {
28 bool isConvertibleToDouble(std::string const& s)
29 {
30  std::size_t pos = 0;
31  double value;
32  try
33  {
34  value = std::stod(s, &pos);
35  }
36  catch (...)
37  {
38  OGS_FATAL("The given string '%s' is not convertible to double.",
39  s.c_str());
40  }
41  if (pos != s.size())
42  {
43  OGS_FATAL(
44  "Only %d characters were used for double conversion of string '%s'",
45  pos, s.c_str());
46  }
47 
48  if (std::isnan(value))
49  {
50  OGS_FATAL("The given string '%s' results in a NaN value.", s.c_str());
51  }
52  return true;
53 }
54 
56 std::string safeString(std::string s)
57 {
58  return "\"" + s + "\"";
59 }
60 
63 std::string findVtkdiff()
64 {
65  // Try to read the VTKDIFF_EXE environment variable.
66  if (const char* vtkdiff_exe_environment_variable =
67  std::getenv("VTKDIFF_EXE"))
68  {
69  std::string const vtkdiff_exe{vtkdiff_exe_environment_variable};
70  DBUG("VTKDIFF_EXE set to %s.", vtkdiff_exe.c_str());
71 
72  //
73  // Sanity checks.
74  //
75  { // The base name is 'vtkdiff'
76  auto const& base_name =
78  if (base_name != "vtkdiff")
79  {
80  OGS_FATAL(
81  "The VTKDIFF_EXE environment variable does not point to "
82  "'vtkdiff'. VTKDIFF_EXE='%s'",
83  vtkdiff_exe.c_str());
84  }
85  }
86  { // vtkdiff must exist.
87  if (!BaseLib::IsFileExisting(vtkdiff_exe))
88  {
89  OGS_FATAL(
90  "The VTKDIFF_EXE points to a non-existing file. "
91  "VTKDIFF_EXE='%s'",
92  vtkdiff_exe.c_str());
93  }
94  }
95 
96  //
97  // Test the actual call.
98  //
99  int const return_value =
100  // TODO (naumov) replace system call with output consuming call
101  // (fork + execl seems to be more safe), and extract the vtkdiff
102  // call to common function. Also properly escape all strings in
103  // command lines.
104  // Reference for POSIX and Windows:
105  // https://wiki.sei.cmu.edu/confluence/pages/viewpage.action?pageId=87152177
106  // Take care when using fork, which might copy resources.
107  std::system((vtkdiff_exe + " --version").c_str());
108  if (return_value == 0)
109  {
110  return vtkdiff_exe;
111  }
112  WARN(
113  "Calling %s from the VTKDIFF_EXE environment variable didn't work "
114  "as expected. Return value was %d.",
115  vtkdiff_exe.c_str(), return_value);
116  }
117 
118  std::string const vtkdiff_exe{"vtkdiff"};
119  std::vector<std::string> const paths = {"", "bin"};
120  auto const path = find_if(
121  begin(paths), end(paths), [&vtkdiff_exe](std::string const& path) {
122  int const return_value =
123  // TODO (naumov) replace system call with output consuming call
124  // as in an above todo comment.
125  std::system(
126  (BaseLib::joinPaths(path, vtkdiff_exe) + " --version")
127  .c_str());
128  return return_value == 0;
129  });
130  if (path == end(paths))
131  {
132  OGS_FATAL("vtkdiff not found.");
133  }
134  return BaseLib::joinPaths(*path, vtkdiff_exe);
135 }
136 
137 } // namespace
138 
139 namespace ApplicationsLib
140 {
142  std::string const& reference_path,
143  std::string const& output_directory)
144 {
145  if (reference_path.empty())
146  {
147  OGS_FATAL(
148  "Reference path containing expected result files can not be "
149  "empty.");
150  }
151 
152  std::string const vtkdiff = findVtkdiff();
153 
154  // Construct command lines for each entry.
156  auto const& vtkdiff_configs = config_tree.getConfigSubtreeList("vtkdiff");
157  _command_lines.reserve(vtkdiff_configs.size());
158  for (auto const& vtkdiff_config : vtkdiff_configs)
159  {
160  std::string const& field_name =
162  vtkdiff_config.getConfigParameter<std::string>("field");
163  DBUG("vtkdiff will compare field '%s'.", field_name.c_str());
164 
165 #ifdef USE_PETSC
166  int rank;
167  MPI_Comm_rank(PETSC_COMM_WORLD, &rank);
168  int mpi_size;
169  MPI_Comm_size(PETSC_COMM_WORLD, &mpi_size);
170  std::string const& filename =
171  MeshLib::IO::getVtuFileNameForPetscOutputWithoutExtension(
173  vtkdiff_config.getConfigParameter<std::string>("file")) +
174  "_" + std::to_string(rank) + ".vtu";
175 #else
176  std::string const& filename =
178  vtkdiff_config.getConfigParameter<std::string>("file");
179 #endif // OGS_USE_PETSC
180  std::string const& output_filename =
181  BaseLib::joinPaths(output_directory, filename);
182  _output_files.push_back(output_filename);
183  // TODO (naumov) expand filename relative to ref path for globbing.
184  std::string const& reference_filename =
185  BaseLib::joinPaths(reference_path, filename);
186 
187  auto const absolute_tolerance =
189  vtkdiff_config.getConfigParameter<std::string>("absolute_tolerance",
190  "");
191  if (!absolute_tolerance.empty() &&
192  !isConvertibleToDouble(absolute_tolerance))
193  {
194  OGS_FATAL(
195  "The absolute tolerance value '%s' is not convertible to "
196  "double.",
197  absolute_tolerance.c_str());
198  }
199  std::string const absolute_tolerance_parameter =
200  "--abs " + absolute_tolerance;
201 
202  auto const relative_tolerance =
204  vtkdiff_config.getConfigParameter<std::string>("relative_tolerance",
205  "");
206  if (!relative_tolerance.empty() &&
207  !isConvertibleToDouble(relative_tolerance))
208  {
209  OGS_FATAL(
210  "The relative tolerance value '%s' is not convertible to "
211  "double.",
212  relative_tolerance.c_str());
213  }
214  std::string const relative_tolerance_parameter =
215  "--rel " + relative_tolerance;
216 
217  //
218  // Construct command line.
219  //
220  std::string command_line =
221  vtkdiff + " -a " + safeString(field_name) + " -b " +
222  safeString(field_name) + " " + safeString(reference_filename) +
223  " " + safeString(output_filename) + " " +
224  absolute_tolerance_parameter + " " + relative_tolerance_parameter;
225  INFO("Will run '%s'", command_line.c_str());
226  _command_lines.emplace_back(std::move(command_line));
227  }
228 }
229 
231 {
232  std::vector<int> return_values;
233  transform(begin(_command_lines), end(_command_lines),
234  back_inserter(return_values),
235  [](std::string const& command_line) {
236  int const return_value = std::system(command_line.c_str());
237  if (return_value != 0)
238  {
239  WARN("Return value %d was returned by '%s'.",
240  return_value, command_line.c_str());
241  }
242  return return_value;
243  });
244  return !return_values.empty() &&
245  all_of(begin(return_values), end(return_values),
246  [](int const& return_value) { return return_value == 0; });
247 }
248 
249 std::vector<std::string> const& TestDefinition::getOutputFiles() const
250 {
251  return _output_files;
252 }
253 } // namespace ApplicationsLib
bool isConvertibleToDouble(std::string const &s)
Test if the given string is convertible to a valid double value, not a NaN.
Implementation of the VtuInterface class.
std::vector< std::string > _output_files
std::string safeString(std::string s)
Wraps a string into double ticks.
bool IsFileExisting(const std::string &strFilename)
Returns true if given file exists. From http://www.techbytes.ca/techbyte103.html. ...
Definition: FileTools.cpp:36
std::string extractBaseNameWithoutExtension(std::string const &pathname)
Definition: FileTools.cpp:126
std::vector< std::string > const & getOutputFiles() const
Range< SubtreeIterator > getConfigSubtreeList(std::string const &root) const
Definition: ConfigTree.cpp:174
std::vector< std::string > _command_lines
#define OGS_FATAL(fmt,...)
Definition: Error.h:63
Filename manipulation routines.
std::string joinPaths(std::string const &pathA, std::string const &pathB)
Definition: FileTools.cpp:195
TestDefinition(BaseLib::ConfigTree const &config_tree, std::string const &reference_path, std::string const &output_directory)