blob: 0db3bfa0d33807f40dea89d4831558d0a0946fc9 [file] [log] [blame]
/*
* upb - a minimalist implementation of protocol buffers.
*
* Copyright (c) 2011 Google Inc. See LICENSE for details.
*
* An exhaustive set of tests for parsing both valid and invalid protobuf
* input, with buffer breaks in arbitrary places.
*
* Tests to add:
* - 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.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "upb/handlers.h"
#include "upb/pb/decoder.h"
#include "upb/pb/varint.h"
#include "upb/upb.h"
#include "upb_test.h"
typedef struct {
char *buf;
size_t len;
} buffer;
// Mem is initialized to NULL.
buffer *buffer_new(size_t len) {
buffer *buf = malloc(sizeof(*buf));
buf->buf = malloc(len);
buf->len = len;
memset(buf->buf, 0, buf->len);
return buf;
}
buffer *buffer_new2(const void *data, size_t len) {
buffer *buf = buffer_new(len);
memcpy(buf->buf, data, len);
return buf;
}
buffer *buffer_new3(const char *data) {
return buffer_new2(data, strlen(data));
}
buffer *buffer_dup(buffer *buf) { return buffer_new2(buf->buf, buf->len); }
void buffer_free(buffer *buf) {
free(buf->buf);
free(buf);
}
void buffer_appendf(buffer *buf, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
size_t size = buf->len;
buf->len += upb_vrprintf(&buf->buf, &size, buf->len, fmt, args);
va_end(args);
}
void buffer_cat(buffer *buf, buffer *buf2) {
size_t newlen = buf->len + buf2->len;
buf->buf = realloc(buf->buf, newlen);
memcpy(buf->buf + buf->len, buf2->buf, buf2->len);
buf->len = newlen;
buffer_free(buf2);
}
bool buffer_eql(buffer *buf, buffer *buf2) {
return buf->len == buf2->len && memcmp(buf->buf, buf2->buf, buf->len) == 0;
}
/* Routines for building arbitrary protos *************************************/
buffer *cat(buffer *arg1, ...) {
va_list ap;
buffer *arg;
va_start(ap, arg1);
while ((arg = va_arg(ap, buffer*)) != NULL) {
buffer_cat(arg1, arg);
}
va_end(ap);
return arg1;
}
buffer *varint(uint64_t x) {
buffer *buf = buffer_new(UPB_PB_VARINT_MAX_LEN + 1);
buf->len = upb_vencode64(x, buf->buf);
return buf;
}
// TODO: proper byte-swapping for big-endian machines.
buffer *fixed32(void *data) { return buffer_new2(data, 4); }
buffer *fixed64(void *data) { return buffer_new2(data, 8); }
buffer *delim(buffer *buf) { return cat( varint(buf->len), buf, NULL ); }
buffer *uint32(uint32_t u32) { return fixed32(&u32); }
buffer *uint64(uint64_t u64) { return fixed64(&u64); }
buffer *flt(float f) { return fixed32(&f); }
buffer *dbl(double d) { return fixed64(&d); }
buffer *zz32(int32_t x) { return varint(upb_zzenc_32(x)); }
buffer *zz64(int64_t x) { return varint(upb_zzenc_64(x)); }
buffer *tag(uint32_t fieldnum, char wire_type) {
return varint((fieldnum << 3) | wire_type);
}
buffer *submsg(uint32_t fn, buffer *buf) {
return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), delim(buf), NULL );
}
/* 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.
#define VALUE_HANDLER(member, fmt) \
upb_flow_t value_ ## member(void *closure, upb_value fval, upb_value val) { \
buffer_appendf(closure, "%" PRIu32 ":%" fmt "; ", \
upb_value_getuint32(fval), upb_value_get ## member(val)); \
return UPB_CONTINUE; \
}
VALUE_HANDLER(uint32, PRIu32)
VALUE_HANDLER(uint64, PRIu64)
VALUE_HANDLER(int32, PRId32)
VALUE_HANDLER(int64, PRId64)
VALUE_HANDLER(float, "g")
VALUE_HANDLER(double, "g")
upb_flow_t value_bool(void *closure, upb_value fval, upb_value val) {
buffer_appendf(closure, "%" PRIu32 ":%s; ",
upb_value_getuint32(fval),
upb_value_getbool(val) ? "true" : "false");
return UPB_CONTINUE;
}
upb_flow_t value_string(void *closure, upb_value fval, upb_value val) {
// Note: won't work with strings that contain NULL.
char *str = upb_byteregion_strdup(upb_value_getbyteregion(val));
buffer_appendf(closure, "%" PRIu32 ":%s; ", upb_value_getuint32(fval), str);
free(str);
return UPB_CONTINUE;
}
upb_sflow_t startsubmsg(void *closure, upb_value fval) {
buffer_appendf(closure, "%" PRIu32 ":{ ", upb_value_getuint32(fval));
return UPB_CONTINUE_WITH(closure);
}
upb_flow_t endsubmsg(void *closure, upb_value fval) {
buffer_appendf(closure, "} ");
return UPB_CONTINUE;
}
upb_sflow_t startseq(void *closure, upb_value fval) {
buffer_appendf(closure, "%" PRIu32 ":[ ", upb_value_getuint32(fval));
return UPB_CONTINUE_WITH(closure);
}
upb_flow_t endseq(void *closure, upb_value fval) {
buffer_appendf(closure, "] ");
return UPB_CONTINUE;
}
void doreg(upb_mhandlers *m, uint32_t num, upb_fieldtype_t type, bool repeated,
upb_value_handler *handler) {
upb_fhandlers *f = upb_mhandlers_newfhandlers(m, num, type, repeated);
ASSERT(f);
upb_fhandlers_setvalue(f, handler);
upb_fhandlers_setstartseq(f, &startseq);
upb_fhandlers_setendseq(f, &endseq);
upb_fhandlers_setfval(f, upb_value_uint32(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
void reg(upb_mhandlers *m, upb_fieldtype_t type, upb_value_handler *handler) {
// 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(m, type, type, false, handler);
doreg(m, rep_fn(type), type, true, handler);
}
void reg_subm(upb_mhandlers *m, uint32_t num, upb_fieldtype_t type,
bool repeated) {
upb_fhandlers *f =
upb_mhandlers_newfhandlers_subm(m, num, type, repeated, m);
ASSERT(f);
upb_fhandlers_setstartseq(f, &startseq);
upb_fhandlers_setendseq(f, &endseq);
upb_fhandlers_setstartsubmsg(f, &startsubmsg);
upb_fhandlers_setendsubmsg(f, &endsubmsg);
upb_fhandlers_setfval(f, upb_value_uint32(num));
}
void reghandlers(upb_mhandlers *m) {
// Register handlers for each type.
reg(m, UPB_TYPE(DOUBLE), &value_double);
reg(m, UPB_TYPE(FLOAT), &value_float);
reg(m, UPB_TYPE(INT64), &value_int64);
reg(m, UPB_TYPE(UINT64), &value_uint64);
reg(m, UPB_TYPE(INT32) , &value_int32);
reg(m, UPB_TYPE(FIXED64), &value_uint64);
reg(m, UPB_TYPE(FIXED32), &value_uint32);
reg(m, UPB_TYPE(BOOL), &value_bool);
reg(m, UPB_TYPE(STRING), &value_string);
reg(m, UPB_TYPE(BYTES), &value_string);
reg(m, UPB_TYPE(UINT32), &value_uint32);
reg(m, UPB_TYPE(ENUM), &value_int32);
reg(m, UPB_TYPE(SFIXED32), &value_int32);
reg(m, UPB_TYPE(SFIXED64), &value_int64);
reg(m, UPB_TYPE(SINT32), &value_int32);
reg(m, UPB_TYPE(SINT64), &value_int64);
// Register submessage/group handlers that are self-recursive
// to this type, eg: message M { optional M m = 1; }
reg_subm(m, UPB_TYPE(MESSAGE), UPB_TYPE(MESSAGE), false);
reg_subm(m, UPB_TYPE(GROUP), UPB_TYPE(GROUP), false);
reg_subm(m, rep_fn(UPB_TYPE(MESSAGE)), UPB_TYPE(MESSAGE), true);
reg_subm(m, rep_fn(UPB_TYPE(GROUP)), UPB_TYPE(GROUP), true);
// Register a no-op string field so we can pad the proto wherever we want.
upb_mhandlers_newfhandlers(m, NOP_FIELD, UPB_TYPE(STRING), false);
}
/* Custom bytesrc that can insert buffer seams in arbitrary places ************/
typedef struct {
upb_bytesrc bytesrc;
const char *str;
size_t len, seam1, seam2;
upb_byteregion byteregion;
} upb_seamsrc;
size_t upb_seamsrc_avail(const upb_seamsrc *src, size_t ofs) {
if (ofs < src->seam1) return src->seam1 - ofs;
if (ofs < src->seam2) return src->seam2 - ofs;
return src->len - ofs;
}
upb_bytesuccess_t upb_seamsrc_fetch(void *_src, uint64_t ofs, size_t *read) {
upb_seamsrc *src = _src;
assert(ofs < src->len);
if (ofs == src->len) {
upb_status_seteof(&src->bytesrc.status);
return UPB_BYTE_EOF;
}
*read = upb_seamsrc_avail(src, ofs);
return UPB_BYTE_OK;
}
void upb_seamsrc_copy(const void *_src, uint64_t ofs,
size_t len, char *dst) {
const upb_seamsrc *src = _src;
assert(ofs + len <= src->len);
memcpy(dst, src->str + ofs, len);
}
void upb_seamsrc_discard(void *src, uint64_t ofs) {
(void)src;
(void)ofs;
}
const char *upb_seamsrc_getptr(const void *_s, uint64_t ofs, size_t *len) {
const upb_seamsrc *src = _s;
*len = upb_seamsrc_avail(src, ofs);
return src->str + ofs;
}
void upb_seamsrc_init(upb_seamsrc *s, const char *str, size_t len) {
static upb_bytesrc_vtbl vtbl = {
&upb_seamsrc_fetch,
&upb_seamsrc_discard,
&upb_seamsrc_copy,
&upb_seamsrc_getptr,
};
upb_bytesrc_init(&s->bytesrc, &vtbl);
s->seam1 = 0;
s->seam2 = 0;
s->str = str;
s->len = len;
s->byteregion.bytesrc = &s->bytesrc;
s->byteregion.toplevel = true;
s->byteregion.start = 0;
s->byteregion.end = len;
}
void upb_seamsrc_resetseams(upb_seamsrc *s, size_t seam1, size_t seam2) {
ASSERT(seam1 <= seam2);
s->seam1 = seam1;
s->seam2 = seam2;
s->byteregion.discard = 0;
s->byteregion.fetch = 0;
}
void upb_seamsrc_uninit(upb_seamsrc *s) { (void)s; }
upb_bytesrc *upb_seamsrc_bytesrc(upb_seamsrc *s) {
return &s->bytesrc;
}
// Returns the top-level upb_byteregion* for this seamsrc. Invalidated when
// the seamsrc is reset.
upb_byteregion *upb_seamsrc_allbytes(upb_seamsrc *s) {
return &s->byteregion;
}
/* Running of test cases ******************************************************/
upb_decoderplan *plan;
void run_decoder(buffer *proto, buffer *expected_output) {
upb_seamsrc src;
upb_seamsrc_init(&src, proto->buf, proto->len);
upb_decoder d;
upb_decoder_init(&d);
upb_decoder_resetplan(&d, plan, 0);
for (size_t i = 0; i < proto->len; i++) {
for (size_t j = i; j < proto->len; j++) {
upb_seamsrc_resetseams(&src, i, j);
upb_byteregion *input = upb_seamsrc_allbytes(&src);
buffer *output = buffer_new(0);
upb_decoder_resetinput(&d, input, output);
upb_success_t success = UPB_SUSPENDED;
while (success == UPB_SUSPENDED)
success = upb_decoder_decode(&d);
ASSERT(upb_ok(upb_decoder_status(&d)) == (success == UPB_OK));
if (expected_output) {
ASSERT(success == UPB_OK);
// The input should be fully consumed.
ASSERT(upb_byteregion_fetchofs(input) == upb_byteregion_endofs(input));
ASSERT(upb_byteregion_discardofs(input) ==
upb_byteregion_endofs(input));
if (!buffer_eql(output, expected_output)) {
fprintf(stderr, "Text mismatch: '%s' vs '%s'\n",
output->buf, expected_output->buf);
}
ASSERT(strcmp(output->buf, expected_output->buf) == 0);
} else {
ASSERT(success == UPB_ERROR);
}
buffer_free(output);
}
}
upb_seamsrc_uninit(&src);
upb_decoder_uninit(&d);
buffer_free(proto);
}
void assert_successful_parse_at_eof(buffer *proto, const char *expected_fmt,
va_list args) {
buffer *expected_text = buffer_new(0);
size_t size = expected_text->len;
expected_text->len += upb_vrprintf(&expected_text->buf, &size,
expected_text->len, expected_fmt, args);
run_decoder(proto, expected_text);
buffer_free(expected_text);
}
void assert_does_not_parse_at_eof(buffer *proto) {
run_decoder(proto, NULL);
}
void assert_successful_parse(buffer *proto, const char *expected_fmt, ...) {
// The JIT is only used for data >=20 bytes from end-of-buffer, so
// repeat once with no-op padding data at the end of buffer.
va_list args, args2;
va_start(args, expected_fmt);
va_copy(args2, args);
assert_successful_parse_at_eof(buffer_dup(proto), expected_fmt, args);
assert_successful_parse_at_eof(
cat( proto,
tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim(buffer_new(30)),
NULL ),
expected_fmt, args2);
va_end(args);
va_end(args2);
}
void assert_does_not_parse(buffer *proto) {
// The JIT is only used for data >=20 bytes from end-of-buffer, so
// repeat once with no-op padding data at the end of buffer.
assert_does_not_parse_at_eof(buffer_dup(proto));
assert_does_not_parse_at_eof(
cat( proto,
tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim( buffer_new(30)),
NULL ));
}
/* The actual tests ***********************************************************/
void test_premature_eof_for_type(upb_fieldtype_t type) {
// Incomplete values for each wire type.
static const char *incompletes[] = {
"\x80", // UPB_WIRE_TYPE_VARINT
"abcdefg", // UPB_WIRE_TYPE_64BIT
"\x80", // UPB_WIRE_TYPE_DELIMITED (partial length)
NULL, // UPB_WIRE_TYPE_START_GROUP (no value required)
NULL, // UPB_WIRE_TYPE_END_GROUP (no value required)
"abc" // UPB_WIRE_TYPE_32BIT
};
uint32_t fieldnum = type;
uint32_t rep_fieldnum = rep_fn(type);
int wire_type = upb_types[type].native_wire_type;
const char *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), buffer_new3(incomplete), NULL ));
// EOF inside a known repeated value.
assert_does_not_parse_at_eof(
cat( tag(rep_fieldnum, wire_type), buffer_new3(incomplete), NULL ));
// EOF inside an unknown value.
assert_does_not_parse_at_eof(
cat( tag(UNKNOWN_FIELD, wire_type), buffer_new3(incomplete), NULL ));
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), NULL ));
// 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), NULL ));
// EOF in the middle of delimited data for unknown value.
assert_does_not_parse_at_eof(
cat( tag(UNKNOWN_FIELD, wire_type), varint(1), NULL ));
if (type == UPB_TYPE(MESSAGE)) {
// Submessage ends in the middle of a value.
buffer *incomplete_submsg =
cat ( tag(UPB_TYPE(INT32), UPB_WIRE_TYPE_VARINT),
buffer_new3(incompletes[UPB_WIRE_TYPE_VARINT]), NULL );
assert_does_not_parse(
cat( tag(fieldnum, UPB_WIRE_TYPE_DELIMITED),
varint(incomplete_submsg->len),
incomplete_submsg, NULL ));
}
} else {
// Packed region ends in the middle of a value.
assert_does_not_parse(
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
varint(strlen(incomplete)),
buffer_new3(incomplete), NULL ));
// EOF in the middle of packed region.
assert_does_not_parse_at_eof(
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(1), NULL ));
}
}
// "33" and "66" are just two random values that all numeric types can
// represent.
void test_valid_data_for_type(upb_fieldtype_t type,
buffer *enc33, buffer *enc66) {
uint32_t fieldnum = type;
uint32_t rep_fieldnum = rep_fn(type);
int wire_type = upb_types[type].native_wire_type;
// Non-repeated
assert_successful_parse(
cat( tag(fieldnum, wire_type), buffer_dup(enc33),
tag(fieldnum, wire_type), buffer_dup(enc66), NULL ),
"%u:33; %u:66; ", fieldnum, fieldnum);
// Non-packed repeated.
assert_successful_parse(
cat( tag(rep_fieldnum, wire_type), buffer_dup(enc33),
tag(rep_fieldnum, wire_type), buffer_dup(enc66), NULL ),
"%u:[ %u:33; %u:66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum);
// Packed repeated.
assert_successful_parse(
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
delim(cat( buffer_dup(enc33), buffer_dup(enc66), NULL )), NULL ),
"%u:[ %u:33; %u:66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum);
buffer_free(enc33);
buffer_free(enc66);
}
void test_valid_data_for_signed_type(upb_fieldtype_t type,
buffer *enc33, buffer *enc66) {
uint32_t fieldnum = type;
uint32_t rep_fieldnum = rep_fn(type);
int wire_type = upb_types[type].native_wire_type;
// Non-repeated
assert_successful_parse(
cat( tag(fieldnum, wire_type), buffer_dup(enc33),
tag(fieldnum, wire_type), buffer_dup(enc66), NULL ),
"%u:33; %u:-66; ", fieldnum, fieldnum);
// Non-packed repeated.
assert_successful_parse(
cat( tag(rep_fieldnum, wire_type), buffer_dup(enc33),
tag(rep_fieldnum, wire_type), buffer_dup(enc66), NULL ),
"%u:[ %u:33; %u:-66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum);
// Packed repeated.
assert_successful_parse(
cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED),
delim(cat( buffer_dup(enc33), buffer_dup(enc66), NULL )), NULL ),
"%u:[ %u:33; %u:-66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum);
buffer_free(enc33);
buffer_free(enc66);
}
// 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_TYPE(DOUBLE));
test_premature_eof_for_type(UPB_TYPE(FLOAT));
test_premature_eof_for_type(UPB_TYPE(INT64));
test_premature_eof_for_type(UPB_TYPE(UINT64));
test_premature_eof_for_type(UPB_TYPE(INT32));
test_premature_eof_for_type(UPB_TYPE(FIXED64));
test_premature_eof_for_type(UPB_TYPE(FIXED32));
test_premature_eof_for_type(UPB_TYPE(BOOL));
test_premature_eof_for_type(UPB_TYPE(STRING));
test_premature_eof_for_type(UPB_TYPE(BYTES));
test_premature_eof_for_type(UPB_TYPE(UINT32));
test_premature_eof_for_type(UPB_TYPE(ENUM));
test_premature_eof_for_type(UPB_TYPE(SFIXED32));
test_premature_eof_for_type(UPB_TYPE(SFIXED64));
test_premature_eof_for_type(UPB_TYPE(SINT32));
test_premature_eof_for_type(UPB_TYPE(SINT64));
// EOF inside a tag's varint.
assert_does_not_parse_at_eof( buffer_new3("\x80") );
// EOF inside a known group.
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), NULL ));
// Field number is too large.
assert_does_not_parse(
cat( tag(UPB_MAX_FIELDNUMBER + 1, UPB_WIRE_TYPE_DELIMITED),
varint(0), NULL ));
// Test exceeding the resource limit of stack depth.
buffer *buf = buffer_new3("");
for (int i = 0; i < UPB_MAX_NESTING; i++) {
buf = submsg(UPB_TYPE(MESSAGE), buf);
}
assert_does_not_parse(buf);
// Staying within the stack limit should work properly.
buf = buffer_new3("");
buffer *textbuf = buffer_new3("");
int total = UPB_MAX_NESTING - 1;
for (int i = 0; i < total; i++) {
buf = submsg(UPB_TYPE(MESSAGE), buf);
buffer_appendf(textbuf, "%u:{ ", UPB_TYPE(MESSAGE));
}
for (int i = 0; i < total; i++) {
buffer_appendf(textbuf, "} ");
}
assert_successful_parse(buf, "%s", textbuf->buf);
buffer_free(textbuf);
}
void test_valid() {
test_valid_data_for_signed_type(UPB_TYPE(DOUBLE), dbl(33), dbl(-66));
test_valid_data_for_signed_type(UPB_TYPE(FLOAT), flt(33), flt(-66));
test_valid_data_for_signed_type(UPB_TYPE(INT64), varint(33), varint(-66));
test_valid_data_for_signed_type(UPB_TYPE(INT32), varint(33), varint(-66));
test_valid_data_for_signed_type(UPB_TYPE(ENUM), varint(33), varint(-66));
test_valid_data_for_signed_type(UPB_TYPE(SFIXED32), uint32(33), uint32(-66));
test_valid_data_for_signed_type(UPB_TYPE(SFIXED64), uint64(33), uint64(-66));
test_valid_data_for_signed_type(UPB_TYPE(SINT32), zz32(33), zz32(-66));
test_valid_data_for_signed_type(UPB_TYPE(SINT64), zz64(33), zz64(-66));
test_valid_data_for_type(UPB_TYPE(UINT64), varint(33), varint(66));
test_valid_data_for_type(UPB_TYPE(UINT32), varint(33), varint(66));
test_valid_data_for_type(UPB_TYPE(FIXED64), uint64(33), uint64(66));
test_valid_data_for_type(UPB_TYPE(FIXED32), uint32(33), uint32(66));
// Submessage tests.
uint32_t msg_fn = UPB_TYPE(MESSAGE);
assert_successful_parse(
submsg(msg_fn, submsg(msg_fn, submsg(msg_fn, buffer_new3("")))),
"%u:{ %u:{ %u:{ } } } ", msg_fn, msg_fn, msg_fn);
uint32_t repm_fn = rep_fn(UPB_TYPE(MESSAGE));
assert_successful_parse(
submsg(repm_fn, submsg(repm_fn, buffer_new3(""))),
"%u:[ %u:{ %u:[ %u:{ } ] } ] ", repm_fn, repm_fn, repm_fn, repm_fn);
}
void run_tests() {
test_invalid();
test_valid();
}
int main() {
// Construct decoder plan.
upb_handlers *h = upb_handlers_new();
reghandlers(upb_handlers_newmhandlers(h));
// Test without JIT.
plan = upb_decoderplan_new(h, false);
run_tests();
upb_decoderplan_unref(plan);
// Test JIT.
plan = upb_decoderplan_new(h, true);
run_tests();
upb_decoderplan_unref(plan);
plan = NULL;
printf("All tests passed, %d assertions.\n", num_assertions);
upb_handlers_unref(h);
return 0;
}