blob: a1d41035a7a64792f276392eaba6620498048e40 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "runtime_probe/probe_result_checker.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <base/check.h>
#include <base/logging.h>
#include <base/values.h>
#include "runtime_probe/field_converter.h"
namespace runtime_probe {
namespace {
using ReturnCode = FieldConverter::ReturnCode;
} // namespace
std::unique_ptr<ProbeResultChecker> ProbeResultChecker::FromValue(
const base::Value& value) {
if (value.is_dict())
return ProbeResultCheckerDict::FromValue(value);
if (value.is_list())
return ProbeResultCheckerList::FromValue(value);
LOG(ERROR) << "invalid type for 'expect' field: "
<< base::Value::GetTypeName(value.type());
return nullptr;
}
std::unique_ptr<ProbeResultCheckerDict> ProbeResultCheckerDict::FromValue(
const base::Value& dict_value) {
auto instance = std::make_unique<ProbeResultCheckerDict>();
for (const auto& entry : dict_value.DictItems()) {
const auto& key = entry.first;
const auto& val = entry.second;
auto print_error_and_return = [&val]() {
LOG(ERROR) << "'expect' attribute should be a list whose values are"
<< "[<required:bool>, <expected_type:string>, "
<< "<optional_validate_rule:string>], got: " << val;
return nullptr;
};
const auto& list_value = val.GetList();
if (list_value.size() < 2 || list_value.size() > 3)
return print_error_and_return();
if (!list_value[0].is_bool())
return print_error_and_return();
bool required = list_value[0].GetBool();
auto* target =
required ? &instance->required_fields_ : &instance->optional_fields_;
if (!list_value[1].is_string())
return print_error_and_return();
const auto& expect_type = list_value[1].GetString();
std::string validate_rule;
if (list_value.size() == 3) {
if (!list_value[2].is_string())
return print_error_and_return();
validate_rule = list_value[2].GetString();
}
std::unique_ptr<FieldConverter> converter = nullptr;
if (expect_type == "str") {
converter = StringFieldConverter::Build(validate_rule);
} else if (expect_type == "int") {
converter = IntegerFieldConverter::Build(validate_rule);
} else if (expect_type == "double") {
converter = DoubleFieldConverter::Build(validate_rule);
} else if (expect_type == "hex") {
converter = HexFieldConverter::Build(validate_rule);
}
if (converter == nullptr) {
LOG(ERROR) << "Cannot build converter, 'expect_type': " << expect_type
<< ", 'validate_rule': " << validate_rule;
return nullptr;
} else {
(*target)[key] = std::move(converter);
}
}
return instance;
}
bool ProbeResultCheckerDict::Apply(base::Value* probe_result) const {
bool success = true;
CHECK(probe_result != nullptr);
// Try to convert and validate each required fields.
// Any failures will cause the final result be |false|.
for (const auto& entry : required_fields_) {
const auto& key = entry.first;
const auto& converter = entry.second;
if (!probe_result->FindKey(key)) {
DVLOG(2) << "Missing key: " << key;
success = false;
break;
}
auto return_code = converter->Convert(key, probe_result);
if (return_code != ReturnCode::OK) {
auto* value = probe_result->FindKey(key);
LOG(ERROR) << "Failed to apply " << converter->ToString() << " on "
<< *value << "(ReturnCode = " << static_cast<int>(return_code)
<< ")";
success = false;
break;
}
}
// |ProbeStatement| will remove this element from final results, there is no
// need to continue.
if (!success) {
VLOG(3) << "probe_result = " << *probe_result;
return false;
}
// Try to convert and validate each optional fields.
// For failures, just remove them from probe_result and continue.
for (const auto& entry : optional_fields_) {
const auto& key = entry.first;
const auto& converter = entry.second;
if (!probe_result->FindKey(key))
continue;
auto return_code = converter->Convert(key, probe_result);
if (return_code != ReturnCode::OK) {
VLOG(1) << "Optional field '" << key << "' has unexpected value, "
<< "remove it from probe result.";
probe_result->RemoveKey(key);
}
}
// Now all fields should have the correct type, let's validate them.
for (const auto& entry : required_fields_) {
auto return_code = entry.second->Validate(entry.first, probe_result);
if (return_code != ReturnCode::OK) {
success = false;
break;
}
}
// Optional fields shouldn't have expect value.
return success;
}
std::unique_ptr<ProbeResultCheckerList> ProbeResultCheckerList::FromValue(
const base::Value& list_value) {
auto instance = std::make_unique<ProbeResultCheckerList>();
for (auto& dv : list_value.GetList()) {
if (!dv.is_dict()) {
LOG(ERROR) << "checker should be a valid dictionary";
return nullptr;
}
auto checker = ProbeResultCheckerDict::FromValue(dv);
if (!checker)
return nullptr;
instance->checkers.push_back(std::move(checker));
}
return instance;
}
bool ProbeResultCheckerList::Apply(base::Value* probe_result) const {
if (checkers.size() == 0)
return true;
for (const auto& checker : checkers) {
if (checker->Apply(probe_result))
return true;
}
return false;
}
} // namespace runtime_probe