1 // Copyright 2012 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 /// Expected header in the test program list.
43 #define TP_LIST_HEADER "Content-Type: application/X-atf-tp; version=\"1\""
46 /// Same as fgets, but removes any trailing newline from the output string.
48 /// \param [out] str Pointer to the output buffer.
49 /// \param size Length of the output buffer.
50 /// \param [in,out] stream File from which to read the line.
52 /// \return A pointer to the output buffer if successful; otherwise NULL.
54 fgets_no_newline(char* str
, int size
, FILE* stream
)
56 char* result
= fgets(str
, size
, stream
);
58 const size_t length
= strlen(str
);
59 if (length
> 0 && str
[length
- 1] == '\n')
60 str
[length
- 1] = '\0';
66 /// Generates an error for the case where fgets() returns NULL.
68 /// \param input Stream on which fgets() returned an error.
69 /// \param message Error message.
71 /// \return An error object with the error message and any relevant details.
73 fgets_error(FILE* input
, const char* message
)
76 return kyua_generic_error_new("%s: unexpected EOF", message
);
78 assert(ferror(input
));
79 return kyua_libc_error_new(errno
, "%s", message
);
84 /// Reads the header of the test cases list.
86 /// The header does not carry any useful information, so all this function does
87 /// is ensure the header is valid.
89 /// \param [in,out] input File from which to read the header.
91 /// \return OK if the header is valid; an error if it is not.
93 parse_header(FILE* input
)
95 char line
[80]; // It's ugly to have a limit, but it's easier this way.
97 if (fgets_no_newline(line
, sizeof(line
), input
) == NULL
)
98 return fgets_error(input
, "fgets failed to read test cases list "
100 if (strcmp(line
, TP_LIST_HEADER
) != 0)
101 return kyua_generic_error_new("Invalid test cases list header '%s'",
104 if (fgets_no_newline(line
, sizeof(line
), input
) == NULL
)
105 return fgets_error(input
, "fgets failed to read test cases list "
107 if (strcmp(line
, "") != 0)
108 return kyua_generic_error_new("Incomplete test cases list header");
110 return kyua_error_ok();
114 /// Looks for the first occurrence of any of the specified delimiters.
116 /// \param container String in which to look for the delimiters.
117 /// \param delimiters List of delimiters to look for.
119 /// \return A pointer to the first occurrence of the delimiter, or NULL if
122 find_first_of(char* container
, const char* delimiters
)
124 char* ptr
= container
;
125 while (*ptr
!= '\0') {
126 if (strchr(delimiters
, *ptr
) != NULL
)
134 /// Prints a string within single quotes, with proper escaping.
136 /// \param [in,out] line The line to be printed. This is a non-const pointer
137 /// and the input string is modified to simplify tokenization.
138 /// \param [in,out] output Buffer onto which to write the quoted string.
139 /// \param surrounding If true, surround the printed value with single quotes.
141 print_quoted(char* line
, FILE* output
, const bool surrounding
)
144 fprintf(output
, "'");
147 while ((quoteptr
= find_first_of(line
, "\'\\")) != NULL
) {
148 const char quote
= *quoteptr
;
150 fprintf(output
, "%s\\%c", line
, quote
);
155 fprintf(output
, "%s'", line
);
157 fprintf(output
, "%s", line
);
161 /// Parses a property from the test cases list.
163 /// The property is of the form "name: value", where the value extends to the
164 /// end of the line without quotations.
166 /// \param [in,out] line The line to be parsed. This is a non-const pointer
167 /// and the input string is modified to simplify tokenization.
168 /// \param [out] key The name of the property if the parsing succeeds. This
169 /// is a pointer within the input line.
170 /// \param [out] value The value of the property if the parsing succeeds. This
171 /// is a pointer within the input line.
173 /// \return OK if the line contains a valid property; an error otherwise.
174 /// In case of success, both key and value are updated.
176 parse_property(char* line
, char** const key
, char** const value
)
178 char* delim
= strstr(line
, ": ");
180 return kyua_generic_error_new("Invalid property '%s'", line
);
181 *delim
= '\0'; *(delim
+ 1) = '\0';
185 return kyua_error_ok();
189 /// Static value to denote an error in the return of rewrite_property;
190 static const char* rewrite_error
= "ERROR";
193 /// Converts the name of an ATF property to a Kyua generic property.
195 /// \param name The name of the ATF property to process.
197 /// \return The name of the corresponding Kyua property if the input property is
198 /// valid; NULL if the property has a custom name that has to be handled in the
199 /// parent; or rewrite_error if the property is invalid. If this returns
200 /// rewrite_error, it's OK to pointer-compare the return value to the static
201 /// symbol for equality.
203 rewrite_property(const char* name
)
205 if (strcmp(name
, "descr") == 0)
206 return "description";
207 else if (strcmp(name
, "has.cleanup") == 0)
208 return "has_cleanup";
209 else if (strcmp(name
, "require.arch") == 0)
210 return "allowed_architectures";
211 else if (strcmp(name
, "require.config") == 0)
212 return "required_configs";
213 else if (strcmp(name
, "require.files") == 0)
214 return "required_files";
215 else if (strcmp(name
, "require.machine") == 0)
216 return "allowed_platforms";
217 else if (strcmp(name
, "require.memory") == 0)
218 return "required_memory";
219 else if (strcmp(name
, "require.progs") == 0)
220 return "required_programs";
221 else if (strcmp(name
, "require.user") == 0)
222 return "required_user";
223 else if (strcmp(name
, "timeout") == 0)
225 else if (strlen(name
) > 2 && name
[0] == 'X' && name
[1] == '-')
228 return rewrite_error
;
232 /// Parses a single test case and writes it to the output.
234 /// This has to be called after the ident property has been read, and takes care
235 /// of reading the rest of the test case and printing the parsed result.
237 /// Be aware that this consumes the newline after the test case. The caller
238 /// should not look for it.
240 /// \param [in,out] input File from which to read the header.
241 /// \param [in,out] output File to which to write the parsed test case.
242 /// \param [in,out] name The name of the test case. This is a non-const pointer
243 /// and the input string is modified to simplify tokenization.
245 /// \return OK if the parsing succeeds; an error otherwise.
247 parse_test_case(FILE* input
, FILE* output
, char* name
)
250 char line
[1024]; // It's ugly to have a limit, but it's easier this way.
252 fprintf(output
, "test_case{name=");
253 print_quoted(name
, output
, true);
255 error
= kyua_error_ok();
256 while (!kyua_error_is_set(error
) &&
257 fgets_no_newline(line
, sizeof(line
), input
) != NULL
&&
258 strcmp(line
, "") != 0) {
259 char* key
; char* value
;
261 /* LSC: -Werror=maybe-uninitialized, with -O3 */
263 #endif /* defined(__minix) */
264 error
= parse_property(line
, &key
, &value
);
265 if (!kyua_error_is_set(error
)) {
266 const char* out_key
= rewrite_property(key
);
267 if (out_key
== rewrite_error
) {
268 error
= kyua_generic_error_new("Unknown ATF property %s", key
);
269 } else if (out_key
== NULL
) {
270 fprintf(output
, ", ['custom.");
271 print_quoted(key
, output
, false);
272 fprintf(output
, "']=");
273 print_quoted(value
, output
, true);
275 fprintf(output
, ", %s=", out_key
);
276 print_quoted(value
, output
, true);
281 fprintf(output
, "}\n");
287 /// Rewrites the test cases list from the input to the output.
289 /// \param [in,out] input Stream from which to read the test program's test
290 /// cases list. The current location must be after the header and at the
291 /// first identifier (if any).
292 /// \param [out] output Stream to which to write the generic list.
294 /// \return An error object.
296 parse_tests(FILE* input
, FILE* output
)
298 char line
[512]; // It's ugly to have a limit, but it's easier this way.
300 if (fgets_no_newline(line
, sizeof(line
), input
) == NULL
) {
301 return fgets_error(input
, "Empty test cases list");
307 char* key
; char* value
;
309 /* LSC: -Werror=maybe-uninitialized, with -O3 */
311 #endif /* defined(__minix) */
312 error
= parse_property(line
, &key
, &value
);
313 if (kyua_error_is_set(error
))
316 if (strcmp(key
, "ident") == 0) {
317 error
= parse_test_case(input
, output
, value
);
319 error
= kyua_generic_error_new("Expected ident property, got %s",
322 } while (!kyua_error_is_set(error
) &&
323 fgets_no_newline(line
, sizeof(line
), input
) != NULL
);
325 if (!kyua_error_is_set(error
)) {
327 error
= kyua_libc_error_new(errno
, "fgets failed");
336 /// Reads an ATF test cases list and prints a Kyua definition.
338 /// \param fd A file descriptor from which to read the test cases list of a test
339 /// program. Should be connected to the stdout of the latter. This
340 /// function grabs ownership of the descriptor and releases it in all cases.
341 /// \param [in,out] output File to which to write the Kyua definition.
343 /// \return OK if the parsing succeeds; an error otherwise. Note that, if there
344 /// is an error, the output may not be consistent and should not be used.
346 atf_list_parse(const int fd
, FILE* output
)
350 FILE* input
= fdopen(fd
, "r");
352 error
= kyua_libc_error_new(errno
, "fdopen(%d) failed", fd
);
355 error
= parse_header(input
);
356 if (!kyua_error_is_set(error
)) {
357 error
= parse_tests(input
, output
);