Checks if a set of requirements is satisified by the capabilities.
This method is exposed to Python within pycapabilities.Capabilities.check in python/pycapabilities/_pycapabilities.C. This external method is used significantly by the TestHarness to check capabilities for individual test specs.
Requirements can use comparison operators (>,<,>=,<=,=!,=), where the name of the capability must always be on the left hand side. Comparisons can be performed on strings "compiler!=GCC" (which are case insensitive), integer numbers "ad_size>=50", and version numbers "petsc>3.8.0". The state of a boolean valued capability can be tested by just specifying the capability name "chaco". This check can be inverted using the ! operator as "!chaco".
The logic operators & and | can be used to chain multiple checks as "thermochimica & thermochimica>1.0". Parenthesis can be used to build complex logic expressions.
95 const auto len = requirements.length();
96 if (len >= 2 && ((requirements[0] ==
'\'' && requirements[len - 1] ==
'\'') ||
97 (requirements[0] ==
'"' && requirements[len - 1] ==
'"')))
98 requirements = requirements.substr(1, len - 2);
104 result.state = CheckState::CERTAIN_FAIL;
106 if (requirements.length() == 0)
108 result.state = CheckState::CERTAIN_PASS;
112 static parser parser(R
"( 113 Expression <- _ Bool _ LogicOperator _ Expression / Bool _ 114 Bool <- Comparison / '!' Bool / '!' Identifier / Identifier / '(' _ Expression _ ')' 115 Comparison <- Identifier _ Operator _ Version / Identifier _ Operator _ String 116 String <- [a-zA-Z0-9_-]+ 117 Identifier <- [a-zA-Z][a-zA-Z0-9_]* 119 LogicOperator <- [&|] 120 Version <- Number '.' Version / Number 125 if (!static_cast<bool>(parser))
126 throw CapabilityException(
"Capabilities parser build failure.");
130 std::set<std::string> unknown_capabilities;
131 const auto add_unknown_capability = [&unknown_capabilities](
const auto &
name)
135 for (
const auto & name : options.ignore_capabilities)
137 throw CapabilityException(
"Capability to ignore '" + name +
"' is not known");
139 parser[
"Number"] = [](
const SemanticValues & vs) {
return vs.token_to_number<
int>(); };
141 parser[
"Version"] = [](
const SemanticValues & vs)
147 std::vector<int> ret{std::any_cast<
int>(vs[0])};
148 const auto & vs1 = std::any_cast<std::vector<int>>(vs[1]);
149 ret.insert(ret.end(), vs1.begin(), vs1.end());
154 return std::vector<int>{std::any_cast<
int>(vs[0])};
166 parser[
"LogicOperator"] = [](
const SemanticValues & vs)
168 const auto op = vs.token();
186 parser[
"Operator"] = [](
const SemanticValues & vs)
188 const auto op = vs.token();
192 return OP_GREATER_EQ;
199 if (op ==
"=" || op ==
"==")
204 parser[
"String"] = [](
const SemanticValues & vs) {
return vs.token_to_string(); };
205 parser[
"Identifier"] = [](
const SemanticValues & vs) {
return vs.token_to_string(); };
207 parser[
"Comparison"] =
208 [
this, &add_unknown_capability, &options, &result](
const SemanticValues & vs)
210 const auto left = std::any_cast<std::string>(vs[0]);
211 const auto op = std::any_cast<Operator>(vs[1]);
214 const auto capability_ptr =
query(left);
219 add_unknown_capability(left);
220 return CheckState::UNKNOWN;
224 const auto & capability = *capability_ptr;
225 const auto &
name = capability.getName();
228 result.capability_names.insert(name);
231 const auto is_ignored = [&
name, &options]() {
return options.ignore_capabilities.count(name); };
234 if (
const auto bool_ptr = capability.queryBoolValue(); (bool_ptr && !(*bool_ptr)))
235 return is_ignored() ? CheckState::IGNORE : CheckState::CERTAIN_FAIL;
238 auto comp = [&is_ignored](
const int i,
const auto & a,
const auto & b)
242 return CheckState::IGNORE;
245 const auto do_comp = [&i, &a, &b]()
264 return do_comp() ? CheckState::CERTAIN_PASS : CheckState::CERTAIN_FAIL;
268 std::vector<int> app_value_version;
275 const auto right = std::any_cast<std::vector<int>>(vs[2]);
276 if (
const auto int_ptr = capability.queryIntValue())
278 if (right.size() != 1)
279 checkException(vs,
"cannot be compared to a version.", capability);
281 return comp(op, *int_ptr, right[0]);
284 const auto string_ptr = capability.queryStringValue();
287 "cannot be compared to a " +
288 std::string(right.size() == 1 ?
"number" :
"version number") +
".",
292 checkException(vs,
"cannot be compared to a version.", capability);
295 return comp(op, app_value_version, right);
303 const auto string_ptr = capability.queryStringValue();
305 checkException(vs,
"cannot be compared to a string.", capability);
309 if (!capability.hasEnumeration(right))
311 "'" + right +
"' invalid for capability '" + left +
312 "'; valid values: " + capability.enumerationToString());
316 checkException(vs,
"cannot be compared to a string.", capability);
318 return comp(op, *string_ptr, right);
325 parser[
"Bool"] = [
this, &add_unknown_capability, &options, &result](
const SemanticValues & vs)
334 switch (std::any_cast<CheckState>(vs[0]))
336 case CheckState::CERTAIN_FAIL:
337 return CheckState::CERTAIN_PASS;
338 case CheckState::CERTAIN_PASS:
339 return CheckState::CERTAIN_FAIL;
340 case CheckState::POSSIBLE_FAIL:
341 return CheckState::POSSIBLE_PASS;
342 case CheckState::POSSIBLE_PASS:
343 return CheckState::POSSIBLE_FAIL;
344 case CheckState::IGNORE:
345 return CheckState::IGNORE;
347 return CheckState::UNKNOWN;
353 const bool negated = vs.choice() == 2;
354 const auto identifier = std::any_cast<std::string>(vs[0]);
355 if (
const auto capability_ptr =
query(identifier))
357 const auto & capability = *capability_ptr;
358 const auto &
name = capability.getName();
361 if (capability.getExplicit())
363 std::string message =
"capability '" +
name +
364 "' requires a value and cannot be used in a boolean expression";
365 if (capability.queryEnumeration())
366 message +=
"; valid values: " + capability.enumerationToString();
371 result.capability_names.insert(name);
373 if (options.ignore_capabilities.count(name))
374 return CheckState::IGNORE;
377 const auto bool_to_pass = [&negated](
const bool val)
378 {
return (val ^ negated) ? CheckState::CERTAIN_FAIL : CheckState::CERTAIN_PASS; };
380 if (
const auto bool_ptr = capability.queryBoolValue())
381 return bool_to_pass(!*bool_ptr);
383 return bool_to_pass(
false);
386 add_unknown_capability(identifier);
387 return negated ? CheckState::POSSIBLE_PASS : CheckState::POSSIBLE_FAIL;
391 throw CapabilityException(
"Unknown choice in Bool non-terminal");
395 parser[
"Expression"] = [](
const SemanticValues & vs)
401 const auto left = std::any_cast<
CheckState>(vs[0]);
402 const auto right = std::any_cast<
CheckState>(vs[2]);
403 const auto op = std::any_cast<LogicOperator>(vs[1]);
408 if (left == CheckState::IGNORE)
410 if (right == CheckState::IGNORE)
412 for (
const auto state : {CheckState::CERTAIN_FAIL,
413 CheckState::POSSIBLE_FAIL,
415 CheckState::POSSIBLE_PASS,
416 CheckState::CERTAIN_PASS})
417 if (left == state || right == state)
419 throw CapabilityException(
"Conjunction failure");
422 if (left == CheckState::IGNORE || right == CheckState::IGNORE)
423 return CheckState::IGNORE;
424 for (
const auto state : {CheckState::CERTAIN_PASS,
425 CheckState::POSSIBLE_PASS,
427 CheckState::POSSIBLE_FAIL,
428 CheckState::CERTAIN_FAIL})
429 if (left == state || right == state)
431 throw CapabilityException(
"Conjunction failure");
434 throw CapabilityException(
"Unknown logic operator");
442 throw CapabilityException(
"Unknown choice in Expression non-terminal");
447 parser.enable_packrat_parsing();
449 if (!parser.parse(requirements, result.state))
450 throw CapabilityException(
"Unable to parse requested capabilities '", requirements,
"'.");
453 if (options.certain && unknown_capabilities.size())
454 throw UnknownCapabilitiesException({unknown_capabilities.begin(), unknown_capabilities.end()});
457 if (result.state == CheckState::IGNORE)
458 result.state = CheckState::CERTAIN_PASS;
std::string name(const ElemQuality q)
bool tokenizeAndConvert(const std::string &str, std::vector< T > &tokenized_vector, const std::string &delimiter=" \\\)
tokenizeAndConvert splits a string using delimiter and then converts to type T.
CheckState
Return state for check.
RegistryType _registry
Registry storage.
void checkException(const peg::SemanticValues &vs, const std::string &message, const std::optional< Capability > capability={})
std::string toLower(std::string name)
Convert supplied string to lower case.
const Capability * query(std::string capability) const
Query a capability, if it exists, otherwise nullptr.