file_test.cc
Go to the documentation of this file.
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 <algorithm>
18 #include <utility>
19 
20 #include <assert.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <openssl/err.h>
29 
30 #include "../internal.h"
31 #include "./test_util.h"
32 
33 
34 FileTest::FileTest(std::unique_ptr<FileTest::LineReader> reader,
35  std::function<void(const std::string &)> comment_callback,
36  bool is_kas_test)
37  : reader_(std::move(reader)),
38  is_kas_test_(is_kas_test),
39  comment_callback_(std::move(comment_callback)) {}
40 
42 
43 // FindDelimiter returns a pointer to the first '=' or ':' in |str| or nullptr
44 // if there is none.
45 static const char *FindDelimiter(const char *str) {
46  while (*str) {
47  if (*str == ':' || *str == '=') {
48  return str;
49  }
50  str++;
51  }
52  return nullptr;
53 }
54 
55 // StripSpace returns a string containing up to |len| characters from |str| with
56 // leading and trailing whitespace removed.
57 static std::string StripSpace(const char *str, size_t len) {
58  // Remove leading space.
59  while (len > 0 && isspace(*str)) {
60  str++;
61  len--;
62  }
63  while (len > 0 && isspace(str[len - 1])) {
64  len--;
65  }
66  return std::string(str, len);
67 }
68 
69 static std::pair<std::string, std::string> ParseKeyValue(const char *str, const size_t len) {
70  const char *delimiter = FindDelimiter(str);
72  if (delimiter == nullptr) {
73  key = StripSpace(str, len);
74  } else {
75  key = StripSpace(str, delimiter - str);
76  value = StripSpace(delimiter + 1, str + len - delimiter - 1);
77  }
78  return {key, value};
79 }
80 
82  // If the previous test had unused attributes or instructions, it is an error.
83  if (!unused_attributes_.empty()) {
84  for (const std::string &key : unused_attributes_) {
85  PrintLine("Unused attribute: %s", key.c_str());
86  }
87  return kReadError;
88  }
89  if (!unused_instructions_.empty()) {
90  for (const std::string &key : unused_instructions_) {
91  PrintLine("Unused instruction: %s", key.c_str());
92  }
93  return kReadError;
94  }
95 
96  ClearTest();
97 
98  static const size_t kBufLen = 8192 * 4;
99  std::unique_ptr<char[]> buf(new char[kBufLen]);
100 
101  bool in_instruction_block = false;
102  is_at_new_instruction_block_ = false;
103 
104  while (true) {
105  // Read the next line.
106  switch (reader_->ReadLine(buf.get(), kBufLen)) {
107  case kReadError:
108  fprintf(stderr, "Error reading from input at line %u.\n", line_ + 1);
109  return kReadError;
110  case kReadEOF:
111  // EOF is a valid terminator for a test.
112  return start_line_ > 0 ? kReadSuccess : kReadEOF;
113  case kReadSuccess:
114  break;
115  }
116 
117  line_++;
118  size_t len = strlen(buf.get());
119  if (buf[0] == '\n' || buf[0] == '\r' || buf[0] == '\0') {
120  // Empty lines delimit tests.
121  if (start_line_ > 0) {
122  return kReadSuccess;
123  }
124  if (in_instruction_block) {
125  in_instruction_block = false;
126  // Delimit instruction block from test with a blank line.
127  current_test_ += "\r\n";
128  } else if (is_kas_test_) {
129  // KAS tests have random blank lines scattered around.
130  current_test_ += "\r\n";
131  }
132  } else if (buf[0] == '#') {
133  if (is_kas_test_ && seen_non_comment_) {
134  // KAS tests have comments after the initial comment block which need
135  // to be included in the corresponding place in the output.
136  current_test_ += std::string(buf.get());
137  } else if (comment_callback_) {
138  comment_callback_(buf.get());
139  }
140  // Otherwise ignore comments.
141  } else if (strcmp("[B.4.2 Key Pair Generation by Testing Candidates]\r\n",
142  buf.get()) == 0) {
143  // The above instruction-like line is ignored because the FIPS lab's
144  // request files are hopelessly inconsistent.
145  } else if (buf[0] == '[') { // Inside an instruction block.
146  is_at_new_instruction_block_ = true;
147  seen_non_comment_ = true;
148  if (start_line_ != 0) {
149  // Instructions should be separate blocks.
150  fprintf(stderr, "Line %u is an instruction in a test case.\n", line_);
151  return kReadError;
152  }
153  if (!in_instruction_block) {
154  ClearInstructions();
155  in_instruction_block = true;
156  }
157 
158  // Parse the line as an instruction ("[key = value]" or "[key]").
159 
160  // KAS tests contain invalid syntax.
161  std::string kv = buf.get();
162  const bool is_broken_kas_instruction =
163  is_kas_test_ &&
164  (kv == "[SHA(s) supported (Used for hashing Z): SHA512 \r\n");
165 
166  if (!is_broken_kas_instruction) {
167  kv = StripSpace(buf.get(), len);
168  if (kv[kv.size() - 1] != ']') {
169  fprintf(stderr, "Line %u, invalid instruction: '%s'\n", line_,
170  kv.c_str());
171  return kReadError;
172  }
173  } else {
174  // Just remove the newline for the broken instruction.
175  kv = kv.substr(0, kv.size() - 2);
176  }
177 
178  current_test_ += kv + "\r\n";
179  kv = std::string(kv.begin() + 1, kv.end() - 1);
180 
181  for (;;) {
182  size_t idx = kv.find(',');
183  if (idx == std::string::npos) {
184  idx = kv.size();
185  }
187  std::tie(key, value) = ParseKeyValue(kv.c_str(), idx);
188  instructions_[key] = value;
189  if (idx == kv.size())
190  break;
191  kv = kv.substr(idx + 1);
192  }
193  } else {
194  // Parsing a test case.
195  if (in_instruction_block) {
196  // Some NIST CAVP test files (TDES) have a test case immediately
197  // following an instruction block, without a separate blank line, some
198  // of the time.
199  in_instruction_block = false;
200  }
201 
202  current_test_ += std::string(buf.get(), len);
204  std::tie(key, value) = ParseKeyValue(buf.get(), len);
205 
206  // Duplicate keys are rewritten to have “/2”, “/3”, … suffixes.
207  std::string mapped_key = key;
208  // If absent, the value will be zero-initialized.
209  const size_t num_occurrences = ++attribute_count_[key];
210  if (num_occurrences > 1) {
211  mapped_key += "/" + std::to_string(num_occurrences);
212  }
213 
214  unused_attributes_.insert(mapped_key);
215  attributes_[mapped_key] = value;
216  if (start_line_ == 0) {
217  // This is the start of a test.
218  type_ = mapped_key;
219  parameter_ = value;
220  start_line_ = line_;
221  for (const auto &kv : instructions_) {
222  unused_instructions_.insert(kv.first);
223  }
224  }
225  }
226  }
227 }
228 
229 void FileTest::PrintLine(const char *format, ...) {
230  va_list args;
231  va_start(args, format);
232 
233  fprintf(stderr, "Line %u: ", start_line_);
234  vfprintf(stderr, format, args);
235  fprintf(stderr, "\n");
236 
237  va_end(args);
238 }
239 
241  OnKeyUsed(type_);
242  return type_;
243 }
244 
246  OnKeyUsed(type_);
247  return parameter_;
248 }
249 
251  OnKeyUsed(key);
252  return attributes_.count(key) > 0;
253 }
254 
256  OnKeyUsed(key);
257  auto iter = attributes_.find(key);
258  if (iter == attributes_.end()) {
259  PrintLine("Missing attribute '%s'.", key.c_str());
260  return false;
261  }
262  *out_value = iter->second;
263  return true;
264 }
265 
267  if (!HasAttribute(key)) {
268  abort();
269  }
270  return attributes_[key];
271 }
272 
274  OnInstructionUsed(key);
275  return instructions_.count(key) > 0;
276 }
277 
279  OnInstructionUsed(key);
280  auto iter = instructions_.find(key);
281  if (iter == instructions_.end()) {
282  PrintLine("Missing instruction '%s'.", key.c_str());
283  return false;
284  }
285  *out_value = iter->second;
286  return true;
287 }
288 
290  unused_instructions_.clear();
291 }
292 
294  if (!HasInstruction(key)) {
295  abort();
296  }
297  return instructions_[key];
298 }
299 
300 bool FileTest::GetInstructionBytes(std::vector<uint8_t> *out,
301  const std::string &key) {
303  return GetInstruction(&value, key) && ConvertToBytes(out, value);
304 }
305 
307  return current_test_;
308 }
309 
310 bool FileTest::GetBytes(std::vector<uint8_t> *out, const std::string &key) {
312  return GetAttribute(&value, key) && ConvertToBytes(out, value);
313 }
314 
316  start_line_ = 0;
317  type_.clear();
318  parameter_.clear();
319  attribute_count_.clear();
320  attributes_.clear();
321  unused_attributes_.clear();
322  unused_instructions_.clear();
323  current_test_ = "";
324 }
325 
327  instructions_.clear();
328  unused_attributes_.clear();
329 }
330 
332  unused_attributes_.erase(key);
333 }
334 
336  unused_instructions_.erase(key);
337 }
338 
339 bool FileTest::ConvertToBytes(std::vector<uint8_t> *out,
340  const std::string &value) {
341  if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') {
342  out->assign(value.begin() + 1, value.end() - 1);
343  return true;
344  }
345 
346  if (!DecodeHex(out, value)) {
347  PrintLine("Error decoding value: %s", value.c_str());
348  return false;
349  }
350  return true;
351 }
352 
354  return is_at_new_instruction_block_;
355 }
356 
358  const std::string &value) {
359  instructions_[key] = value;
360 }
361 
363  public:
364  explicit FileLineReader(const char *path) : file_(fopen(path, "r")) {}
365  ~FileLineReader() override {
366  if (file_ != nullptr) {
367  fclose(file_);
368  }
369  }
370 
371  // is_open returns true if the file was successfully opened.
372  bool is_open() const { return file_ != nullptr; }
373 
374  FileTest::ReadResult ReadLine(char *out, size_t len) override {
375  assert(len > 0);
376  if (file_ == nullptr) {
377  return FileTest::kReadError;
378  }
379 
380  if (fgets(out, len, file_) == nullptr) {
382  }
383 
384  if (strlen(out) == len - 1 && out[len - 2] != '\n' && !feof(file_)) {
385  fprintf(stderr, "Line too long.\n");
386  return FileTest::kReadError;
387  }
388 
389  return FileTest::kReadSuccess;
390  }
391 
392  private:
394 
395  FileLineReader(const FileLineReader &) = delete;
396  FileLineReader &operator=(const FileLineReader &) = delete;
397 };
398 
399 int FileTestMain(FileTestFunc run_test, void *arg, const char *path) {
401  opts.callback = run_test;
402  opts.arg = arg;
403  opts.path = path;
404 
405  return FileTestMain(opts);
406 }
407 
409  std::unique_ptr<FileLineReader> reader(
410  new FileLineReader(opts.path));
411  if (!reader->is_open()) {
412  fprintf(stderr, "Could not open file %s: %s.\n", opts.path,
413  strerror(errno));
414  return 1;
415  }
416 
417  FileTest t(std::move(reader), opts.comment_callback, opts.is_kas_test);
418 
419  bool failed = false;
420  while (true) {
421  FileTest::ReadResult ret = t.ReadNext();
422  if (ret == FileTest::kReadError) {
423  return 1;
424  } else if (ret == FileTest::kReadEOF) {
425  break;
426  }
427 
428  bool result = opts.callback(&t, opts.arg);
429  if (t.HasAttribute("Error")) {
430  if (result) {
431  t.PrintLine("Operation unexpectedly succeeded.");
432  failed = true;
433  continue;
434  }
436  if (ERR_reason_error_string(err) != t.GetAttributeOrDie("Error")) {
437  t.PrintLine("Unexpected error; wanted '%s', got '%s'.",
438  t.GetAttributeOrDie("Error").c_str(),
440  failed = true;
441  ERR_clear_error();
442  continue;
443  }
444  ERR_clear_error();
445  } else if (!result) {
446  // In case the test itself doesn't print output, print something so the
447  // line number is reported.
448  t.PrintLine("Test failed");
450  failed = true;
451  continue;
452  }
453  }
454 
455  if (!opts.silent && !failed) {
456  printf("PASS\n");
457  }
458 
459  return failed ? 1 : 0;
460 }
461 
463  ClearTest();
464 }
xds_interop_client.str
str
Definition: xds_interop_client.py:487
FileLineReader::is_open
bool is_open() const
Definition: file_test.cc:372
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
FileTest::GetBytes
bool GetBytes(std::vector< uint8_t > *out, const std::string &key)
Definition: file_test.cc:310
FileLineReader::file_
FILE * file_
Definition: file_test.cc:393
gen_build_yaml.out
dictionary out
Definition: src/benchmark/gen_build_yaml.py:24
http2_test_server.format
format
Definition: http2_test_server.py:118
FileLineReader::ReadLine
FileTest::ReadResult ReadLine(char *out, size_t len) override
Definition: file_test.cc:374
FileTest::GetParameter
const std::string & GetParameter()
Definition: file_test.cc:245
FileTest::ReadNext
ReadResult ReadNext()
Definition: file_test.cc:81
FileTestMain
int FileTestMain(FileTestFunc run_test, void *arg, const char *path)
Definition: file_test.cc:399
FileTest::IsAtNewInstructionBlock
bool IsAtNewInstructionBlock() const
Definition: file_test.cc:353
FileTest::GetInstruction
bool GetInstruction(std::string *out_value, const std::string &key)
Definition: file_test.cc:278
FileTest::InjectInstruction
void InjectInstruction(const std::string &key, const std::string &value)
Definition: file_test.cc:357
file_
FileDescriptorProto * file_
Definition: bloaty/third_party/protobuf/src/google/protobuf/compiler/annotation_test_util.cc:68
string.h
line_
int line_
Definition: bloaty/third_party/protobuf/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc:1468
buf
voidpf void * buf
Definition: bloaty/third_party/zlib/contrib/minizip/ioapi.h:136
printf
_Use_decl_annotations_ int __cdecl printf(const char *_Format,...)
Definition: cs_driver.c:91
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
ERR_print_errors_fp
#define ERR_print_errors_fp
Definition: boringssl_prefix_symbols.h:1437
type_
std::string type_
Definition: client_channel_stress_test.cc:212
error_ref_leak.err
err
Definition: error_ref_leak.py:35
cstest_report.opts
opts
Definition: cstest_report.py:81
check_documentation.path
path
Definition: check_documentation.py:57
FileTest::HasInstruction
bool HasInstruction(const std::string &key)
Definition: file_test.cc:273
python_utils.port_server.stderr
stderr
Definition: port_server.py:51
FileTest::ReadResult
ReadResult
Definition: file_test.h:92
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
asyncio_get_stats.args
args
Definition: asyncio_get_stats.py:40
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
FileTest::GetInstructionOrDie
const std::string & GetInstructionOrDie(const std::string &key)
Definition: file_test.cc:293
FileTest::kReadSuccess
@ kReadSuccess
Definition: file_test.h:93
FileTestFunc
bool(* FileTestFunc)(FileTest *t, void *arg)
Definition: file_test.h:88
FileTest::GetAttributeOrDie
const std::string & GetAttributeOrDie(const std::string &key)
Definition: file_test.cc:266
FileTest::~FileTest
~FileTest()
Definition: file_test.cc:41
FileTest::LineReader
Definition: file_test.h:98
ERR_peek_error
#define ERR_peek_error
Definition: boringssl_prefix_symbols.h:1428
FileTest
Definition: file_test.h:90
err.h
arg
Definition: cmdline.cc:40
FileTest::ClearInstructions
void ClearInstructions()
Definition: file_test.cc:326
StripSpace
static std::string StripSpace(const char *str, size_t len)
Definition: file_test.cc:57
FileTest::SkipCurrent
void SkipCurrent()
Definition: file_test.cc:462
FileTest::GetInstructionBytes
bool GetInstructionBytes(std::vector< uint8_t > *out, const std::string &key)
Definition: file_test.cc:300
setup.idx
idx
Definition: third_party/bloaty/third_party/capstone/bindings/python/setup.py:197
FindDelimiter
static const char * FindDelimiter(const char *str)
Definition: file_test.cc:45
FileTest::GetType
const std::string & GetType()
Definition: file_test.cc:240
value
const char * value
Definition: hpack_parser_table.cc:165
ParseKeyValue
static std::pair< std::string, std::string > ParseKeyValue(const char *str, const size_t len)
Definition: file_test.cc:69
FileTest::OnKeyUsed
void OnKeyUsed(const std::string &key)
Definition: file_test.cc:331
FileLineReader::~FileLineReader
~FileLineReader() override
Definition: file_test.cc:365
FileTest::GetAttribute
bool GetAttribute(std::string *out_value, const std::string &key)
Definition: file_test.cc:255
key
const char * key
Definition: hpack_parser_table.cc:164
benchmark.FILE
FILE
Definition: benchmark.py:21
parameter_
std::string parameter_
Definition: bloaty/third_party/protobuf/src/google/protobuf/compiler/command_line_interface_unittest.cc:241
grpc::fclose
fclose(creds_file)
DecodeHex
bool DecodeHex(std::vector< uint8_t > *out, const std::string &in)
Definition: boringssl-with-bazel/src/crypto/test/test_util.cc:58
ret
UniquePtr< SSL_SESSION > ret
Definition: ssl_x509.cc:1029
FileTest::PrintLine
void PrintLine(const char *format,...) OPENSSL_PRINTF_FORMAT_FUNC(2
Definition: file_test.cc:229
std
Definition: grpcpp/impl/codegen/async_unary_call.h:407
FileLineReader::FileLineReader
FileLineReader(const char *path)
Definition: file_test.cc:364
arg
struct arg arg
ERR_clear_error
#define ERR_clear_error
Definition: boringssl_prefix_symbols.h:1413
FileTest::Options
Definition: file_test.h:104
tests.stress.client.run_test
def run_test(args)
Definition: src/python/grpcio_tests/tests/stress/client.py:116
FileLineReader
Definition: file_test.cc:362
FileTest::HasAttribute
bool HasAttribute(const std::string &key)
Definition: file_test.cc:250
iter
Definition: test_winkernel.cpp:47
test_util.h
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
function
std::function< bool(GrpcTool *, int, const char **, const CliCredentials &, GrpcToolOutputCallback)> function
Definition: grpc_tool.cc:250
FileTest::OnInstructionUsed
void OnInstructionUsed(const std::string &key)
Definition: file_test.cc:335
file_test.h
to_string
static bool to_string(zval *from)
Definition: protobuf/php/ext/google/protobuf/convert.c:333
FileTest::kReadEOF
@ kReadEOF
Definition: file_test.h:94
FileTest::ClearTest
void ClearTest()
Definition: file_test.cc:315
FileTest::CurrentTestToString
const std::string & CurrentTestToString() const
Definition: file_test.cc:306
FileTest::FileTest
FileTest(std::unique_ptr< LineReader > reader, std::function< void(const std::string &)> comment_callback, bool is_kas_test)
errno.h
reader
void reader(void *n)
Definition: libuv/docs/code/locks/main.c:8
FileTest::IgnoreAllUnusedInstructions
void IgnoreAllUnusedInstructions()
Definition: file_test.cc:289
FileTest::ConvertToBytes
bool ConvertToBytes(std::vector< uint8_t > *out, const std::string &value)
Definition: file_test.cc:339
ERR_reason_error_string
#define ERR_reason_error_string
Definition: boringssl_prefix_symbols.h:1439
FileTest::kReadError
@ kReadError
Definition: file_test.h:95


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:59:20