|
|
|
/* 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 <functional>
|
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <cmext/string_view>
|
|
|
|
|
|
|
|
#include <cm3p/json/value.h>
|
|
|
|
|
|
|
|
#include "cmsys/RegularExpression.hxx"
|
|
|
|
|
|
|
|
#include "cmJSONHelpers.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
using JSONHelperBuilder = cmJSONHelperBuilder;
|
|
|
|
const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" };
|
|
|
|
const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" };
|
|
|
|
|
|
|
|
struct Version
|
|
|
|
{
|
|
|
|
int Major = 1;
|
|
|
|
int Minor = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct TopVersion
|
|
|
|
{
|
|
|
|
struct Version Version;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto const VersionFieldHelper =
|
|
|
|
JSONHelperBuilder::Int(cmCTestResourceSpecErrors::INVALID_VERSION);
|
|
|
|
|
|
|
|
auto const VersionHelper = JSONHelperBuilder::Required<Version>(
|
|
|
|
cmCTestResourceSpecErrors::NO_VERSION,
|
|
|
|
JSONHelperBuilder::Object<Version>()
|
|
|
|
.Bind("major"_s, &Version::Major, VersionFieldHelper)
|
|
|
|
.Bind("minor"_s, &Version::Minor, VersionFieldHelper));
|
|
|
|
|
|
|
|
auto const RootVersionHelper = JSONHelperBuilder::Object<TopVersion>().Bind(
|
|
|
|
"version"_s, &TopVersion::Version, VersionHelper, false);
|
|
|
|
|
|
|
|
bool ResourceIdHelper(std::string& out, const Json::Value* value,
|
|
|
|
cmJSONState* state)
|
|
|
|
{
|
|
|
|
if (!JSONHelperBuilder::String(cmCTestResourceSpecErrors::INVALID_RESOURCE)(
|
|
|
|
out, value, state)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
cmsys::RegularExpressionMatch match;
|
|
|
|
if (!IdRegex.find(out.c_str(), match)) {
|
|
|
|
cmCTestResourceSpecErrors::INVALID_RESOURCE(value, state);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const ResourceHelper =
|
|
|
|
JSONHelperBuilder::Object<cmCTestResourceSpec::Resource>()
|
|
|
|
.Bind("id"_s, &cmCTestResourceSpec::Resource::Id, ResourceIdHelper)
|
|
|
|
.Bind(
|
|
|
|
"slots"_s, &cmCTestResourceSpec::Resource::Capacity,
|
|
|
|
JSONHelperBuilder::UInt(cmCTestResourceSpecErrors::INVALID_RESOURCE, 1),
|
|
|
|
false);
|
|
|
|
|
|
|
|
auto const ResourceListHelper =
|
|
|
|
JSONHelperBuilder::Vector<cmCTestResourceSpec::Resource>(
|
|
|
|
cmCTestResourceSpecErrors::INVALID_RESOURCE_TYPE, ResourceHelper);
|
|
|
|
|
|
|
|
auto const ResourceMapHelper =
|
|
|
|
JSONHelperBuilder::MapFilter<std::vector<cmCTestResourceSpec::Resource>>(
|
|
|
|
cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC, ResourceListHelper,
|
|
|
|
[](const std::string& key) -> bool {
|
|
|
|
cmsys::RegularExpressionMatch match;
|
|
|
|
return IdentifierRegex.find(key.c_str(), match);
|
|
|
|
});
|
|
|
|
|
|
|
|
auto const SocketSetHelper = JSONHelperBuilder::Vector<
|
|
|
|
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>(
|
|
|
|
cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC, ResourceMapHelper);
|
|
|
|
|
|
|
|
bool SocketHelper(cmCTestResourceSpec::Socket& out, const Json::Value* value,
|
|
|
|
cmJSONState* state)
|
|
|
|
{
|
|
|
|
std::vector<
|
|
|
|
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>
|
|
|
|
sockets;
|
|
|
|
if (!SocketSetHelper(sockets, value, state)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (sockets.size() > 1) {
|
|
|
|
cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC(value, state);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (sockets.empty()) {
|
|
|
|
out.Resources.clear();
|
|
|
|
} else {
|
|
|
|
out.Resources = std::move(sockets[0]);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const LocalRequiredHelper =
|
|
|
|
JSONHelperBuilder::Required<cmCTestResourceSpec::Socket>(
|
|
|
|
cmCTestResourceSpecErrors::INVALID_SOCKET_SPEC, SocketHelper);
|
|
|
|
|
|
|
|
auto const RootHelper = JSONHelperBuilder::Object<cmCTestResourceSpec>().Bind(
|
|
|
|
"local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cmCTestResourceSpec::ReadFromJSONFile(const std::string& filename)
|
|
|
|
{
|
|
|
|
Json::Value root;
|
|
|
|
|
|
|
|
this->parseState = cmJSONState(filename, &root);
|
|
|
|
if (!this->parseState.errors.empty()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TopVersion version;
|
|
|
|
bool result;
|
|
|
|
if ((result = RootVersionHelper(version, &root, &parseState)) != true) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (version.Version.Major != 1 || version.Version.Minor != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return RootHelper(*this, &root, &parseState);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|