1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCTestResourceSpec.h"
4
5 #include <functional>
6 #include <map>
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include <cmext/string_view>
12
13 #include <cm3p/json/reader.h>
14 #include <cm3p/json/value.h>
15
16 #include "cmsys/FStream.hxx"
17 #include "cmsys/RegularExpression.hxx"
18
19 #include "cmJSONHelpers.h"
20
21 namespace {
22 const cmsys::RegularExpression IdentifierRegex{ "^[a-z_][a-z0-9_]*$" };
23 const cmsys::RegularExpression IdRegex{ "^[a-z0-9_]+$" };
24
25 struct Version
26 {
27 int Major = 1;
28 int Minor = 0;
29 };
30
31 struct TopVersion
32 {
33 struct Version Version;
34 };
35
36 auto const VersionFieldHelper =
37 cmJSONIntHelper<cmCTestResourceSpec::ReadFileResult>(
38 cmCTestResourceSpec::ReadFileResult::READ_OK,
39 cmCTestResourceSpec::ReadFileResult::INVALID_VERSION);
40
41 auto const VersionHelper =
42 cmJSONRequiredHelper<Version, cmCTestResourceSpec::ReadFileResult>(
43 cmCTestResourceSpec::ReadFileResult::NO_VERSION,
44 cmJSONObjectHelper<Version, cmCTestResourceSpec::ReadFileResult>(
45 cmCTestResourceSpec::ReadFileResult::READ_OK,
46 cmCTestResourceSpec::ReadFileResult::INVALID_VERSION)
47 .Bind("major"_s, &Version::Major, VersionFieldHelper)
48 .Bind("minor"_s, &Version::Minor, VersionFieldHelper));
49
50 auto const RootVersionHelper =
51 cmJSONObjectHelper<TopVersion, cmCTestResourceSpec::ReadFileResult>(
52 cmCTestResourceSpec::ReadFileResult::READ_OK,
53 cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
54 .Bind("version"_s, &TopVersion::Version, VersionHelper, false);
55
ResourceIdHelper(std::string & out,const Json::Value * value)56 cmCTestResourceSpec::ReadFileResult ResourceIdHelper(std::string& out,
57 const Json::Value* value)
58 {
59 auto result = cmJSONStringHelper(
60 cmCTestResourceSpec::ReadFileResult::READ_OK,
61 cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)(out, value);
62 if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
63 return result;
64 }
65 cmsys::RegularExpressionMatch match;
66 if (!IdRegex.find(out.c_str(), match)) {
67 return cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE;
68 }
69 return cmCTestResourceSpec::ReadFileResult::READ_OK;
70 }
71
72 auto const ResourceHelper =
73 cmJSONObjectHelper<cmCTestResourceSpec::Resource,
74 cmCTestResourceSpec::ReadFileResult>(
75 cmCTestResourceSpec::ReadFileResult::READ_OK,
76 cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE)
77 .Bind("id"_s, &cmCTestResourceSpec::Resource::Id, ResourceIdHelper)
78 .Bind("slots"_s, &cmCTestResourceSpec::Resource::Capacity,
79 cmJSONUIntHelper(
80 cmCTestResourceSpec::ReadFileResult::READ_OK,
81 cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE, 1),
82 false);
83
84 auto const ResourceListHelper =
85 cmJSONVectorHelper<cmCTestResourceSpec::Resource,
86 cmCTestResourceSpec::ReadFileResult>(
87 cmCTestResourceSpec::ReadFileResult::READ_OK,
88 cmCTestResourceSpec::ReadFileResult::INVALID_RESOURCE_TYPE,
89 ResourceHelper);
90
91 auto const ResourceMapHelper =
92 cmJSONMapFilterHelper<std::vector<cmCTestResourceSpec::Resource>,
93 cmCTestResourceSpec::ReadFileResult>(
94 cmCTestResourceSpec::ReadFileResult::READ_OK,
95 cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC,
__anon728adff60202(const std::string& key) 96 ResourceListHelper, [](const std::string& key) -> bool {
97 cmsys::RegularExpressionMatch match;
98 return IdentifierRegex.find(key.c_str(), match);
99 });
100
101 auto const SocketSetHelper = cmJSONVectorHelper<
102 std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>(
103 cmCTestResourceSpec::ReadFileResult::READ_OK,
104 cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, ResourceMapHelper);
105
SocketHelper(cmCTestResourceSpec::Socket & out,const Json::Value * value)106 cmCTestResourceSpec::ReadFileResult SocketHelper(
107 cmCTestResourceSpec::Socket& out, const Json::Value* value)
108 {
109 std::vector<
110 std::map<std::string, std::vector<cmCTestResourceSpec::Resource>>>
111 sockets;
112 cmCTestResourceSpec::ReadFileResult result = SocketSetHelper(sockets, value);
113 if (result != cmCTestResourceSpec::ReadFileResult::READ_OK) {
114 return result;
115 }
116 if (sockets.size() > 1) {
117 return cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC;
118 }
119 if (sockets.empty()) {
120 out.Resources.clear();
121 } else {
122 out.Resources = std::move(sockets[0]);
123 }
124 return cmCTestResourceSpec::ReadFileResult::READ_OK;
125 }
126
127 auto const LocalRequiredHelper =
128 cmJSONRequiredHelper<cmCTestResourceSpec::Socket,
129 cmCTestResourceSpec::ReadFileResult>(
130 cmCTestResourceSpec::ReadFileResult::INVALID_SOCKET_SPEC, SocketHelper);
131
132 auto const RootHelper =
133 cmJSONObjectHelper<cmCTestResourceSpec, cmCTestResourceSpec::ReadFileResult>(
134 cmCTestResourceSpec::ReadFileResult::READ_OK,
135 cmCTestResourceSpec::ReadFileResult::INVALID_ROOT)
136 .Bind("local", &cmCTestResourceSpec::LocalSocket, LocalRequiredHelper,
137 false);
138 }
139
ReadFromJSONFile(const std::string & filename)140 cmCTestResourceSpec::ReadFileResult cmCTestResourceSpec::ReadFromJSONFile(
141 const std::string& filename)
142 {
143 cmsys::ifstream fin(filename.c_str());
144 if (!fin) {
145 return ReadFileResult::FILE_NOT_FOUND;
146 }
147
148 Json::Value root;
149 Json::CharReaderBuilder builder;
150 if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
151 return ReadFileResult::JSON_PARSE_ERROR;
152 }
153
154 TopVersion version;
155 ReadFileResult result;
156 if ((result = RootVersionHelper(version, &root)) !=
157 ReadFileResult::READ_OK) {
158 return result;
159 }
160 if (version.Version.Major != 1 || version.Version.Minor != 0) {
161 return ReadFileResult::UNSUPPORTED_VERSION;
162 }
163
164 return RootHelper(*this, &root);
165 }
166
ResultToString(ReadFileResult result)167 const char* cmCTestResourceSpec::ResultToString(ReadFileResult result)
168 {
169 switch (result) {
170 case ReadFileResult::READ_OK:
171 return "OK";
172
173 case ReadFileResult::FILE_NOT_FOUND:
174 return "File not found";
175
176 case ReadFileResult::JSON_PARSE_ERROR:
177 return "JSON parse error";
178
179 case ReadFileResult::INVALID_ROOT:
180 return "Invalid root object";
181
182 case ReadFileResult::NO_VERSION:
183 return "No version specified";
184
185 case ReadFileResult::INVALID_VERSION:
186 return "Invalid version object";
187
188 case ReadFileResult::UNSUPPORTED_VERSION:
189 return "Unsupported version";
190
191 case ReadFileResult::INVALID_SOCKET_SPEC:
192 return "Invalid socket object";
193
194 case ReadFileResult::INVALID_RESOURCE_TYPE:
195 return "Invalid resource type object";
196
197 case ReadFileResult::INVALID_RESOURCE:
198 return "Invalid resource object";
199
200 default:
201 return "Unknown";
202 }
203 }
204
operator ==(const cmCTestResourceSpec & other) const205 bool cmCTestResourceSpec::operator==(const cmCTestResourceSpec& other) const
206 {
207 return this->LocalSocket == other.LocalSocket;
208 }
209
operator !=(const cmCTestResourceSpec & other) const210 bool cmCTestResourceSpec::operator!=(const cmCTestResourceSpec& other) const
211 {
212 return !(*this == other);
213 }
214
operator ==(const cmCTestResourceSpec::Socket & other) const215 bool cmCTestResourceSpec::Socket::operator==(
216 const cmCTestResourceSpec::Socket& other) const
217 {
218 return this->Resources == other.Resources;
219 }
220
operator !=(const cmCTestResourceSpec::Socket & other) const221 bool cmCTestResourceSpec::Socket::operator!=(
222 const cmCTestResourceSpec::Socket& other) const
223 {
224 return !(*this == other);
225 }
226
operator ==(const cmCTestResourceSpec::Resource & other) const227 bool cmCTestResourceSpec::Resource::operator==(
228 const cmCTestResourceSpec::Resource& other) const
229 {
230 return this->Id == other.Id && this->Capacity == other.Capacity;
231 }
232
operator !=(const cmCTestResourceSpec::Resource & other) const233 bool cmCTestResourceSpec::Resource::operator!=(
234 const cmCTestResourceSpec::Resource& other) const
235 {
236 return !(*this == other);
237 }
238