|
|
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmCTestResourceSpec.h"
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <cm3p/json/reader.h>
|
|
|
|
#include <cm3p/json/value.h>
|
|
|
|
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
|
|
#include "cmsys/RegularExpression.hxx"
|
|
|
|
|
|
|
|
static const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" };
|
|
|
|
static const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" };
|
|
|
|
|
|
|
|
cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile(
|
|
|
|
const std::string& filename)
|
|
|
|
{
|
|
|
|
cmsys::ifstream fin(filename.c_str());
|
|
|
|
if (!fin) {
|
|
|
|
return ReadFileResult::FILE_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
Json::Value root;
|
|
|
|
Json::CharReaderBuilder builder;
|
|
|
|
if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
|
|
|
|
return ReadFileResult::JSON_PARSE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!root.isObject()) {
|
|
|
|
return ReadFileResult::INVALID_ROOT;
|
|
|
|
}
|
|
|
|
|
|
|
|
int majorVersion = 1;
|
|
|
|
int minorVersion = 0;
|
|
|
|
if (root.isMember("version")) {
|
|
|
|
auto const& version = root["version"];
|
|
|
|
if (version.isObject()) {
|
|
|
|
if (!version.isMember("major") || !version.isMember("minor")) {
|
|
|
|
return ReadFileResult::INVALID_VERSION;
|
|
|
|
}
|
|
|
|
auto const& major = version["major"];
|
|
|
|
auto const& minor = version["minor"];
|
|
|
|
if (!major.isInt() || !minor.isInt()) {
|
|
|
|
return ReadFileResult::INVALID_VERSION;
|
|
|
|
}
|
|
|
|
majorVersion = major.asInt();
|
|
|
|
minorVersion = minor.asInt();
|
|
|
|
} else {
|
|
|
|
return ReadFileResult::INVALID_VERSION;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return ReadFileResult::NO_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (majorVersion != 1 || minorVersion != 0) {
|
|
|
|
return ReadFileResult::UNSUPPORTED_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const& local = root["local"];
|
|
|
|
if (!local.isArray()) {
|
|
|
|
return ReadFileResult::INVALID_SOCKET_SPEC;
|
|
|
|
}
|
|
|
|
if (local.size() > 1) {
|
|
|
|
return ReadFileResult::INVALID_SOCKET_SPEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (local.empty()) {
|
|
|
|
this->LocalSocket.Resources.clear();
|
|
|
|
return ReadFileResult::READ_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const& localSocket = local[0];
|
|
|
|
if (!localSocket.isObject()) {
|
|
|
|
return ReadFileResult::INVALID_SOCKET_SPEC;
|
|
|
|
}
|
|
|
|
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>> resources;
|
|
|
|
cmsys::RegularExpressionMatch match;
|
|
|
|
for (auto const& key : localSocket.getMemberNames()) {
|
|
|
|
if (IdentifierRegex.find(key.c_str(), match)) {
|
|
|
|
auto const& value = localSocket[key];
|
|
|
|
auto& r = resources[key];
|
|
|
|
if (value.isArray()) {
|
|
|
|
for (auto const& item : value) {
|
|
|
|
if (item.isObject()) {
|
|
|
|
cmCTestResourceSpec::Resource resource;
|
|
|
|
|
|
|
|
if (!item.isMember("id")) {
|
|
|
|
return ReadFileResult::INVALID_RESOURCE;
|
|
|
|
}
|
|
|
|
auto const& id = item["id"];
|
|
|
|
if (!id.isString()) {
|
|
|
|
return ReadFileResult::INVALID_RESOURCE;
|
|
|
|
}
|
|
|
|
resource.Id = id.asString();
|
|
|
|
if (!IdRegex.find(resource.Id.c_str(), match)) {
|
|
|
|
return ReadFileResult::INVALID_RESOURCE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item.isMember("slots")) {
|
|
|
|
auto const& capacity = item["slots"];
|
|
|
|
if (!capacity.isConvertibleTo(Json::uintValue)) {
|
|
|
|
return ReadFileResult::INVALID_RESOURCE;
|
|
|
|
}
|
|
|
|
resource.Capacity = capacity.asUInt();
|
|
|
|
} else {
|
|
|
|
resource.Capacity = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
r.push_back(resource);
|
|
|
|
} else {
|
|
|
|
return ReadFileResult::INVALID_RESOURCE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return ReadFileResult::INVALID_RESOURCE_TYPE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this->LocalSocket.Resources = std::move(resources);
|
|
|
|
return ReadFileResult::READ_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* cmCTestResourceSpec::ResultToString(ReadFileResult result)
|
|
|
|
{
|
|
|
|
switch (result) {
|
|
|
|
case ReadFileResult::READ_OK:
|
|
|
|
return "OK";
|
|
|
|
|
|
|
|
case ReadFileResult::FILE_NOT_FOUND:
|
|
|
|
return "File not found";
|
|
|
|
|
|
|
|
case ReadFileResult::JSON_PARSE_ERROR:
|
|
|
|
return "JSON parse error";
|
|
|
|
|
|
|
|
case ReadFileResult::INVALID_ROOT:
|
|
|
|
return "Invalid root object";
|
|
|
|
|
|
|
|
case ReadFileResult::NO_VERSION:
|
|
|
|
return "No version specified";
|
|
|
|
|
|
|
|
case ReadFileResult::INVALID_VERSION:
|
|
|
|
return "Invalid version object";
|
|
|
|
|
|
|
|
case ReadFileResult::UNSUPPORTED_VERSION:
|
|
|
|
return "Unsupported version";
|
|
|
|
|
|
|
|
case ReadFileResult::INVALID_SOCKET_SPEC:
|
|
|
|
return "Invalid socket object";
|
|
|
|
|
|
|
|
case ReadFileResult::INVALID_RESOURCE_TYPE:
|
|
|
|
return "Invalid resource type object";
|
|
|
|
|
|
|
|
case ReadFileResult::INVALID_RESOURCE:
|
|
|
|
return "Invalid resource object";
|
|
|
|
|
|
|
|
default:
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::operator==(const cmCTestResourceSpec& other) const
|
|
|
|
{
|
|
|
|
return this->LocalSocket == other.LocalSocket;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::operator!=(const cmCTestResourceSpec& other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::Socket::operator==(
|
|
|
|
const cmCTestResourceSpec::Socket& other) const
|
|
|
|
{
|
|
|
|
return this->Resources == other.Resources;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::Socket::operator!=(
|
|
|
|
const cmCTestResourceSpec::Socket& other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::Resource::operator==(
|
|
|
|
const cmCTestResourceSpec::Resource& other) const
|
|
|
|
{
|
|
|
|
return this->Id == other.Id && this->Capacity == other.Capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::Resource::operator!=(
|
|
|
|
const cmCTestResourceSpec::Resource& other) const
|
|
|
|
{
|
|
|
|
return !(*this == other);
|
|
|
|
}
|