| /* 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); |
| } |