blob: d684aa09fe16b64031bdf280d209f8bd9bfa51a2 [file] [log] [blame]
David Benjamin06b94de2015-05-09 22:46:47 -04001/* Copyright (c) 2015, Google Inc.
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15#include "file_test.h"
16
Adam Langleya5ee83f2016-02-24 10:04:31 -080017#include <memory>
18
David Benjamin06b94de2015-05-09 22:46:47 -040019#include <ctype.h>
20#include <errno.h>
21#include <stdarg.h>
David Benjamin771a1382015-05-12 18:01:17 -040022#include <stdlib.h>
David Benjamin06b94de2015-05-09 22:46:47 -040023#include <string.h>
24
David Benjamin5c694e32015-05-11 15:58:08 -040025#include <openssl/err.h>
26
David Benjamin06b94de2015-05-09 22:46:47 -040027
28FileTest::FileTest(const char *path) {
29 file_ = fopen(path, "r");
30 if (file_ == nullptr) {
31 fprintf(stderr, "Could not open file %s: %s.\n", path, strerror(errno));
32 }
33}
34
35FileTest::~FileTest() {
36 if (file_ != nullptr) {
37 fclose(file_);
38 }
39}
40
41// FindDelimiter returns a pointer to the first '=' or ':' in |str| or nullptr
42// if there is none.
43static const char *FindDelimiter(const char *str) {
44 while (*str) {
45 if (*str == ':' || *str == '=') {
46 return str;
47 }
48 str++;
49 }
50 return nullptr;
51}
52
53// StripSpace returns a string containing up to |len| characters from |str| with
54// leading and trailing whitespace removed.
55static std::string StripSpace(const char *str, size_t len) {
56 // Remove leading space.
57 while (len > 0 && isspace(*str)) {
58 str++;
59 len--;
60 }
61 while (len > 0 && isspace(str[len-1])) {
62 len--;
63 }
64 return std::string(str, len);
65}
66
67FileTest::ReadResult FileTest::ReadNext() {
David Benjamin9cd7fbd2016-01-03 01:48:06 -080068 // If the previous test had unused attributes, it is an error.
David Benjamin06b94de2015-05-09 22:46:47 -040069 if (!unused_attributes_.empty()) {
70 for (const std::string &key : unused_attributes_) {
71 PrintLine("Unused attribute: %s", key.c_str());
72 }
73 return kReadError;
74 }
David Benjamin06b94de2015-05-09 22:46:47 -040075
76 ClearTest();
77
Adam Langleya5ee83f2016-02-24 10:04:31 -080078 static const size_t kBufLen = 64 + 8192*2;
79 std::unique_ptr<char[]> buf(new char[kBufLen]);
80
David Benjamin06b94de2015-05-09 22:46:47 -040081 while (true) {
82 // Read the next line.
Adam Langleya5ee83f2016-02-24 10:04:31 -080083 if (fgets(buf.get(), kBufLen, file_) == nullptr) {
David Benjamin06b94de2015-05-09 22:46:47 -040084 if (feof(file_)) {
David Benjamin06b94de2015-05-09 22:46:47 -040085 // EOF is a valid terminator for a test.
86 return start_line_ > 0 ? kReadSuccess : kReadEOF;
87 }
88 fprintf(stderr, "Error reading from input.\n");
89 return kReadError;
90 }
91
92 line_++;
Adam Langleya5ee83f2016-02-24 10:04:31 -080093 size_t len = strlen(buf.get());
David Benjamin06b94de2015-05-09 22:46:47 -040094 // Check for truncation.
95 if (len > 0 && buf[len - 1] != '\n' && !feof(file_)) {
96 fprintf(stderr, "Line %u too long.\n", line_);
97 return kReadError;
98 }
99
David Benjamin9cd7fbd2016-01-03 01:48:06 -0800100 if (buf[0] == '\n' || buf[0] == '\0') {
David Benjamin06b94de2015-05-09 22:46:47 -0400101 // Empty lines delimit tests.
102 if (start_line_ > 0) {
103 return kReadSuccess;
104 }
105 } else if (buf[0] != '#') { // Comment lines are ignored.
106 // Parse the line as an attribute.
Adam Langleya5ee83f2016-02-24 10:04:31 -0800107 const char *delimiter = FindDelimiter(buf.get());
David Benjamin06b94de2015-05-09 22:46:47 -0400108 if (delimiter == nullptr) {
109 fprintf(stderr, "Line %u: Could not parse attribute.\n", line_);
Brian Smith7b5f08e2015-08-06 10:42:49 -0400110 return kReadError;
David Benjamin06b94de2015-05-09 22:46:47 -0400111 }
Adam Langleya5ee83f2016-02-24 10:04:31 -0800112 std::string key = StripSpace(buf.get(), delimiter - buf.get());
David Benjamin06b94de2015-05-09 22:46:47 -0400113 std::string value = StripSpace(delimiter + 1,
Adam Langleya5ee83f2016-02-24 10:04:31 -0800114 buf.get() + len - delimiter - 1);
David Benjamin06b94de2015-05-09 22:46:47 -0400115
116 unused_attributes_.insert(key);
117 attributes_[key] = value;
118 if (start_line_ == 0) {
119 // This is the start of a test.
120 type_ = key;
121 parameter_ = value;
122 start_line_ = line_;
123 }
124 }
125 }
126}
127
128void FileTest::PrintLine(const char *format, ...) {
129 va_list args;
130 va_start(args, format);
131
132 fprintf(stderr, "Line %u: ", start_line_);
133 vfprintf(stderr, format, args);
134 fprintf(stderr, "\n");
135
136 va_end(args);
137}
138
139const std::string &FileTest::GetType() {
140 OnKeyUsed(type_);
141 return type_;
142}
143
144const std::string &FileTest::GetParameter() {
145 OnKeyUsed(type_);
146 return parameter_;
147}
148
David Benjamin06b94de2015-05-09 22:46:47 -0400149bool FileTest::HasAttribute(const std::string &key) {
150 OnKeyUsed(key);
151 return attributes_.count(key) > 0;
152}
153
154bool FileTest::GetAttribute(std::string *out_value, const std::string &key) {
155 OnKeyUsed(key);
156 auto iter = attributes_.find(key);
157 if (iter == attributes_.end()) {
158 PrintLine("Missing attribute '%s'.", key.c_str());
159 return false;
160 }
161 *out_value = iter->second;
162 return true;
163}
164
David Benjamin5c694e32015-05-11 15:58:08 -0400165const std::string &FileTest::GetAttributeOrDie(const std::string &key) {
166 if (!HasAttribute(key)) {
167 abort();
168 }
169 return attributes_[key];
170}
171
David Benjamin06b94de2015-05-09 22:46:47 -0400172static bool FromHexDigit(uint8_t *out, char c) {
173 if ('0' <= c && c <= '9') {
174 *out = c - '0';
175 return true;
176 }
177 if ('a' <= c && c <= 'f') {
178 *out = c - 'a' + 10;
179 return true;
180 }
181 if ('A' <= c && c <= 'F') {
182 *out = c - 'A' + 10;
183 return true;
184 }
185 return false;
186}
187
188bool FileTest::GetBytes(std::vector<uint8_t> *out, const std::string &key) {
189 std::string value;
190 if (!GetAttribute(&value, key)) {
191 return false;
192 }
193
194 if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') {
195 out->assign(value.begin() + 1, value.end() - 1);
196 return true;
197 }
198
199 if (value.size() % 2 != 0) {
200 PrintLine("Error decoding value: %s", value.c_str());
201 return false;
202 }
David Benjamine30a09e2016-01-01 01:17:30 -0500203 out->clear();
David Benjamin06b94de2015-05-09 22:46:47 -0400204 out->reserve(value.size() / 2);
205 for (size_t i = 0; i < value.size(); i += 2) {
206 uint8_t hi, lo;
207 if (!FromHexDigit(&hi, value[i]) || !FromHexDigit(&lo, value[i+1])) {
208 PrintLine("Error decoding value: %s", value.c_str());
209 return false;
210 }
211 out->push_back((hi << 4) | lo);
212 }
213 return true;
214}
215
216static std::string EncodeHex(const uint8_t *in, size_t in_len) {
217 static const char kHexDigits[] = "0123456789abcdef";
218 std::string ret;
219 ret.reserve(in_len * 2);
220 for (size_t i = 0; i < in_len; i++) {
221 ret += kHexDigits[in[i] >> 4];
222 ret += kHexDigits[in[i] & 0xf];
223 }
224 return ret;
225}
226
227bool FileTest::ExpectBytesEqual(const uint8_t *expected, size_t expected_len,
228 const uint8_t *actual, size_t actual_len) {
229 if (expected_len == actual_len &&
230 memcmp(expected, actual, expected_len) == 0) {
231 return true;
232 }
233
234 std::string expected_hex = EncodeHex(expected, expected_len);
235 std::string actual_hex = EncodeHex(actual, actual_len);
236 PrintLine("Expected: %s", expected_hex.c_str());
237 PrintLine("Actual: %s", actual_hex.c_str());
238 return false;
239}
240
241void FileTest::ClearTest() {
242 start_line_ = 0;
243 type_.clear();
244 parameter_.clear();
245 attributes_.clear();
David Benjamin06b94de2015-05-09 22:46:47 -0400246 unused_attributes_.clear();
David Benjamin06b94de2015-05-09 22:46:47 -0400247}
248
249void FileTest::OnKeyUsed(const std::string &key) {
250 unused_attributes_.erase(key);
251}
252
David Benjamin5c694e32015-05-11 15:58:08 -0400253int FileTestMain(bool (*run_test)(FileTest *t, void *arg), void *arg,
254 const char *path) {
David Benjamin06b94de2015-05-09 22:46:47 -0400255 FileTest t(path);
256 if (!t.is_open()) {
257 return 1;
258 }
259
260 bool failed = false;
261 while (true) {
262 FileTest::ReadResult ret = t.ReadNext();
263 if (ret == FileTest::kReadError) {
264 return 1;
265 } else if (ret == FileTest::kReadEOF) {
266 break;
267 }
268
David Benjamin5c694e32015-05-11 15:58:08 -0400269 bool result = run_test(&t, arg);
270 if (t.HasAttribute("Error")) {
271 if (result) {
272 t.PrintLine("Operation unexpectedly succeeded.");
273 failed = true;
274 continue;
275 }
276 uint32_t err = ERR_peek_error();
277 if (ERR_reason_error_string(err) != t.GetAttributeOrDie("Error")) {
278 t.PrintLine("Unexpected error; wanted '%s', got '%s'.",
279 t.GetAttributeOrDie("Error").c_str(),
280 ERR_reason_error_string(err));
281 failed = true;
David Benjamine30a09e2016-01-01 01:17:30 -0500282 ERR_clear_error();
David Benjamin5c694e32015-05-11 15:58:08 -0400283 continue;
284 }
285 ERR_clear_error();
286 } else if (!result) {
287 // In case the test itself doesn't print output, print something so the
288 // line number is reported.
289 t.PrintLine("Test failed");
290 ERR_print_errors_fp(stderr);
David Benjamin06b94de2015-05-09 22:46:47 -0400291 failed = true;
David Benjamin5c694e32015-05-11 15:58:08 -0400292 continue;
David Benjamin06b94de2015-05-09 22:46:47 -0400293 }
294 }
295
296 if (failed) {
297 return 1;
298 }
299
300 printf("PASS\n");
301 return 0;
302}