|
|
|
/* 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/reader.h>
|
|
|
|
#include <cm3p/json/value.h>
|
|
|
|
|
|
|
|
#include "cmsys/FStream.hxx"
|
|
|
|
#include "cmsys/RegularExpression.hxx"
|
|
|
|
|
|
|
|
#include "cmJSONHelpers.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
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 =
|
|
|
|
cmJSONIntHelper<cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_VERSION);
|
|
|
|
|
|
|
|
auto const VersionHelper =
|
|
|
|
cmJSONRequiredHelper<Version, cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::NO_VERSION,
|
|
|
|
cmJSONObjectHelper<Version, cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_VERSION)
|
|
|
|
.Bind("major"_s, &Version::Major, VersionFieldHelper)
|
|
|
|
.Bind("minor"_s, &Version::Minor, VersionFieldHelper));
|
|
|
|
|
|
|
|
auto const RootVersionHelper =
|
|
|
|
cmJSONObjectHelper<TopVersion, cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
|
|
|
|
.Bind("version"_s, &TopVersion::Version, VersionHelper, false);
|
|
|
|
|
|
|
|
cmCTestResourceSpec::ReadFileResult ResourceIdHelper(std::string& out,
|
|
|
|
const Json::Value* value)
|
|
|
|
{
|
|
|
|
auto result = cmJSONStringHelper(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)(out, value);
|
|
|
|
if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
cmsys::RegularExpressionMatch match;
|
|
|
|
if (!IdRegex.find(out.c_str(), match)) {
|
|
|
|
return cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE;
|
|
|
|
}
|
|
|
|
return cmCTestResourceSpec::ReadFileResult::READ_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const ResourceHelper =
|
|
|
|
cmJSONObjectHelper<cmCTestResourceSpec::Resource,
|
|
|
|
cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)
|
|
|
|
.Bind("id"_s, &cmCTestResourceSpec::Resource::Id, ResourceIdHelper)
|
|
|
|
.Bind("slots"_s, &cmCTestResourceSpec::Resource::Capacity,
|
|
|
|
cmJSONUIntHelper(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, 1),
|
|
|
|
false);
|
|
|
|
|
|
|
|
auto const ResourceListHelper =
|
|
|
|
cmJSONVectorHelper<cmCTestResourceSpec::Resource,
|
|
|
|
cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE,
|
|
|
|
ResourceHelper);
|
|
|
|
|
|
|
|
auto const ResourceMapHelper =
|
|
|
|
cmJSONMapFilterHelper<std::vector<cmCTestResourceSpec::Resource>,
|
|
|
|
cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
|
|
|
|
ResourceListHelper, [](const std::string& key) -> bool {
|
|
|
|
cmsys::RegularExpressionMatch match;
|
|
|
|
return IdentifierRegex.find(key.c_str(), match);
|
|
|
|
});
|
|
|
|
|
|
|
|
auto const SocketSetHelper = cmJSONVectorHelper<
|
|
|
|
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, ResourceMapHelper);
|
|
|
|
|
|
|
|
cmCTestResourceSpec::ReadFileResult SocketHelper(
|
|
|
|
cmCTestResourceSpec::Socket& out, const Json::Value* value)
|
|
|
|
{
|
|
|
|
std::vector<
|
|
|
|
std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>
|
|
|
|
sockets;
|
|
|
|
cmCTestResourceSpec::ReadFileResult result = SocketSetHelper(sockets, value);
|
|
|
|
if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (sockets.size() > 1) {
|
|
|
|
return cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC;
|
|
|
|
}
|
|
|
|
if (sockets.empty()) {
|
|
|
|
out.Resources.clear();
|
|
|
|
} else {
|
|
|
|
out.Resources = std::move(sockets[0]);
|
|
|
|
}
|
|
|
|
return cmCTestResourceSpec::ReadFileResult::READ_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const LocalRequiredHelper =
|
|
|
|
cmJSONRequiredHelper<cmCTestResourceSpec::Socket,
|
|
|
|
cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, SocketHelper);
|
|
|
|
|
|
|
|
auto const RootHelper =
|
|
|
|
cmJSONObjectHelper<cmCTestResourceSpec, cmCTestResourceSpec::ReadFileResult>(
|
|
|
|
cmCTestResourceSpec::ReadFileResult::READ_OK,
|
|
|
|
cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
|
|
|
|
.Bind("local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TopVersion version;
|
|
|
|
ReadFileResult result;
|
|
|
|
if ((result = RootVersionHelper(version, &root)) !=
|
|
|
|
ReadFileResult::READ_OK) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
if (version.Version.Major != 1 || version.Version.Minor != 0) {
|
|
|
|
return ReadFileResult::UNSUPPORTED_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
return RootHelper(*this, &root);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|