Nektar++
MetricRegex.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: MetricRegex.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Implementation of the regular-expression metric.
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#include <MetricRegex.h>
36
37#include <boost/algorithm/string.hpp>
38#include <boost/core/ignore_unused.hpp>
39#include <boost/lexical_cast.hpp>
40
41using namespace std;
42
43namespace Nektar
44{
45std::string MetricRegex::type =
47
48/**
49 * @brief Constructor.
50 */
51MetricRegex::MetricRegex(TiXmlElement *metric, bool generate)
52 : Metric(metric, generate)
53{
54 // If we are a derived class, do nothing
55 if (m_type != "REGEX")
56 {
57 return;
58 }
59
60 // Parse Regex expression
61 TiXmlElement *regex = metric->FirstChildElement("regex");
62 ASSERTL0(regex, "No Regex defined.");
63 ASSERTL0(regex->GetText(), "Failed to get text");
64 m_regex = std::regex(regex->GetText(), std::regex::ECMAScript);
65
66 // Parse matching values if not generating.
67 if (m_generate)
68 {
69 return;
70 }
71
72 TiXmlElement *matches = metric->FirstChildElement("matches");
73 ASSERTL0(matches, "No matches defined.");
74 TiXmlElement *match = matches->FirstChildElement("match");
75 while (match)
76 {
77 std::vector<MetricRegexFieldValue> tmp;
78 TiXmlElement *field = match->FirstChildElement("field");
79 while (field)
80 {
82 v.m_value = field->GetText();
83 v.m_useTolerance = false;
84 v.m_useIntTolerance = false;
85
86 const char *tol = field->Attribute("tolerance");
87 if (tol)
88 {
89 v.m_useTolerance = true;
90 v.m_tolerance = atof(tol);
91 }
92
93 const char *intTol = field->Attribute("inttolerance");
94 if (intTol)
95 {
96 v.m_useIntTolerance = true;
97 v.m_intTolerance = atoi(intTol);
98 }
99
100 ASSERTL0(!(intTol && tol),
101 "Cannot use both integer and double tolerances.");
102
103 tmp.push_back(v);
104 field = field->NextSiblingElement("field");
105 }
106 m_matches.push_back(tmp);
107
108 match = match->NextSiblingElement("match");
109 }
110}
111
112/**
113 * @brief Test output against a regex expression and set of matches.
114 */
115bool MetricRegex::v_Test(std::istream &pStdout, std::istream &pStderr)
116{
117 boost::ignore_unused(pStdout, pStderr);
118
119 ASSERTL0(m_matches.size(), "No test conditions defined for Regex.");
120
121 // Select istream to use.
122 std::istream &is = m_useStderr ? pStderr : pStdout;
123
124 std::vector<MetricRegexFieldValue> &okValues = m_matches[0];
125 int nMatch = m_matches.size();
126 bool success = true;
127 bool matchedTol = false;
128 std::smatch matches;
129
130 // Process output file line by line searching for regex matches
131 std::string line;
132 while (getline(is, line) && m_matches.size() > 0)
133 {
134 matchedTol = true;
135
136 // Test to see if we have a match on this line.
137 if (std::regex_match(line, matches, m_regex))
138 {
139 // Error if no fields in regex then throw an error.
140 if (matches.size() == 1)
141 {
142 cerr << "No test sections in regex!" << endl;
143 return false;
144 }
145
146 // Check each regex capture group in turn
147 for (int i = 1; i < matches.size(); ++i)
148 {
149 // extract the captured string
150 std::string match(matches[i].first, matches[i].second);
151
152 if (okValues[i - 1].m_useTolerance)
153 {
154 double val;
155 try
156 {
157 val = fabs(fabs(boost::lexical_cast<double>(
158 okValues[i - 1].m_value)) -
159 fabs(boost::lexical_cast<double>(match)));
160 }
161 catch (boost::bad_lexical_cast &e)
162 {
163 cerr << "Could not convert one of " << match
164 << " (match) or " << okValues[i - 1].m_value
165 << " (comparison value) to double" << endl;
166 success = false;
167 continue;
168 }
169
170 // If the okValues are not within tolerance, failed the
171 // test.
172 if (val > okValues[i - 1].m_tolerance)
173 {
174 if (m_unordered)
175 {
176 matchedTol = false;
177 }
178 else
179 {
180 cerr << "Failed tolerance match." << endl;
181 cerr << " Expected: " << okValues[i - 1].m_value
182 << " +/- " << okValues[i - 1].m_tolerance
183 << endl;
184 cerr << " Result: " << match << endl;
185 success = false;
186 }
187 }
188 }
189 else if (okValues[i - 1].m_useIntTolerance)
190 {
191 int val;
192 try
193 {
194 val = abs(
195 boost::lexical_cast<int>(okValues[i - 1].m_value) -
196 boost::lexical_cast<int>(match));
197 }
198 catch (boost::bad_lexical_cast &e)
199 {
200 cerr << "Could not convert one of " << match
201 << " (match) or " << okValues[i - 1].m_value
202 << " (comparison value) to an integer" << endl;
203 success = false;
204 continue;
205 }
206
207 // If the okValues are not within tolerance, failed the
208 // test.
209 if (val > okValues[i - 1].m_intTolerance)
210 {
211 if (m_unordered)
212 {
213 matchedTol = false;
214 }
215 else
216 {
217 cerr << "Failed tolerance match." << endl;
218 cerr << " Expected: " << okValues[i - 1].m_value
219 << " +/- " << okValues[i - 1].m_intTolerance
220 << endl;
221 cerr << " Result: " << match << endl;
222 success = false;
223 }
224 }
225 }
226 else
227 {
228 // Case insensitive match.
229 if (!boost::iequals(match, okValues[i - 1].m_value))
230 {
231 if (m_unordered)
232 {
233 matchedTol = false;
234 }
235 else
236 {
237 cerr << "Failed case-insensitive match." << endl;
238 cerr << " Expected: " << okValues[i - 1].m_value
239 << endl;
240 cerr << " Result: " << match << endl;
241 success = false;
242 }
243 }
244 }
245 }
246
247 // Remove this match from the list of matches.
248 if (matchedTol)
249 {
250 m_matches.erase(m_matches.begin());
251 }
252 }
253 }
254
255 if (m_matches.size() != 0)
256 {
257 cerr << "Expected " << nMatch << " matches but only found "
258 << (nMatch - m_matches.size()) << "!" << endl;
259 success = false;
260 }
261
262 return success;
263}
264
265/**
266 * @brief Test output against a regex expression and set of matches.
267 */
268void MetricRegex::v_Generate(std::istream &pStdout, std::istream &pStderr)
269{
270 boost::ignore_unused(pStderr);
271
272 // Select istream to use.
273 std::istream &is = m_useStderr ? pStderr : pStdout;
274
275 std::smatch matches;
276
277 // Process output file line by line searching for regex matches
278 std::string line;
279 while (getline(is, line))
280 {
281 // Test to see if we have a match on this line.
282 if (std::regex_match(line, matches, m_regex))
283 {
284 // Error if no fields in regex then throw an error.
285 ASSERTL0(matches.size() != 1, "No test sections in regex!");
286
287 vector<MetricRegexFieldValue> okValues;
288
289 for (int i = 1; i < matches.size(); ++i)
290 {
291 // Create new field.
292 MetricRegexFieldValue okValue;
293 okValue.m_useTolerance = false;
294 okValue.m_value =
295 std::string(matches[i].first, matches[i].second);
296 okValues.push_back(okValue);
297 }
298
299 m_matches.push_back(okValues);
300 }
301 }
302
303 // If we are not a derived class then create a new structure.
304 if (m_type == "REGEX")
305 {
306 // Remove matches if they already exist.
307 TiXmlElement *matches = m_metric->FirstChildElement("matches");
308 if (matches)
309 {
310 ASSERTL0(m_metric->RemoveChild(matches),
311 "Couldn't remove matches from metric!");
312 }
313
314 // Create new matches element.
315 matches = new TiXmlElement("matches");
316 m_metric->LinkEndChild(matches);
317
318 for (int i = 0; i < m_matches.size(); ++i)
319 {
320 TiXmlElement *match = new TiXmlElement("match");
321 matches->LinkEndChild(match);
322
323 for (int j = 0; j < m_matches[i].size(); ++j)
324 {
325 TiXmlElement *field = new TiXmlElement("field");
326 match->LinkEndChild(field);
327
328 field->SetAttribute("id", boost::lexical_cast<std::string>(j));
329
330 if (m_matches[i][j].m_useTolerance)
331 {
332 field->SetAttribute("tolerance",
333 boost::lexical_cast<std::string>(
334 m_matches[i][j].m_tolerance));
335 }
336
337 field->LinkEndChild(new TiXmlText(m_matches[i][j].m_value));
338 }
339 }
340 }
341}
342} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
std::string RegisterCreatorFunction(std::string key, CreatorFunction func)
Definition: Metric.h:133
Base class for all metrics. Metric represents a test metric that can be used to evaluate the function...
Definition: Metric.h:65
TiXmlElement * m_metric
Pointer to XML structure containing metric definition.
Definition: Metric.h:101
std::string m_type
Stores the type of this metric (uppercase).
Definition: Metric.h:94
bool m_generate
Determines whether to generate this metric or not.
Definition: Metric.h:96
bool m_unordered
If true, regex matches may be in any order in output.
Definition: MetricRegex.h:86
void v_Generate(std::istream &pStdout, std::istream &pStderr) override
Test output against a regex expression and set of matches.
static std::string type
Definition: MetricRegex.h:78
MetricRegex(TiXmlElement *metric, bool generate)
Constructor.
Definition: MetricRegex.cpp:51
std::vector< std::vector< MetricRegexFieldValue > > m_matches
Stores the multiple matches defined in each <MATCH> tag.
Definition: MetricRegex.h:84
bool m_useStderr
If true, use stderr for testing/generation instead of stdout.
Definition: MetricRegex.h:88
static MetricSharedPtr create(TiXmlElement *metric, bool generate)
Definition: MetricRegex.h:73
std::regex m_regex
Storage for the boost regex.
Definition: MetricRegex.h:82
bool v_Test(std::istream &pStdout, std::istream &pStderr) override
Test output against a regex expression and set of matches.
MetricFactory & GetMetricFactory()
Definition: Metric.cpp:42
STL namespace.
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
Data structure for a Regex value to match.
Definition: MetricRegex.h:49