/* ** Common functionality for tests. **/ #ifndef UPB_TEST_UTIL_H_ #define UPB_TEST_UTIL_H_ #include #include #include "tests/upb_test.h" #include "upb/sink.h" #include "upb/port_def.inc" #ifdef __cplusplus upb_bufhandle global_handle; /* A convenience class for parser tests. Provides some useful features: * * - can support multiple calls to parse, to test the parser's handling * of buffer seams. * * - can output verbose output about each parse call when requested, for * ease of debugging. * * - can pass NULL for skipped regions of the input if requested. * * - allocates and passes a separate buffer for each parsed region, to * ensure that the parser is not erroneously overreading its buffer. */ class VerboseParserEnvironment { public: /* Pass verbose=true to print detailed diagnostics to stderr. */ VerboseParserEnvironment(bool verbose) : verbose_(verbose) {} void Reset(const char *buf, size_t len, bool may_skip, bool expect_error) { buf_ = buf; len_ = len; ofs_ = 0; expect_error_ = expect_error; end_ok_set_ = false; skip_until_ = may_skip ? 0 : -1; skipped_with_null_ = false; } /* The user should call a series of: * * Reset(buf, len, may_skip); * Start() * ParseBuffer(X); * ParseBuffer(Y); * // Repeat ParseBuffer as desired, but last call should pass -1. * ParseBuffer(-1); * End(); */ bool Start() { if (verbose_) { fprintf(stderr, "Calling start()\n"); } return sink_.Start(len_, &subc_); } bool End() { if (verbose_) { fprintf(stderr, "Calling end()\n"); } end_ok_ = sink_.End(); end_ok_set_ = true; return end_ok_; } bool CheckConsistency() { /* If we called end (which we should only do when previous bytes are fully * accepted), then end() should return true iff there were no errors. */ if (end_ok_set_ && end_ok_ != status_.ok()) { fprintf(stderr, "End() status and saw_error didn't match.\n"); return false; } if (expect_error_ && status_.ok()) { fprintf(stderr, "Expected error but saw none.\n"); return false; } if (!status_.ok()) { if (expect_error_ && verbose_) { fprintf(stderr, "Encountered error, as expected: %s", status_.error_message()); } else if (!expect_error_) { fprintf(stderr, "Encountered unexpected error: %s", status_.error_message()); return false; } } return true; } bool ParseBuffer(int bytes) { if (bytes < 0) { bytes = len_ - ofs_; } ASSERT((size_t)bytes <= (len_ - ofs_)); /* Copy buffer into a separate, temporary buffer. * This is necessary to verify that the parser is not erroneously * reading outside the specified bounds. */ char *buf2 = NULL; if ((int)(ofs_ + bytes) <= skip_until_) { skipped_with_null_ = true; } else { buf2 = (char*)malloc(bytes); UPB_ASSERT(buf2); memcpy(buf2, buf_ + ofs_, bytes); } if (buf2 == NULL && bytes == 0) { /* Decoders dont' support buf=NULL, bytes=0. */ return true; } if (verbose_) { fprintf(stderr, "Calling parse(%u) for bytes %u-%u of the input\n", (unsigned)bytes, (unsigned)ofs_, (unsigned)(ofs_ + bytes)); } int parsed = sink_.PutBuffer(subc_, buf2, bytes, &global_handle); free(buf2); if (verbose_) { if (parsed == bytes) { fprintf(stderr, "parse(%u) = %u, complete byte count indicates success\n", (unsigned)bytes, (unsigned)bytes); } else if (parsed > bytes) { fprintf(stderr, "parse(%u) = %u, long byte count indicates success and skip " "of the next %u bytes\n", (unsigned)bytes, (unsigned)parsed, (unsigned)(parsed - bytes)); } else { fprintf(stderr, "parse(%u) = %u, short byte count indicates failure; " "last %u bytes were not consumed\n", (unsigned)bytes, (unsigned)parsed, (unsigned)(bytes - parsed)); } } if (!status_.ok()) { return false; } if (parsed > bytes && skip_until_ >= 0) { skip_until_ = ofs_ + parsed; } ofs_ += UPB_MIN(parsed, bytes); return true; } void ResetBytesSink(upb::BytesSink sink) { sink_ = sink; } size_t ofs() { return ofs_; } bool SkippedWithNull() { return skipped_with_null_; } upb::Arena* arena() { return &arena_; } upb::Status* status() { return &status_; } private: upb::Arena arena_; upb::Status status_; upb::BytesSink sink_; const char* buf_; size_t len_; bool verbose_; size_t ofs_; void *subc_; bool expect_error_; bool end_ok_; bool end_ok_set_; /* When our parse call returns a value greater than the number of bytes * we passed in, the decoder is indicating to us that the next N bytes * in the stream are not needed and can be skipped. The user is allowed * to pass a NULL buffer for those N bytes. * * skip_until_ is initially set to 0 if we should do this NULL-buffer * skipping or -1 if we should not. If we are open to doing NULL-buffer * skipping and we get an opportunity to do it, we set skip_until to the * stream offset where we can skip until. The user can then test whether * this happened by testing SkippedWithNull(). */ int skip_until_; bool skipped_with_null_; }; #endif /* __cplusplus */ UPB_INLINE char *upb_readfile(const char *filename, size_t *len) { long size; char *buf; FILE *f = fopen(filename, "rb"); if(!f) return NULL; if(fseek(f, 0, SEEK_END) != 0) goto error; size = ftell(f); if(size < 0) goto error; if(fseek(f, 0, SEEK_SET) != 0) goto error; buf = (char*)malloc(size + 1); if(size && fread(buf, size, 1, f) != 1) goto error; fclose(f); if (len) *len = size; buf[size] = '\0'; return buf; error: fclose(f); return NULL; } #include "upb/port_undef.inc" #endif /* UPB_TEST_UTIL_H_ */