|
|
|
/*
|
|
|
|
*
|
|
|
|
* An exhaustive set of tests for parsing both valid and invalid protobuf
|
|
|
|
* input, with buffer breaks in arbitrary places.
|
|
|
|
*
|
|
|
|
* Tests to add:
|
|
|
|
* - string/bytes
|
|
|
|
* - unknown field handler called appropriately
|
|
|
|
* - unknown fields can be inserted in random places
|
|
|
|
* - fuzzing of valid input
|
|
|
|
* - resource limits (max stack depth, max string len)
|
|
|
|
* - testing of groups
|
|
|
|
* - more throrough testing of sequences
|
|
|
|
* - test skipping of submessages
|
|
|
|
* - test suspending the decoder
|
|
|
|
* - buffers that are close enough to the end of the address space that
|
|
|
|
* pointers overflow (this might be difficult).
|
|
|
|
* - a few "kitchen sink" examples (one proto that uses all types, lots
|
|
|
|
* of submsg/sequences, etc.
|
|
|
|
* - test different handlers at every level and whether handlers fire at
|
|
|
|
* the correct field path.
|
|
|
|
* - test skips that extend past the end of current buffer (where decoder
|
|
|
|
* returns value greater than the size param).
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
|
|
#define __STDC_FORMAT_MACROS // For PRIuS, etc.
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "tests/test_util.h"
|
|
|
|
#include "tests/upb_test.h"
|
|
|
|
#include "tests/pb/test_decoder.upbdefs.h"
|
|
|
|
|
|
|
|
#ifdef AMALGAMATED
|
|
|
|
#include "upb.h"
|
|
|
|
#else // AMALGAMATED
|
|
|
|
#include "upb/handlers.h"
|
|
|
|
#include "upb/pb/decoder.h"
|
|
|
|
#include "upb/pb/varint.int.h"
|
|
|
|
#include "upb/upb.h"
|
|
|
|
#endif // !AMALGAMATED
|
|
|
|
|
|
|
|
#undef PRINT_FAILURE
|
|
|
|
#define PRINT_FAILURE(expr) \
|
|
|
|
fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \
|
|
|
|
fprintf(stderr, "expr: %s\n", #expr); \
|
|
|
|
if (testhash) { \
|
|
|
|
fprintf(stderr, "assertion failed running test %x.\n", testhash); \
|
|
|
|
if (!filter_hash) { \
|
|
|
|
fprintf(stderr, \
|
|
|
|
"Run with the arg %x to run only this test. " \
|
|
|
|
"(This will also turn on extra debugging output)\n", \
|
|
|
|
testhash); \
|
|
|
|
} \
|
|
|
|
fprintf(stderr, "Failed at %02.2f%% through tests.\n", \
|
|
|
|
(float)completed * 100 / total); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define MAX_NESTING 64
|
|
|
|
|
|
|
|
#define LINE(x) x "\n"
|
|
|
|
|
|
|
|
uint32_t filter_hash = 0;
|
|
|
|
double completed;
|
|
|
|
double total;
|
|
|
|
double *count;
|
|
|
|
|
|
|
|
enum TestMode {
|
|
|
|
COUNT_ONLY = 1,
|
|
|
|
NO_HANDLERS = 2,
|
|
|
|
ALL_HANDLERS = 3
|
|
|
|
} test_mode;
|
|
|
|
|
|
|
|
// Copied from decoder.c, since this is not a public interface.
|
|
|
|
typedef struct {
|
|
|
|
uint8_t native_wire_type;
|
|
|
|
bool is_numeric;
|
|
|
|
} upb_decoder_typeinfo;
|
|
|
|
|
|
|
|
static const upb_decoder_typeinfo upb_decoder_types[] = {
|
|
|
|
{UPB_WIRE_TYPE_END_GROUP, false}, // ENDGROUP
|
|
|
|
{UPB_WIRE_TYPE_64BIT, true}, // DOUBLE
|
|
|
|
{UPB_WIRE_TYPE_32BIT, true}, // FLOAT
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // INT64
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // UINT64
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // INT32
|
|
|
|
{UPB_WIRE_TYPE_64BIT, true}, // FIXED64
|
|
|
|
{UPB_WIRE_TYPE_32BIT, true}, // FIXED32
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // BOOL
|
|
|
|
{UPB_WIRE_TYPE_DELIMITED, false}, // STRING
|
|
|
|
{UPB_WIRE_TYPE_START_GROUP, false}, // GROUP
|
|
|
|
{UPB_WIRE_TYPE_DELIMITED, false}, // MESSAGE
|
|
|
|
{UPB_WIRE_TYPE_DELIMITED, false}, // BYTES
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // UINT32
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // ENUM
|
|
|
|
{UPB_WIRE_TYPE_32BIT, true}, // SFIXED32
|
|
|
|
{UPB_WIRE_TYPE_64BIT, true}, // SFIXED64
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // SINT32
|
|
|
|
{UPB_WIRE_TYPE_VARINT, true}, // SINT64
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifndef USE_GOOGLE
|
|
|
|
using std::string;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void vappendf(string* str, const char *format, va_list args) {
|
|
|
|
va_list copy;
|
|
|
|
_upb_va_copy(copy, args);
|
|
|
|
|
|
|
|
int count = vsnprintf(NULL, 0, format, args);
|
|
|
|
if (count >= 0)
|
|
|
|
{
|
|
|
|
UPB_ASSERT(count < 32768);
|
|
|
|
char *buffer = new char[count + 1];
|
|
|
|
UPB_ASSERT(buffer);
|
|
|
|
count = vsnprintf(buffer, count + 1, format, copy);
|
|
|
|
UPB_ASSERT(count >= 0);
|
|
|
|
str->append(buffer, count);
|
|
|
|
delete [] buffer;
|
|
|
|
}
|
|
|
|
va_end(copy);
|
|
|
|
}
|
|
|
|
|
|
|
|
void appendf(string* str, const char *fmt, ...) {
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
vappendf(str, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrintBinary(const string& str) {
|
|
|
|
for (size_t i = 0; i < str.size(); i++) {
|
|
|
|
if (isprint(str[i])) {
|
|
|
|
fprintf(stderr, "%c", str[i]);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "\\x%02x", (int)(uint8_t)str[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Routines for building arbitrary protos *************************************/
|
|
|
|
|
|
|
|
const string empty;
|
|
|
|
|
|
|
|
string cat(const string& a, const string& b,
|
|
|
|
const string& c = empty,
|
|
|
|
const string& d = empty,
|
|
|
|
const string& e = empty,
|
|
|
|
const string& f = empty,
|
|
|
|
const string& g = empty,
|
|
|
|
const string& h = empty,
|
|
|
|
const string& i = empty,
|
|
|
|
const string& j = empty,
|
|
|
|
const string& k = empty,
|
|
|
|
const string& l = empty) {
|
|
|
|
string ret;
|
|
|
|
ret.reserve(a.size() + b.size() + c.size() + d.size() + e.size() + f.size() +
|
|
|
|
g.size() + h.size() + i.size() + j.size() + k.size() + l.size());
|
|
|
|
ret.append(a);
|
|
|
|
ret.append(b);
|
|
|
|
ret.append(c);
|
|
|
|
ret.append(d);
|
|
|
|
ret.append(e);
|
|
|
|
ret.append(f);
|
|
|
|
ret.append(g);
|
|
|
|
ret.append(h);
|
|
|
|
ret.append(i);
|
|
|
|
ret.append(j);
|
|
|
|
ret.append(k);
|
|
|
|
ret.append(l);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
string num2string(T num) {
|
|
|
|
std::ostringstream ss;
|
|
|
|
ss << num;
|
|
|
|
return ss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
string varint(uint64_t x) {
|
|
|
|
char buf[UPB_PB_VARINT_MAX_LEN];
|
|
|
|
size_t len = upb_vencode64(x, buf);
|
|
|
|
return string(buf, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: proper byte-swapping for big-endian machines.
|
|
|
|
string fixed32(void *data) { return string(static_cast<char*>(data), 4); }
|
|
|
|
string fixed64(void *data) { return string(static_cast<char*>(data), 8); }
|
|
|
|
|
|
|
|
string delim(const string& buf) { return cat(varint(buf.size()), buf); }
|
|
|
|
string uint32(uint32_t u32) { return fixed32(&u32); }
|
|
|
|
string uint64(uint64_t u64) { return fixed64(&u64); }
|
|
|
|
string flt(float f) { return fixed32(&f); }
|
|
|
|
string dbl(double d) { return fixed64(&d); }
|
|
|
|
string zz32(int32_t x) { return varint(upb_zzenc_32(x)); }
|
|
|
|
string zz64(int64_t x) { return varint(upb_zzenc_64(x)); }
|
|
|
|
|
|
|
|
string tag(uint32_t fieldnum, char wire_type) {
|
|
|
|
return varint((fieldnum << 3) | wire_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
string submsg(uint32_t fn, const string& buf) {
|
|
|
|
return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), delim(buf) );
|
|
|
|
}
|
|
|
|
|
|
|
|
string group(uint32_t fn, const string& buf) {
|
|
|
|
return cat(tag(fn, UPB_WIRE_TYPE_START_GROUP), buf,
|
|
|
|
tag(fn, UPB_WIRE_TYPE_END_GROUP));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Like delim()/submsg(), but intentionally encodes an incorrect length.
|
|
|
|
// These help test when a delimited boundary doesn't land in the right place.
|
|
|
|
string badlen_delim(int err, const string& buf) {
|
|
|
|
return cat(varint(buf.size() + err), buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
string badlen_submsg(int err, uint32_t fn, const string& buf) {
|
|
|
|
return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), badlen_delim(err, buf) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* A set of handlers that covers all .proto types *****************************/
|
|
|
|
|
|
|
|
// The handlers simply append to a string indicating what handlers were called.
|
|
|
|
// This string is similar to protobuf text format but fields are referred to by
|
|
|
|
// number instead of name and sequences are explicitly delimited. We indent
|
|
|
|
// using the closure depth to test that the stack of closures is properly
|
|
|
|
// handled.
|
|
|
|
|
|
|
|
int closures[MAX_NESTING];
|
|
|
|
string output;
|
|
|
|
|
|
|
|
void indentbuf(string *buf, int depth) {
|
|
|
|
buf->append(2 * depth, ' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
void check_stack_alignment() {
|
|
|
|
#ifdef UPB_USE_JIT_X64
|
|
|
|
void *rsp = __builtin_frame_address(0);
|
|
|
|
ASSERT(((uintptr_t)rsp % 16) == 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NUMERIC_VALUE_HANDLER(member, ctype, fmt) \
|
|
|
|
bool value_##member(int* depth, const uint32_t* num, ctype val) { \
|
|
|
|
check_stack_alignment(); \
|
|
|
|
indentbuf(&output, *depth); \
|
|
|
|
appendf(&output, "%" PRIu32 ":%" fmt "\n", *num, val); \
|
|
|
|
return true; \
|
|
|
|
}
|
|
|
|
|
|
|
|
NUMERIC_VALUE_HANDLER(uint32, uint32_t, PRIu32)
|
|
|
|
NUMERIC_VALUE_HANDLER(uint64, uint64_t, PRIu64)
|
|
|
|
NUMERIC_VALUE_HANDLER(int32, int32_t, PRId32)
|
|
|
|
NUMERIC_VALUE_HANDLER(int64, int64_t, PRId64)
|
|
|
|
NUMERIC_VALUE_HANDLER(float, float, "g")
|
|
|
|
NUMERIC_VALUE_HANDLER(double, double, "g")
|
|
|
|
|
|
|
|
bool value_bool(int* depth, const uint32_t* num, bool val) {
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
appendf(&output, "%" PRIu32 ":%s\n", *num, val ? "true" : "false");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int* startstr(int* depth, const uint32_t* num, size_t size_hint) {
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
appendf(&output, "%" PRIu32 ":(%zu)\"", *num, size_hint);
|
|
|
|
return depth + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t value_string(int* depth, const uint32_t* num, const char* buf,
|
|
|
|
size_t n, const upb_bufhandle* handle) {
|
|
|
|
UPB_UNUSED(num);
|
|
|
|
UPB_UNUSED(depth);
|
|
|
|
check_stack_alignment();
|
|
|
|
output.append(buf, n);
|
|
|
|
ASSERT(handle == &global_handle);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool endstr(int* depth, const uint32_t* num) {
|
|
|
|
UPB_UNUSED(num);
|
|
|
|
check_stack_alignment();
|
|
|
|
output.append("\n");
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
appendf(&output, "%" PRIu32 ":\"\n", *num);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int* startsubmsg(int* depth, const uint32_t* num) {
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
appendf(&output, "%" PRIu32 ":{\n", *num);
|
|
|
|
return depth + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool endsubmsg(int* depth, const uint32_t* num) {
|
|
|
|
UPB_UNUSED(num);
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
output.append("}\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int* startseq(int* depth, const uint32_t* num) {
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
appendf(&output, "%" PRIu32 ":[\n", *num);
|
|
|
|
return depth + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool endseq(int* depth, const uint32_t* num) {
|
|
|
|
UPB_UNUSED(num);
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
output.append("]\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool startmsg(int* depth) {
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
output.append("<\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool endmsg(int* depth, upb_status* status) {
|
|
|
|
UPB_UNUSED(status);
|
|
|
|
check_stack_alignment();
|
|
|
|
indentbuf(&output, *depth);
|
|
|
|
output.append(">\n");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_uint32(void *val) {
|
|
|
|
uint32_t *u32 = static_cast<uint32_t*>(val);
|
|
|
|
delete u32;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class T, bool F(int*, const uint32_t*, T)>
|
|
|
|
void doreg(upb::HandlersPtr h, uint32_t num) {
|
|
|
|
upb::FieldDefPtr f = h.message_def().FindFieldByNumber(num);
|
|
|
|
ASSERT(f);
|
|
|
|
ASSERT(h.SetValueHandler<T>(f, UpbBind(F, new uint32_t(num))));
|
|
|
|
if (f.IsSequence()) {
|
|
|
|
ASSERT(h.SetStartSequenceHandler(f, UpbBind(startseq, new uint32_t(num))));
|
|
|
|
ASSERT(h.SetEndSequenceHandler(f, UpbBind(endseq, new uint32_t(num))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The repeated field number to correspond to the given non-repeated field
|
|
|
|
// number.
|
|
|
|
uint32_t rep_fn(uint32_t fn) {
|
|
|
|
return (UPB_MAX_FIELDNUMBER - 1000) + fn;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NOP_FIELD 40
|
|
|
|
#define UNKNOWN_FIELD 666
|
|
|
|
|
|
|
|
template <class T, bool F(int*, const uint32_t*, T)>
|
|
|
|
void reg(upb::HandlersPtr h, upb_descriptortype_t type) {
|
|
|
|
// We register both a repeated and a non-repeated field for every type.
|
|
|
|
// For the non-repeated field we make the field number the same as the
|
|
|
|
// type. For the repeated field we make it a function of the type.
|
|
|
|
doreg<T, F>(h, type);
|
|
|
|
doreg<T, F>(h, rep_fn(type));
|
|
|
|
}
|
|
|
|
|
|
|
|
void regseq(upb::HandlersPtr h, upb::FieldDefPtr f, uint32_t num) {
|
|
|
|
ASSERT(h.SetStartSequenceHandler(f, UpbBind(startseq, new uint32_t(num))));
|
|
|
|
ASSERT(h.SetEndSequenceHandler(f, UpbBind(endseq, new uint32_t(num))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void reg_subm(upb::HandlersPtr h, uint32_t num) {
|
|
|
|
upb::FieldDefPtr f = h.message_def().FindFieldByNumber(num);
|
|
|
|
ASSERT(f);
|
|
|
|
if (f.IsSequence()) regseq(h, f, num);
|
|
|
|
ASSERT(
|
|
|
|
h.SetStartSubMessageHandler(f, UpbBind(startsubmsg, new uint32_t(num))));
|
|
|
|
ASSERT(h.SetEndSubMessageHandler(f, UpbBind(endsubmsg, new uint32_t(num))));
|
|
|
|
}
|
|
|
|
|
|
|
|
void reg_str(upb::HandlersPtr h, uint32_t num) {
|
|
|
|
upb::FieldDefPtr f = h.message_def().FindFieldByNumber(num);
|
|
|
|
ASSERT(f);
|
|
|
|
if (f.IsSequence()) regseq(h, f, num);
|
|
|
|
ASSERT(h.SetStartStringHandler(f, UpbBind(startstr, new uint32_t(num))));
|
|
|
|
ASSERT(h.SetEndStringHandler(f, UpbBind(endstr, new uint32_t(num))));
|
|
|
|
ASSERT(h.SetStringHandler(f, UpbBind(value_string, new uint32_t(num))));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct HandlerRegisterData {
|
|
|
|
TestMode mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
void callback(const void *closure, upb::Handlers* h_ptr) {
|
|
|
|
upb::HandlersPtr h(h_ptr);
|
|
|
|
const HandlerRegisterData* data =
|
|
|
|
static_cast<const HandlerRegisterData*>(closure);
|
|
|
|
if (data->mode == ALL_HANDLERS) {
|
|
|
|
h.SetStartMessageHandler(UpbMakeHandler(startmsg));
|
|
|
|
h.SetEndMessageHandler(UpbMakeHandler(endmsg));
|
|
|
|
|
|
|
|
// Register handlers for each type.
|
|
|
|
reg<double, value_double>(h, UPB_DESCRIPTOR_TYPE_DOUBLE);
|
|
|
|
reg<float, value_float> (h, UPB_DESCRIPTOR_TYPE_FLOAT);
|
|
|
|
reg<int64_t, value_int64> (h, UPB_DESCRIPTOR_TYPE_INT64);
|
|
|
|
reg<uint64_t, value_uint64>(h, UPB_DESCRIPTOR_TYPE_UINT64);
|
|
|
|
reg<int32_t, value_int32> (h, UPB_DESCRIPTOR_TYPE_INT32);
|
|
|
|
reg<uint64_t, value_uint64>(h, UPB_DESCRIPTOR_TYPE_FIXED64);
|
|
|
|
reg<uint32_t, value_uint32>(h, UPB_DESCRIPTOR_TYPE_FIXED32);
|
|
|
|
reg<bool, value_bool> (h, UPB_DESCRIPTOR_TYPE_BOOL);
|
|
|
|
reg<uint32_t, value_uint32>(h, UPB_DESCRIPTOR_TYPE_UINT32);
|
|
|
|
reg<int32_t, value_int32> (h, UPB_DESCRIPTOR_TYPE_ENUM);
|
|
|
|
reg<int32_t, value_int32> (h, UPB_DESCRIPTOR_TYPE_SFIXED32);
|
|
|
|
reg<int64_t, value_int64> (h, UPB_DESCRIPTOR_TYPE_SFIXED64);
|
|
|
|
reg<int32_t, value_int32> (h, UPB_DESCRIPTOR_TYPE_SINT32);
|
|
|
|
reg<int64_t, value_int64> (h, UPB_DESCRIPTOR_TYPE_SINT64);
|
|
|
|
|
|
|
|
reg_str(h, UPB_DESCRIPTOR_TYPE_STRING);
|
|
|
|
reg_str(h, UPB_DESCRIPTOR_TYPE_BYTES);
|
|
|
|
reg_str(h, rep_fn(UPB_DESCRIPTOR_TYPE_STRING));
|
|
|
|
reg_str(h, rep_fn(UPB_DESCRIPTOR_TYPE_BYTES));
|
|
|
|
|
|
|
|
// Register submessage/group handlers that are self-recursive
|
|
|
|
// to this type, eg: message M { optional M m = 1; }
|
|
|
|
reg_subm(h, UPB_DESCRIPTOR_TYPE_MESSAGE);
|
|
|
|
reg_subm(h, rep_fn(UPB_DESCRIPTOR_TYPE_MESSAGE));
|
|
|
|
|
|
|
|
if (h.message_def().full_name() == std::string("DecoderTest")) {
|
|
|
|
reg_subm(h, UPB_DESCRIPTOR_TYPE_GROUP);
|
|
|
|
reg_subm(h, rep_fn(UPB_DESCRIPTOR_TYPE_GROUP));
|
|
|
|
}
|
|
|
|
|
|
|
|
// For NOP_FIELD we register no handlers, so we can pad a proto freely without
|
|
|
|
// changing the output.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Running of test cases ******************************************************/
|
|
|
|
|
|
|
|
const upb::Handlers *global_handlers;
|
|
|
|
upb::pb::DecoderMethodPtr global_method;
|
|
|
|
|
|
|
|
upb::pb::DecoderPtr CreateDecoder(upb::Arena* arena,
|
|
|
|
upb::pb::DecoderMethodPtr method,
|
|
|
|
upb::Sink sink, upb::Status* status) {
|
|
|
|
upb::pb::DecoderPtr ret =
|
|
|
|
upb::pb::DecoderPtr::Create(arena, method, sink, status);
|
|
|
|
ret.set_max_nesting(MAX_NESTING);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Hash(const string& proto, const string* expected_output, size_t seam1,
|
|
|
|
size_t seam2, bool may_skip) {
|
|
|
|
uint32_t hash = MurmurHash2(proto.c_str(), proto.size(), 0);
|
|
|
|
if (expected_output)
|
|
|
|
hash = MurmurHash2(expected_output->c_str(), expected_output->size(), hash);
|
|
|
|
hash = MurmurHash2(&seam1, sizeof(seam1), hash);
|
|
|
|
hash = MurmurHash2(&seam2, sizeof(seam2), hash);
|
|
|
|
hash = MurmurHash2(&may_skip, sizeof(may_skip), hash);
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckBytesParsed(upb::pb::DecoderPtr decoder, size_t ofs) {
|
|
|
|
// We can't have parsed more data than the decoder callback is telling us it
|
|
|
|
// parsed.
|
|
|
|
ASSERT(decoder.BytesParsed() <= ofs);
|
|
|
|
|
|
|
|
// The difference between what we've decoded and what the decoder has accepted
|
|
|
|
// represents the internally buffered amount. This amount should not exceed
|
|
|
|
// this value which comes from decoder.int.h.
|
|
|
|
ASSERT(ofs <= (decoder.BytesParsed() + UPB_DECODER_MAX_RESIDUAL_BYTES));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool parse(VerboseParserEnvironment* env,
|
|
|
|
upb::pb::DecoderPtr decoder, int bytes) {
|
|
|
|
CheckBytesParsed(decoder, env->ofs());
|
|
|
|
bool ret = env->ParseBuffer(bytes);
|
|
|
|
if (ret) {
|
|
|
|
CheckBytesParsed(decoder, env->ofs());
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void do_run_decoder(VerboseParserEnvironment* env, upb::pb::DecoderPtr decoder,
|
|
|
|
const string& proto, const string* expected_output,
|
|
|
|
size_t i, size_t j, bool may_skip) {
|
|
|
|
env->Reset(proto.c_str(), proto.size(), may_skip, expected_output == NULL);
|
|
|
|
decoder.Reset();
|
|
|
|
|
|
|
|
testhash = Hash(proto, expected_output, i, j, may_skip);
|
|
|
|
if (filter_hash && testhash != filter_hash) return;
|
|
|
|
if (test_mode != COUNT_ONLY) {
|
|
|
|
output.clear();
|
|
|
|
|
|
|
|
if (filter_hash) {
|
|
|
|
fprintf(stderr, "RUNNING TEST CASE, hash=%x\n", testhash);
|
|
|
|
fprintf(stderr, "JIT on: %s\n",
|
|
|
|
global_method.is_native() ? "true" : "false");
|
|
|
|
fprintf(stderr, "Input (len=%u): ", (unsigned)proto.size());
|
|
|
|
PrintBinary(proto);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
if (expected_output) {
|
|
|
|
if (test_mode == ALL_HANDLERS) {
|
|
|
|
fprintf(stderr, "Expected output: %s\n", expected_output->c_str());
|
|
|
|
} else if (test_mode == NO_HANDLERS) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"No handlers are registered, BUT if they were "
|
|
|
|
"the expected output would be: %s\n",
|
|
|
|
expected_output->c_str());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Expected to FAIL\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ok = env->Start() &&
|
|
|
|
parse(env, decoder, i) &&
|
|
|
|
parse(env, decoder, j - i) &&
|
|
|
|
parse(env, decoder, -1) &&
|
|
|
|
env->End();
|
|
|
|
|
|
|
|
ASSERT(env->CheckConsistency());
|
|
|
|
|
|
|
|
if (test_mode == ALL_HANDLERS) {
|
|
|
|
if (expected_output) {
|
|
|
|
if (output != *expected_output) {
|
|
|
|
fprintf(stderr, "Text mismatch: '%s' vs '%s'\n",
|
|
|
|
output.c_str(), expected_output->c_str());
|
|
|
|
}
|
|
|
|
ASSERT(ok);
|
|
|
|
ASSERT(output == *expected_output);
|
|
|
|
} else {
|
|
|
|
if (ok) {
|
|
|
|
fprintf(stderr, "Didn't expect ok result, but got output: '%s'\n",
|
|
|
|
output.c_str());
|
|
|
|
}
|
|
|
|
ASSERT(!ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(*count)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_decoder(const string& proto, const string* expected_output) {
|
|
|
|
VerboseParserEnvironment env(filter_hash != 0);
|
|
|
|
upb::Sink sink(global_handlers, &closures[0]);
|
|
|
|
upb::pb::DecoderPtr decoder = CreateDecoder(env.arena(), global_method, sink, env.status());
|
|
|
|
env.ResetBytesSink(decoder.input());
|
|
|
|
for (size_t i = 0; i < proto.size(); i++) {
|
|
|
|
for (size_t j = i; j < UPB_MIN(proto.size(), i + 5); j++) {
|
|
|
|
do_run_decoder(&env, decoder, proto, expected_output, i, j, true);
|
|
|
|
if (env.SkippedWithNull()) {
|
|
|
|
do_run_decoder(&env, decoder, proto, expected_output, i, j, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
testhash = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const static string thirty_byte_nop = cat(
|
|
|
|
tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim(string(30, 'X')) );
|
|
|
|
|
|
|
|
// Indents and wraps text as if it were a submessage with this field number
|
|
|
|
string wrap_text(int32_t fn, const string& text) {
|
|
|
|
string wrapped_text = text;
|
|
|
|
size_t pos = 0;
|
|
|
|
string replace_with = "\n ";
|
|
|
|
while ((pos = wrapped_text.find("\n", pos)) != string::npos &&
|
|
|
|
pos != wrapped_text.size() - 1) {
|
|
|
|
wrapped_text.replace(pos, 1, replace_with);
|
|
|
|
pos += replace_with.size();
|
|
|
|
}
|
|
|
|
wrapped_text = cat(
|
|
|
|
LINE("<"),
|
|
|
|
num2string(fn), LINE(":{")
|
|
|
|
" ", wrapped_text,
|
|
|
|
LINE("}")
|
|
|
|
LINE(">"));
|
|
|
|
return wrapped_text;
|
|
|
|
}
|
|
|
|
|
|
|
|
void assert_successful_parse(const string& proto,
|
|
|
|
const char *expected_fmt, ...) {
|
|
|
|
string expected_text;
|
|
|
|
va_list args;
|
|
|
|
va_start(args, expected_fmt);
|
|
|
|
vappendf(&expected_text, expected_fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
// To test both middle-of-buffer and end-of-buffer code paths,
|
|
|
|
// repeat once with no-op padding data at the end of buffer.
|
|
|
|
run_decoder(proto, &expected_text);
|
|
|
|
run_decoder(cat( proto, thirty_byte_nop ), &expected_text);
|
|
|
|
|
|
|
|
// Test that this also works when wrapped in a submessage or group.
|
|
|
|
// Indent the expected text one level and wrap it.
|
|
|
|
string wrapped_text1 = wrap_text(UPB_DESCRIPTOR_TYPE_MESSAGE, expected_text);
|
|
|
|
string wrapped_text2 = wrap_text(UPB_DESCRIPTOR_TYPE_GROUP, expected_text);
|
|
|
|
|
|
|
|
run_decoder(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, proto), &wrapped_text1);
|
|
|
|
run_decoder(group(UPB_DESCRIPTOR_TYPE_GROUP, proto), &wrapped_text2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void assert_does_not_parse_at_eof(const string& proto) {
|
|
|
|
run_decoder(proto, NULL);
|
|
|
|
|
|
|
|
// Also test that we fail to parse at end-of-submessage, not just
|
|
|
|
// end-of-message. But skip this if we have no handlers, because in that
|
|
|
|
// case we won't descend into the submessage.
|
|
|
|
if (test_mode != NO_HANDLERS) {
|
|
|
|
run_decoder(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, proto), NULL);
|
|
|
|
run_decoder(cat(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, proto),
|
|
|
|
thirty_byte_nop), NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void assert_does_not_parse(const string& proto) {
|
|
|
|
// Test that the error is caught both at end-of-buffer and middle-of-buffer.
|
|
|
|
assert_does_not_parse_at_eof(proto);
|
|
|
|
assert_does_not_parse_at_eof(cat( proto, thirty_byte_nop ));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* The actual tests ***********************************************************/
|
|
|
|
|
|
|
|
void test_premature_eof_for_type(upb_descriptortype_t type) {
|
|
|
|
// Incomplete values for each wire type.
|
|
|
|
static const string incompletes[6] = {
|
|
|
|
string("\x80"), // UPB_WIRE_TYPE_VARINT
|
|
|
|
string("abcdefg"), // UPB_WIRE_TYPE_64BIT
|
|
|
|
string("\x80"), // UPB_WIRE_TYPE_DELIMITED (partial length)
|
|
|
|
string(), // UPB_WIRE_TYPE_START_GROUP (no value required)
|
|
|
|
string(), // UPB_WIRE_TYPE_END_GROUP (no value required)
|
|
|
|
string("abc") // UPB_WIRE_TYPE_32BIT
|
|
|
|
};
|
|
|
|
|
|
|
|
uint32_t fieldnum = type;
|
|
|
|
uint32_t rep_fieldnum = rep_fn(type);
|
|
|
|
int wire_type = upb_decoder_types[type].native_wire_type;
|
|
|
|
const string& incomplete = incompletes[wire_type];
|
|
|
|
|
|
|
|
// EOF before a known non-repeated value.
|
|
|
|
assert_does_not_parse_at_eof(tag(fieldnum, wire_type));
|
|
|
|
|
|
|
|
// EOF before a known repeated value.
|
|
|
|
assert_does_not_parse_at_eof(tag(rep_fieldnum, wire_type));
|
|
|
|
|
|
|
|
// EOF before an unknown value.
|
|
|
|
assert_does_not_parse_at_eof(tag(UNKNOWN_FIELD, wire_type));
|
|
|
|
|
|
|
|
// EOF inside a known non-repeated value.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(fieldnum, wire_type), incomplete ));
|
|
|
|
|
|
|
|
// EOF inside a known repeated value.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(rep_fieldnum, wire_type), incomplete ));
|
|
|
|
|
|
|
|
// EOF inside an unknown value.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(UNKNOWN_FIELD, wire_type), incomplete ));
|
|
|
|
|
|
|
|
if (wire_type == UPB_WIRE_TYPE_DELIMITED) {
|
|
|
|
// EOF in the middle of delimited data for known non-repeated value.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(fieldnum, wire_type), varint(1) ));
|
|
|
|
|
|
|
|
// EOF in the middle of delimited data for known repeated value.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(rep_fieldnum, wire_type), varint(1) ));
|
|
|
|
|
|
|
|
// EOF in the middle of delimited data for unknown value.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(UNKNOWN_FIELD, wire_type), varint(1) ));
|
|
|
|
|
|
|
|
if (type == UPB_DESCRIPTOR_TYPE_MESSAGE) {
|
|
|
|
// Submessage ends in the middle of a value.
|
|
|
|
string incomplete_submsg =
|
|
|
|
cat ( tag(UPB_DESCRIPTOR_TYPE_INT32, UPB_WIRE_TYPE_VARINT),
|
|
|
|
incompletes[UPB_WIRE_TYPE_VARINT] );
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat( tag(fieldnum, UPB_WIRE_TYPE_DELIMITED),
|
|
|
|
varint(incomplete_submsg.size()),
|
|
|
|
incomplete_submsg ));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Packed region ends in the middle of a value.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
|
|
|
|
varint(incomplete.size()),
|
|
|
|
incomplete ));
|
|
|
|
|
|
|
|
// EOF in the middle of packed region.
|
|
|
|
assert_does_not_parse_at_eof(
|
|
|
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(1) ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// "33" and "66" are just two random values that all numeric types can
|
|
|
|
// represent.
|
|
|
|
void test_valid_data_for_type(upb_descriptortype_t type,
|
|
|
|
const string& enc33, const string& enc66) {
|
|
|
|
uint32_t fieldnum = type;
|
|
|
|
uint32_t rep_fieldnum = rep_fn(type);
|
|
|
|
int wire_type = upb_decoder_types[type].native_wire_type;
|
|
|
|
|
|
|
|
// Non-repeated
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(fieldnum, wire_type), enc33,
|
|
|
|
tag(fieldnum, wire_type), enc66 ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:33")
|
|
|
|
LINE("%u:66")
|
|
|
|
LINE(">"), fieldnum, fieldnum);
|
|
|
|
|
|
|
|
// Non-packed repeated.
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(rep_fieldnum, wire_type), enc33,
|
|
|
|
tag(rep_fieldnum, wire_type), enc66 ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:33")
|
|
|
|
LINE(" %u:66")
|
|
|
|
LINE("]")
|
|
|
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
|
|
|
|
|
|
|
|
// Packed repeated.
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
|
|
|
|
delim(cat( enc33, enc66 )) ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:33")
|
|
|
|
LINE(" %u:66")
|
|
|
|
LINE("]")
|
|
|
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
void test_valid_data_for_signed_type(upb_descriptortype_t type,
|
|
|
|
const string& enc33, const string& enc66) {
|
|
|
|
uint32_t fieldnum = type;
|
|
|
|
uint32_t rep_fieldnum = rep_fn(type);
|
|
|
|
int wire_type = upb_decoder_types[type].native_wire_type;
|
|
|
|
|
|
|
|
// Non-repeated
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(fieldnum, wire_type), enc33,
|
|
|
|
tag(fieldnum, wire_type), enc66 ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:33")
|
|
|
|
LINE("%u:-66")
|
|
|
|
LINE(">"), fieldnum, fieldnum);
|
|
|
|
|
|
|
|
// Non-packed repeated.
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(rep_fieldnum, wire_type), enc33,
|
|
|
|
tag(rep_fieldnum, wire_type), enc66 ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:33")
|
|
|
|
LINE(" %u:-66")
|
|
|
|
LINE("]")
|
|
|
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
|
|
|
|
|
|
|
|
// Packed repeated.
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
|
|
|
|
delim(cat( enc33, enc66 )) ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:33")
|
|
|
|
LINE(" %u:-66")
|
|
|
|
LINE("]")
|
|
|
|
LINE(">"), rep_fieldnum, rep_fieldnum, rep_fieldnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that invalid protobufs are properly detected (without crashing) and
|
|
|
|
// have an error reported. Field numbers match registered handlers above.
|
|
|
|
void test_invalid() {
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_DOUBLE);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_FLOAT);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_INT64);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_UINT64);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_INT32);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_FIXED64);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_FIXED32);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_BOOL);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_STRING);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_BYTES);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_UINT32);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_ENUM);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SFIXED32);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SFIXED64);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SINT32);
|
|
|
|
test_premature_eof_for_type(UPB_DESCRIPTOR_TYPE_SINT64);
|
|
|
|
|
|
|
|
// EOF inside a tag's varint.
|
|
|
|
assert_does_not_parse_at_eof( string("\x80") );
|
|
|
|
|
|
|
|
// EOF inside a known group.
|
|
|
|
// TODO(haberman): add group to decoder test schema.
|
|
|
|
//assert_does_not_parse_at_eof( tag(4, UPB_WIRE_TYPE_START_GROUP) );
|
|
|
|
|
|
|
|
// EOF inside an unknown group.
|
|
|
|
assert_does_not_parse_at_eof( tag(UNKNOWN_FIELD, UPB_WIRE_TYPE_START_GROUP) );
|
|
|
|
|
|
|
|
// End group that we are not currently in.
|
|
|
|
assert_does_not_parse( tag(4, UPB_WIRE_TYPE_END_GROUP) );
|
|
|
|
|
|
|
|
// Field number is 0.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat( tag(0, UPB_WIRE_TYPE_DELIMITED), varint(0) ));
|
|
|
|
// The previous test alone did not catch this particular pattern which could
|
|
|
|
// corrupt the internal state.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat( tag(0, UPB_WIRE_TYPE_64BIT), uint64(0) ));
|
|
|
|
|
|
|
|
// Field number is too large.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat( tag(UPB_MAX_FIELDNUMBER + 1, UPB_WIRE_TYPE_DELIMITED),
|
|
|
|
varint(0) ));
|
|
|
|
|
|
|
|
// Known group inside a submessage has ENDGROUP tag AFTER submessage end.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat ( submsg(UPB_DESCRIPTOR_TYPE_MESSAGE,
|
|
|
|
tag(UPB_DESCRIPTOR_TYPE_GROUP, UPB_WIRE_TYPE_START_GROUP)),
|
|
|
|
tag(UPB_DESCRIPTOR_TYPE_GROUP, UPB_WIRE_TYPE_END_GROUP)));
|
|
|
|
|
|
|
|
// Unknown string extends past enclosing submessage.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat (badlen_submsg(-1, UPB_DESCRIPTOR_TYPE_MESSAGE,
|
|
|
|
submsg(12345, string(" "))),
|
|
|
|
submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, string(" "))));
|
|
|
|
|
|
|
|
// Unknown fixed-length field extends past enclosing submessage.
|
|
|
|
assert_does_not_parse(
|
|
|
|
cat (badlen_submsg(-1, UPB_DESCRIPTOR_TYPE_MESSAGE,
|
|
|
|
cat( tag(12345, UPB_WIRE_TYPE_64BIT), uint64(0))),
|
|
|
|
submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, string(" "))));
|
|
|
|
|
|
|
|
// Test exceeding the resource limit of stack depth.
|
|
|
|
if (test_mode != NO_HANDLERS) {
|
|
|
|
string buf;
|
|
|
|
for (int i = 0; i <= MAX_NESTING; i++) {
|
|
|
|
buf.assign(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, buf));
|
|
|
|
}
|
|
|
|
assert_does_not_parse(buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void test_valid() {
|
|
|
|
// Empty protobuf.
|
|
|
|
assert_successful_parse(string(""), "<\n>\n");
|
|
|
|
|
|
|
|
// Empty protobuf where we never call PutString between
|
|
|
|
// StartString/EndString.
|
|
|
|
|
|
|
|
// Randomly generated hash for this test, hope it doesn't conflict with others
|
|
|
|
// by chance.
|
|
|
|
const uint32_t emptyhash = 0x5709be8e;
|
|
|
|
if (!filter_hash || filter_hash == testhash) {
|
|
|
|
testhash = emptyhash;
|
|
|
|
upb::Status status;
|
|
|
|
upb::Arena arena;
|
|
|
|
upb::Sink sink(global_handlers, &closures[0]);
|
|
|
|
upb::pb::DecoderPtr decoder =
|
|
|
|
CreateDecoder(&arena, global_method, sink, &status);
|
|
|
|
output.clear();
|
|
|
|
bool ok = upb::PutBuffer(std::string(), decoder.input());
|
|
|
|
ASSERT(ok);
|
|
|
|
ASSERT(status.ok());
|
|
|
|
if (test_mode == ALL_HANDLERS) {
|
|
|
|
ASSERT(output == string("<\n>\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_DOUBLE,
|
|
|
|
dbl(33),
|
|
|
|
dbl(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_FLOAT, flt(33), flt(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_INT64,
|
|
|
|
varint(33),
|
|
|
|
varint(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_INT32,
|
|
|
|
varint(33),
|
|
|
|
varint(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_ENUM,
|
|
|
|
varint(33),
|
|
|
|
varint(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SFIXED32,
|
|
|
|
uint32(33),
|
|
|
|
uint32(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SFIXED64,
|
|
|
|
uint64(33),
|
|
|
|
uint64(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SINT32,
|
|
|
|
zz32(33),
|
|
|
|
zz32(-66));
|
|
|
|
test_valid_data_for_signed_type(UPB_DESCRIPTOR_TYPE_SINT64,
|
|
|
|
zz64(33),
|
|
|
|
zz64(-66));
|
|
|
|
|
|
|
|
test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_UINT64, varint(33), varint(66));
|
|
|
|
test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_UINT32, varint(33), varint(66));
|
|
|
|
test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_FIXED64, uint64(33), uint64(66));
|
|
|
|
test_valid_data_for_type(UPB_DESCRIPTOR_TYPE_FIXED32, uint32(33), uint32(66));
|
|
|
|
|
|
|
|
// Unknown fields.
|
|
|
|
int int32_type = UPB_DESCRIPTOR_TYPE_INT32;
|
|
|
|
int msg_type = UPB_DESCRIPTOR_TYPE_MESSAGE;
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678) ),
|
|
|
|
"<\n>\n");
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(12345, UPB_WIRE_TYPE_32BIT), uint32(2345678) ),
|
|
|
|
"<\n>\n");
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(12345, UPB_WIRE_TYPE_64BIT), uint64(2345678) ),
|
|
|
|
"<\n>\n");
|
|
|
|
assert_successful_parse(
|
|
|
|
submsg(12345, string(" ")),
|
|
|
|
"<\n>\n");
|
|
|
|
|
|
|
|
// Unknown field inside a known submessage.
|
|
|
|
assert_successful_parse(
|
|
|
|
submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, submsg(12345, string(" "))),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" >")
|
|
|
|
LINE("}")
|
|
|
|
LINE(">"), UPB_DESCRIPTOR_TYPE_MESSAGE);
|
|
|
|
|
|
|
|
assert_successful_parse(
|
|
|
|
cat (submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, submsg(12345, string(" "))),
|
|
|
|
tag(UPB_DESCRIPTOR_TYPE_INT32, UPB_WIRE_TYPE_VARINT),
|
|
|
|
varint(5)),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" >")
|
|
|
|
LINE("}")
|
|
|
|
LINE("%u:5")
|
|
|
|
LINE(">"), UPB_DESCRIPTOR_TYPE_MESSAGE, UPB_DESCRIPTOR_TYPE_INT32);
|
|
|
|
|
|
|
|
// This triggered a previous bug in the decoder.
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(UPB_DESCRIPTOR_TYPE_SFIXED32, UPB_WIRE_TYPE_VARINT),
|
|
|
|
varint(0) ),
|
|
|
|
"<\n>\n");
|
|
|
|
|
|
|
|
assert_successful_parse(
|
|
|
|
cat(
|
|
|
|
submsg(UPB_DESCRIPTOR_TYPE_MESSAGE,
|
|
|
|
submsg(UPB_DESCRIPTOR_TYPE_MESSAGE,
|
|
|
|
cat( tag(int32_type, UPB_WIRE_TYPE_VARINT), varint(2345678),
|
|
|
|
tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678) ))),
|
|
|
|
tag(int32_type, UPB_WIRE_TYPE_VARINT), varint(22222)),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" %u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" %u:2345678")
|
|
|
|
LINE(" >")
|
|
|
|
LINE(" }")
|
|
|
|
LINE(" >")
|
|
|
|
LINE("}")
|
|
|
|
LINE("%u:22222")
|
|
|
|
LINE(">"), msg_type, msg_type, int32_type, int32_type);
|
|
|
|
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(UPB_DESCRIPTOR_TYPE_INT32, UPB_WIRE_TYPE_VARINT), varint(1),
|
|
|
|
tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678) ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:1")
|
|
|
|
LINE(">"), UPB_DESCRIPTOR_TYPE_INT32);
|
|
|
|
|
|
|
|
// String inside submsg.
|
|
|
|
uint32_t msg_fn = UPB_DESCRIPTOR_TYPE_MESSAGE;
|
|
|
|
assert_successful_parse(
|
|
|
|
submsg(msg_fn,
|
|
|
|
cat ( tag(UPB_DESCRIPTOR_TYPE_STRING, UPB_WIRE_TYPE_DELIMITED),
|
|
|
|
delim(string("abcde"))
|
|
|
|
)
|
|
|
|
),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" %u:(5)\"abcde")
|
|
|
|
LINE(" %u:\"")
|
|
|
|
LINE(" >")
|
|
|
|
LINE("}")
|
|
|
|
LINE(">"), msg_fn, UPB_DESCRIPTOR_TYPE_STRING,
|
|
|
|
UPB_DESCRIPTOR_TYPE_STRING);
|
|
|
|
|
|
|
|
// Test implicit startseq/endseq.
|
|
|
|
uint32_t repfl_fn = rep_fn(UPB_DESCRIPTOR_TYPE_FLOAT);
|
|
|
|
uint32_t repdb_fn = rep_fn(UPB_DESCRIPTOR_TYPE_DOUBLE);
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(repfl_fn, UPB_WIRE_TYPE_32BIT), flt(33),
|
|
|
|
tag(repdb_fn, UPB_WIRE_TYPE_64BIT), dbl(66) ),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:33")
|
|
|
|
LINE("]")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:66")
|
|
|
|
LINE("]")
|
|
|
|
LINE(">"), repfl_fn, repfl_fn, repdb_fn, repdb_fn);
|
|
|
|
|
|
|
|
// Submessage tests.
|
|
|
|
assert_successful_parse(
|
|
|
|
submsg(msg_fn, submsg(msg_fn, submsg(msg_fn, string()))),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" %u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" %u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" >")
|
|
|
|
LINE(" }")
|
|
|
|
LINE(" >")
|
|
|
|
LINE(" }")
|
|
|
|
LINE(" >")
|
|
|
|
LINE("}")
|
|
|
|
LINE(">"), msg_fn, msg_fn, msg_fn);
|
|
|
|
|
|
|
|
uint32_t repm_fn = rep_fn(UPB_DESCRIPTOR_TYPE_MESSAGE);
|
|
|
|
assert_successful_parse(
|
|
|
|
submsg(repm_fn, submsg(repm_fn, string())),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:[")
|
|
|
|
LINE(" %u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" %u:[")
|
|
|
|
LINE(" %u:{")
|
|
|
|
LINE(" <")
|
|
|
|
LINE(" >")
|
|
|
|
LINE(" }")
|
|
|
|
LINE(" ]")
|
|
|
|
LINE(" >")
|
|
|
|
LINE(" }")
|
|
|
|
LINE("]")
|
|
|
|
LINE(">"), repm_fn, repm_fn, repm_fn, repm_fn);
|
|
|
|
|
|
|
|
// Test unknown group.
|
|
|
|
uint32_t unknown_group_fn = 12321;
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(unknown_group_fn, UPB_WIRE_TYPE_START_GROUP),
|
|
|
|
tag(unknown_group_fn, UPB_WIRE_TYPE_END_GROUP) ),
|
|
|
|
LINE("<")
|
|
|
|
LINE(">")
|
|
|
|
);
|
|
|
|
|
|
|
|
// Test some unknown fields inside an unknown group.
|
|
|
|
const string unknown_group_with_data =
|
|
|
|
cat(
|
|
|
|
tag(unknown_group_fn, UPB_WIRE_TYPE_START_GROUP),
|
|
|
|
tag(12345, UPB_WIRE_TYPE_VARINT), varint(2345678),
|
|
|
|
tag(123456789, UPB_WIRE_TYPE_32BIT), uint32(2345678),
|
|
|
|
tag(123477, UPB_WIRE_TYPE_64BIT), uint64(2345678),
|
|
|
|
tag(123, UPB_WIRE_TYPE_DELIMITED), varint(0),
|
|
|
|
tag(unknown_group_fn, UPB_WIRE_TYPE_END_GROUP)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Nested unknown group with data.
|
|
|
|
assert_successful_parse(
|
|
|
|
cat(
|
|
|
|
tag(unknown_group_fn, UPB_WIRE_TYPE_START_GROUP),
|
|
|
|
unknown_group_with_data,
|
|
|
|
tag(unknown_group_fn, UPB_WIRE_TYPE_END_GROUP),
|
|
|
|
tag(UPB_DESCRIPTOR_TYPE_INT32, UPB_WIRE_TYPE_VARINT), varint(1)
|
|
|
|
),
|
|
|
|
LINE("<")
|
|
|
|
LINE("%u:1")
|
|
|
|
LINE(">"),
|
|
|
|
UPB_DESCRIPTOR_TYPE_INT32
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_successful_parse(
|
|
|
|
cat( tag(unknown_group_fn, UPB_WIRE_TYPE_START_GROUP),
|
|
|
|
tag(unknown_group_fn + 1, UPB_WIRE_TYPE_START_GROUP),
|
|
|
|
tag(unknown_group_fn + 1, UPB_WIRE_TYPE_END_GROUP),
|
|
|
|
tag(unknown_group_fn, UPB_WIRE_TYPE_END_GROUP) ),
|
|
|
|
LINE("<")
|
|
|
|
LINE(">")
|
|
|
|
);
|
|
|
|
|
|
|
|
// Staying within the stack limit should work properly.
|
|
|
|
string buf;
|
|
|
|
string textbuf;
|
|
|
|
int total = MAX_NESTING - 1;
|
|
|
|
for (int i = 0; i < total; i++) {
|
|
|
|
buf.assign(submsg(UPB_DESCRIPTOR_TYPE_MESSAGE, buf));
|
|
|
|
indentbuf(&textbuf, i);
|
|
|
|
textbuf.append("<\n");
|
|
|
|
indentbuf(&textbuf, i);
|
|
|
|
appendf(&textbuf, "%u:{\n", UPB_DESCRIPTOR_TYPE_MESSAGE);
|
|
|
|
}
|
|
|
|
indentbuf(&textbuf, total);
|
|
|
|
textbuf.append("<\n");
|
|
|
|
indentbuf(&textbuf, total);
|
|
|
|
textbuf.append(">\n");
|
|
|
|
for (int i = 0; i < total; i++) {
|
|
|
|
indentbuf(&textbuf, total - i - 1);
|
|
|
|
textbuf.append("}\n");
|
|
|
|
indentbuf(&textbuf, total - i - 1);
|
|
|
|
textbuf.append(">\n");
|
|
|
|
}
|
|
|
|
// Have to use run_decoder directly, because we are at max nesting and can't
|
|
|
|
// afford the extra nesting that assert_successful_parse() will do.
|
|
|
|
run_decoder(buf, &textbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void empty_callback(const void *closure, upb::Handlers* h_ptr) {}
|
|
|
|
|
|
|
|
void test_emptyhandlers(upb::SymbolTable* symtab, bool allowjit) {
|
|
|
|
// Create an empty handlers to make sure that the decoder can handle empty
|
|
|
|
// messages.
|
|
|
|
HandlerRegisterData handlerdata;
|
|
|
|
handlerdata.mode = test_mode;
|
|
|
|
|
|
|
|
upb::HandlerCache handler_cache(empty_callback, &handlerdata);
|
|
|
|
upb::pb::CodeCache pb_code_cache(&handler_cache);
|
|
|
|
|
|
|
|
pb_code_cache.set_allow_jit(allowjit);
|
|
|
|
|
|
|
|
upb::MessageDefPtr md = upb::MessageDefPtr(Empty_getmsgdef(symtab->ptr()));
|
|
|
|
global_handlers = handler_cache.Get(md);
|
|
|
|
global_method = pb_code_cache.Get(md);
|
|
|
|
|
|
|
|
// TODO: also test the case where a message has fields, but the fields are
|
|
|
|
// submessage fields and have no handlers. This also results in a decoder
|
|
|
|
// method with no field-handling code.
|
|
|
|
|
|
|
|
// Ensure that the method can run with empty and non-empty input.
|
|
|
|
string test_unknown_field_msg =
|
|
|
|
cat(tag(1, UPB_WIRE_TYPE_VARINT), varint(42),
|
|
|
|
tag(2, UPB_WIRE_TYPE_DELIMITED), delim("My test data"));
|
|
|
|
const struct {
|
|
|
|
const char* data;
|
|
|
|
size_t length;
|
|
|
|
} testdata[] = {
|
|
|
|
{ "", 0 },
|
|
|
|
{ test_unknown_field_msg.data(), test_unknown_field_msg.size() },
|
|
|
|
{ NULL, 0 },
|
|
|
|
};
|
|
|
|
for (int i = 0; testdata[i].data; i++) {
|
|
|
|
VerboseParserEnvironment env(filter_hash != 0);
|
|
|
|
upb::Sink sink(global_method.dest_handlers(), &closures[0]);
|
|
|
|
upb::pb::DecoderPtr decoder =
|
|
|
|
CreateDecoder(env.arena(), global_method, sink, env.status());
|
|
|
|
env.ResetBytesSink(decoder.input());
|
|
|
|
env.Reset(testdata[i].data, testdata[i].length, true, false);
|
|
|
|
ASSERT(env.Start());
|
|
|
|
ASSERT(env.ParseBuffer(-1));
|
|
|
|
ASSERT(env.End());
|
|
|
|
ASSERT(env.CheckConsistency());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_tests(bool use_jit) {
|
|
|
|
HandlerRegisterData handlerdata;
|
|
|
|
handlerdata.mode = test_mode;
|
|
|
|
|
|
|
|
upb::SymbolTable symtab;
|
|
|
|
upb::HandlerCache handler_cache(callback, &handlerdata);
|
|
|
|
upb::pb::CodeCache pb_code_cache(&handler_cache);
|
|
|
|
|
|
|
|
pb_code_cache.set_allow_jit(use_jit);
|
|
|
|
|
|
|
|
upb::MessageDefPtr md(DecoderTest_getmsgdef(symtab.ptr()));
|
|
|
|
global_handlers = handler_cache.Get(md);
|
|
|
|
global_method = pb_code_cache.Get(md);
|
|
|
|
ASSERT(use_jit == global_method.is_native());
|
|
|
|
completed = 0;
|
|
|
|
|
|
|
|
test_invalid();
|
|
|
|
test_valid();
|
|
|
|
|
|
|
|
test_emptyhandlers(&symtab, use_jit);
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_test_suite() {
|
|
|
|
// Test without/with JIT.
|
|
|
|
run_tests(false);
|
|
|
|
#ifdef UPB_USE_JIT_X64
|
|
|
|
run_tests(true);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
int run_tests(int argc, char *argv[]) {
|
|
|
|
if (argc > 1)
|
|
|
|
filter_hash = strtol(argv[1], NULL, 16);
|
|
|
|
for (int i = 0; i < MAX_NESTING; i++) {
|
|
|
|
closures[i] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Count tests.
|
|
|
|
count = &total;
|
|
|
|
total = 0;
|
|
|
|
test_mode = COUNT_ONLY;
|
|
|
|
run_test_suite();
|
|
|
|
count = &completed;
|
|
|
|
|
|
|
|
total *= 2; // NO_HANDLERS, ALL_HANDLERS.
|
|
|
|
|
|
|
|
test_mode = NO_HANDLERS;
|
|
|
|
run_test_suite();
|
|
|
|
|
|
|
|
test_mode = ALL_HANDLERS;
|
|
|
|
run_test_suite();
|
|
|
|
|
|
|
|
printf("All tests passed, %d assertions.\n", num_assertions);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|