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