Nektar++
MetricNoWarning.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: MetricNoWarning.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:
32// Implementation of the no-warning metric. This test metric can be used
33// in either of two ways:
34//
35// 1. Default mode: Test fails if "WARNING" appears in output OR error stream
36//
37// <metric type="nowarning" id="1">
38//
39// 2. Advanced mode: Test fails if the specified regex expression appears in
40// the output OR the error stream, AND the regex groups (caught with the
41// parenthesises) match all the expressions specified within ONE of the
42// match tags (one expression = one field tag). The example here contains
43// two regex groups, and two possible matches. The test will therefore fail
44// if, e.g.,
45// "WARNING: Invalid value"
46// or
47// "WARNING: Invalid input value"
48// appears in the output OR the error stream. A simple "WARNING" will
49// however not fail the test. The advanced mode option is therefore
50// intended to target very specific warnings/errors. Note that a regex
51// without specific regex groups can also be specified.
52//
53// <metric type="nowarning" id="1">
54// <regex>.*WARNING:.(\w+).(\w+).*</regex>
55// <matches>
56// <match>
57// <field id="1">Invalid</field>
58// <field id="2">value</field>
59// </match>
60// <match>
61// <field id="1">Invalid</field>
62// <field id="2">input</field>
63// </match>
64// </matches>
65// </metric>
66//
67//
68///////////////////////////////////////////////////////////////////////////////
69
70#include <MetricNoWarning.h>
71
72#include <algorithm>
73#include <boost/core/ignore_unused.hpp>
74#include <boost/lexical_cast.hpp>
75
76namespace Nektar
77{
79 "NOWARNING", MetricNoWarning::create);
80
81/**
82 * @brief Constructor
83 */
84MetricNoWarning::MetricNoWarning(TiXmlElement *metric, bool generate)
85 : Metric(metric, generate)
86{
87 // Check if user has provided custom warning regex, which can contain
88 // several matching groups to target specific Warning messages
89 TiXmlElement *regex = metric->FirstChildElement("regex");
90 if (regex)
91 {
92 // Check that user provided a regex for this xml tag
93 const char *tmp = regex->GetText();
94 ASSERTL0(tmp, "No text found in regex tag");
95 m_regexWarning = tmp;
96 }
97
98 // If we generate from output, we don't need to parse the xml file
99 if (m_generate)
100 {
101 return;
102 }
103
104 // Check if user has provided custom matching groups for regex
105 // Note that this is optional, a regex without matching groups is
106 // also permissible
107 TiXmlElement *matches = metric->FirstChildElement("matches");
108 if (matches)
109 {
110 // Go through all "match" tags
111 for (TiXmlElement *match = matches->FirstChildElement("match"); match;
112 match = match->NextSiblingElement("match"))
113 {
114 // Add vector with regex groups
115 m_matches.push_back(std::vector<std::string>());
116
117 // Go though all "field" tags
118 for (TiXmlElement *field = match->FirstChildElement("field"); field;
119 field = field->NextSiblingElement("field"))
120 {
121 // Extract field text (i.e. regex group)
122 const char *tmp = field->GetText();
123 // Check that user provided a text
124 ASSERTL0(tmp, "No text that specifies regex group "
125 "found in field tag");
126 m_matches.back().push_back(tmp);
127 }
128 }
129
130 // Check if user provided any match tags
131 ASSERTL0(m_matches.size(),
132 "No match tag that specifies "
133 "regex groups was found inside matches tag.");
134
135 // Check that all set of regex groups are the same size,
136 // i.e. contains the same number of "field" tags
137 int size_min = 1E3;
138 int size_max = 0;
139 for (const auto &match : m_matches)
140 {
141 size_min = std::min((int)match.size(), size_min);
142 size_max = std::max((int)match.size(), size_max);
143 }
144 ASSERTL0(size_min != 0, "No valid field tags found "
145 "for one of the match tags");
146 ASSERTL0(size_min == size_max, "Number of valid field tags "
147 "not the same for all match tags");
148 }
149}
150
151/**
152 * @brief Test if the output contains the warning message.
153 * If so, the test fails, otherwise, it passes
154 */
155bool MetricNoWarning::v_Test(std::istream &pStdout, std::istream &pStderr)
156{
157 boost::ignore_unused(pStdout, pStderr);
158
159 // Loop over both standard output and error output
160 for (std::string line; getline(pStdout, line) || getline(pStderr, line);)
161 {
162 std::smatch matches;
163
164 // Check if regex match against given output
165 if (std::regex_search(line, matches, m_regexWarning))
166 {
167 // Test fails if regex matches line and
168 // no matching groups have been specified
169 if (!m_matches.size())
170 {
171 return false;
172 }
173 // If regex groups are specified, the test fails if any of
174 // them matches the groups in "matches"
175 for (const auto &match : m_matches)
176 {
177 bool all_match = false;
178 for (int i = 1;
179 i < matches.size() && matches.size() == match.size() + 1;
180 ++i)
181 {
182 // Construct string object that contains submatch
183 std::string submatch(matches[i].first, matches[i].second);
184
185 // Compare to specified pattern
186 if (submatch == match[i - 1])
187 {
188 all_match = true;
189 continue;
190 }
191 else
192 {
193 all_match = false;
194 break;
195 }
196 }
197 if (all_match)
198 {
199 return false;
200 }
201 else
202 {
203 continue;
204 }
205 }
206 }
207 }
208
209 // If we arrived here, the test passed
210 return true;
211}
212
213/**
214 * @brief Test if the output contains the warning message.
215 * If so, generate the .tst file
216 */
217void MetricNoWarning::v_Generate(std::istream &pStdout, std::istream &pStderr)
218{
219 boost::ignore_unused(pStderr);
220
221 // Check both standard output and error output
222 for (std::string line; getline(pStdout, line) || getline(pStderr, line);)
223 {
224 std::smatch matches;
225
226 // Check if regex match against given output
227 if (std::regex_search(line, matches, m_regexWarning))
228 {
229 // If regex groups were not found, continue
230 if (matches.size() == 1)
231 {
232 continue;
233 }
234
235 // Add vector with regex groups
236 m_matches.push_back(std::vector<std::string>());
237
238 // Save all regex groups
239 for (int i = 1; i < matches.size(); ++i)
240 {
241 // Construct string object that contains submatch
242 std::string submatch(matches[i].first, matches[i].second);
243
244 m_matches.back().push_back(submatch);
245 }
246 }
247 }
248
249 // Remove matches if they already exist.
250 TiXmlElement *matches = m_metric->FirstChildElement("matches");
251 if (matches)
252 {
253 ASSERTL0(m_metric->RemoveChild(matches), "Couldn't remove matches "
254 "from metric")
255 }
256
257 // Add new "matches" tag
258 matches = new TiXmlElement("matches");
259 m_metric->LinkEndChild(matches);
260
261 // Add all "match" tags under "matches" tag
262 for (const auto &match_it : m_matches)
263 {
264 TiXmlElement *match = new TiXmlElement("match");
265 matches->LinkEndChild(match);
266
267 int j = 0;
268 for (const auto &field_it : match_it)
269 {
270 TiXmlElement *field = new TiXmlElement("field");
271 match->LinkEndChild(field);
272
273 field->SetAttribute("id", boost::lexical_cast<std::string>(j++));
274
275 field->LinkEndChild(new TiXmlText(field_it));
276 }
277 }
278}
279
280} // 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
bool m_generate
Determines whether to generate this metric or not.
Definition: Metric.h:96
std::vector< std::vector< std::string > > m_matches
static std::string type
static MetricSharedPtr create(TiXmlElement *metric, bool generate)
MetricNoWarning(TiXmlElement *metric, bool generate)
Constructor.
bool v_Test(std::istream &pStdout, std::istream &pStderr) override
Test if the output contains the warning message. If so, the test fails, otherwise,...
void v_Generate(std::istream &pStdout, std::istream &pStderr) override
Test if the output contains the warning message. If so, generate the .tst file.
MetricFactory & GetMetricFactory()
Definition: Metric.cpp:42