• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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 
17 #include <ctype.h>
18 #include <errno.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <openssl/err.h>
24 
25 #include "stl_compat.h"
26 
27 
FileTest(const char * path)28 FileTest::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 
~FileTest()35 FileTest::~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.
FindDelimiter(const char * str)43 static 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.
StripSpace(const char * str,size_t len)55 static 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 
ReadNext()67 FileTest::ReadResult FileTest::ReadNext() {
68   // If the previous test had unused attributes or block, it is an error.
69   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   }
75   if (!block_.empty() && !used_block_) {
76     PrintLine("Unused block");
77     return kReadError;
78   }
79 
80   ClearTest();
81 
82   bool in_block = false;
83   while (true) {
84     // Read the next line.
85     char buf[4096];
86     if (fgets(buf, sizeof(buf), file_) == nullptr) {
87       if (feof(file_)) {
88         if (in_block) {
89           fprintf(stderr, "Unterminated block.\n");
90           return kReadError;
91         }
92         // EOF is a valid terminator for a test.
93         return start_line_ > 0 ? kReadSuccess : kReadEOF;
94       }
95       fprintf(stderr, "Error reading from input.\n");
96       return kReadError;
97     }
98 
99     line_++;
100     size_t len = strlen(buf);
101     // Check for truncation.
102     if (len > 0 && buf[len - 1] != '\n' && !feof(file_)) {
103       fprintf(stderr, "Line %u too long.\n", line_);
104       return kReadError;
105     }
106 
107     bool is_delimiter = strncmp(buf, "---", 3) == 0;
108     if (in_block) {
109       block_ += buf;
110       if (is_delimiter) {
111         // Ending the block completes the test.
112         return kReadSuccess;
113       }
114     } else if (is_delimiter) {
115       if (start_line_ == 0) {
116         fprintf(stderr, "Line %u: Unexpected block.\n", line_);
117         return kReadError;
118       }
119       in_block = true;
120       block_ += buf;
121     } else if (buf[0] == '\n' || buf[0] == '\0') {
122       // Empty lines delimit tests.
123       if (start_line_ > 0) {
124         return kReadSuccess;
125       }
126     } else if (buf[0] != '#') {  // Comment lines are ignored.
127       // Parse the line as an attribute.
128       const char *delimiter = FindDelimiter(buf);
129       if (delimiter == nullptr) {
130         fprintf(stderr, "Line %u: Could not parse attribute.\n", line_);
131       }
132       std::string key = StripSpace(buf, delimiter - buf);
133       std::string value = StripSpace(delimiter + 1,
134                                      buf + len - delimiter - 1);
135 
136       unused_attributes_.insert(key);
137       attributes_[key] = value;
138       if (start_line_ == 0) {
139         // This is the start of a test.
140         type_ = key;
141         parameter_ = value;
142         start_line_ = line_;
143       }
144     }
145   }
146 }
147 
PrintLine(const char * format,...)148 void FileTest::PrintLine(const char *format, ...) {
149   va_list args;
150   va_start(args, format);
151 
152   fprintf(stderr, "Line %u: ", start_line_);
153   vfprintf(stderr, format, args);
154   fprintf(stderr, "\n");
155 
156   va_end(args);
157 }
158 
GetType()159 const std::string &FileTest::GetType() {
160   OnKeyUsed(type_);
161   return type_;
162 }
163 
GetParameter()164 const std::string &FileTest::GetParameter() {
165   OnKeyUsed(type_);
166   return parameter_;
167 }
168 
GetBlock()169 const std::string &FileTest::GetBlock() {
170   used_block_ = true;
171   return block_;
172 }
173 
HasAttribute(const std::string & key)174 bool FileTest::HasAttribute(const std::string &key) {
175   OnKeyUsed(key);
176   return attributes_.count(key) > 0;
177 }
178 
GetAttribute(std::string * out_value,const std::string & key)179 bool FileTest::GetAttribute(std::string *out_value, const std::string &key) {
180   OnKeyUsed(key);
181   auto iter = attributes_.find(key);
182   if (iter == attributes_.end()) {
183     PrintLine("Missing attribute '%s'.", key.c_str());
184     return false;
185   }
186   *out_value = iter->second;
187   return true;
188 }
189 
GetAttributeOrDie(const std::string & key)190 const std::string &FileTest::GetAttributeOrDie(const std::string &key) {
191   if (!HasAttribute(key)) {
192     abort();
193   }
194   return attributes_[key];
195 }
196 
FromHexDigit(uint8_t * out,char c)197 static bool FromHexDigit(uint8_t *out, char c) {
198   if ('0' <= c && c <= '9') {
199     *out = c - '0';
200     return true;
201   }
202   if ('a' <= c && c <= 'f') {
203     *out = c - 'a' + 10;
204     return true;
205   }
206   if ('A' <= c && c <= 'F') {
207     *out = c - 'A' + 10;
208     return true;
209   }
210   return false;
211 }
212 
GetBytes(std::vector<uint8_t> * out,const std::string & key)213 bool FileTest::GetBytes(std::vector<uint8_t> *out, const std::string &key) {
214   std::string value;
215   if (!GetAttribute(&value, key)) {
216     return false;
217   }
218 
219   if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') {
220     out->assign(value.begin() + 1, value.end() - 1);
221     return true;
222   }
223 
224   if (value.size() % 2 != 0) {
225     PrintLine("Error decoding value: %s", value.c_str());
226     return false;
227   }
228   out->reserve(value.size() / 2);
229   for (size_t i = 0; i < value.size(); i += 2) {
230     uint8_t hi, lo;
231     if (!FromHexDigit(&hi, value[i]) || !FromHexDigit(&lo, value[i+1])) {
232       PrintLine("Error decoding value: %s", value.c_str());
233       return false;
234     }
235     out->push_back((hi << 4) | lo);
236   }
237   return true;
238 }
239 
EncodeHex(const uint8_t * in,size_t in_len)240 static std::string EncodeHex(const uint8_t *in, size_t in_len) {
241   static const char kHexDigits[] = "0123456789abcdef";
242   std::string ret;
243   ret.reserve(in_len * 2);
244   for (size_t i = 0; i < in_len; i++) {
245     ret += kHexDigits[in[i] >> 4];
246     ret += kHexDigits[in[i] & 0xf];
247   }
248   return ret;
249 }
250 
ExpectBytesEqual(const uint8_t * expected,size_t expected_len,const uint8_t * actual,size_t actual_len)251 bool FileTest::ExpectBytesEqual(const uint8_t *expected, size_t expected_len,
252                                 const uint8_t *actual, size_t actual_len) {
253   if (expected_len == actual_len &&
254       memcmp(expected, actual, expected_len) == 0) {
255     return true;
256   }
257 
258   std::string expected_hex = EncodeHex(expected, expected_len);
259   std::string actual_hex = EncodeHex(actual, actual_len);
260   PrintLine("Expected: %s", expected_hex.c_str());
261   PrintLine("Actual:   %s", actual_hex.c_str());
262   return false;
263 }
264 
ClearTest()265 void FileTest::ClearTest() {
266   start_line_ = 0;
267   type_.clear();
268   parameter_.clear();
269   attributes_.clear();
270   block_.clear();
271   unused_attributes_.clear();
272   used_block_ = false;
273 }
274 
OnKeyUsed(const std::string & key)275 void FileTest::OnKeyUsed(const std::string &key) {
276   unused_attributes_.erase(key);
277 }
278 
FileTestMain(bool (* run_test)(FileTest * t,void * arg),void * arg,const char * path)279 int FileTestMain(bool (*run_test)(FileTest *t, void *arg), void *arg,
280                  const char *path) {
281   FileTest t(path);
282   if (!t.is_open()) {
283     return 1;
284   }
285 
286   bool failed = false;
287   while (true) {
288     FileTest::ReadResult ret = t.ReadNext();
289     if (ret == FileTest::kReadError) {
290       return 1;
291     } else if (ret == FileTest::kReadEOF) {
292       break;
293     }
294 
295     bool result = run_test(&t, arg);
296     if (t.HasAttribute("Error")) {
297       if (result) {
298         t.PrintLine("Operation unexpectedly succeeded.");
299         failed = true;
300         continue;
301       }
302       uint32_t err = ERR_peek_error();
303       if (ERR_reason_error_string(err) != t.GetAttributeOrDie("Error")) {
304         t.PrintLine("Unexpected error; wanted '%s', got '%s'.",
305                      t.GetAttributeOrDie("Error").c_str(),
306                      ERR_reason_error_string(err));
307         failed = true;
308         continue;
309       }
310       ERR_clear_error();
311     } else if (!result) {
312       // In case the test itself doesn't print output, print something so the
313       // line number is reported.
314       t.PrintLine("Test failed");
315       ERR_print_errors_fp(stderr);
316       failed = true;
317       continue;
318     }
319   }
320 
321   if (failed) {
322     return 1;
323   }
324 
325   printf("PASS\n");
326   return 0;
327 }
328