mirror of https://github.com/grpc/grpc.git
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
6.0 KiB
231 lines
6.0 KiB
5 years ago
|
/*
|
||
|
** Common functionality for tests.
|
||
|
**/
|
||
|
|
||
|
#ifndef UPB_TEST_UTIL_H_
|
||
|
#define UPB_TEST_UTIL_H_
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <math.h>
|
||
|
#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) {
|
||
5 years ago
|
bytes = (int)(len_ - ofs_);
|
||
5 years ago
|
}
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
|
||
5 years ago
|
int parsed = (int)sink_.PutBuffer(subc_, buf2, bytes, &global_handle);
|
||
5 years ago
|
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) {
|
||
5 years ago
|
skip_until_ = (int)(ofs_ + parsed);
|
||
5 years ago
|
}
|
||
|
|
||
|
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_ */
|