diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test.proto | 24 | ||||
-rw-r--r-- | tests/test_cpp.cc | 11 | ||||
-rw-r--r-- | tests/test_decoder.cc (renamed from tests/test_decoder.c) | 543 | ||||
-rw-r--r-- | tests/test_def.c | 171 | ||||
-rw-r--r-- | tests/test_table.cc | 134 | ||||
-rw-r--r-- | tests/test_vs_proto2.cc | 294 | ||||
-rw-r--r-- | tests/tests.c | 121 | ||||
-rw-r--r-- | tests/upb_test.h | 22 |
8 files changed, 691 insertions, 629 deletions
diff --git a/tests/test.proto b/tests/test.proto index f3dde24..e634ed2 100644 --- a/tests/test.proto +++ b/tests/test.proto @@ -1,14 +1,10 @@ // A series of messages with various kinds of cycles in them. -// +-+---+ +---+ -// V | | | | -// A -> B-+-> C -> D<--+ -// ^ | | -// +----------+----+ -// -// This tests the following cases: -// - B and C are together in multiple cycles -// - B and D are cycles to themselves. +// +-+---+ +---+ +---+ +// V | | V | V | +// A -> B-+-> C -> D---+--->E---+ +// ^ |`---|--------^ +// +----------+----+ F message A { optional B b = 1; @@ -23,11 +19,21 @@ message C { optional A a = 1; optional B b = 2; optional D d = 3; + optional E e = 4; } message D { optional A a = 1; optional D d = 2; + optional E e = 3; +} + +message E { + optional E e = 1; +} + +message F { + optional E e = 1; } // A proto with a bunch of simple primitives. diff --git a/tests/test_cpp.cc b/tests/test_cpp.cc index 5182217..4d70e85 100644 --- a/tests/test_cpp.cc +++ b/tests/test_cpp.cc @@ -15,6 +15,7 @@ #include "upb/upb.hpp" #include "upb/pb/decoder.hpp" #include "upb/pb/glue.hpp" +#include "upb_test.h" static void TestSymbolTable(const char *descriptor_file) { upb::SymbolTable *s = upb::SymbolTable::New(); @@ -23,20 +24,20 @@ static void TestSymbolTable(const char *descriptor_file) { std::cerr << "Couldn't load descriptor: " << status; exit(1); } - const upb::MessageDef *md = s->LookupMessage("A"); - assert(md); + const upb::MessageDef *md = s->LookupMessage("A", &md); + ASSERT(md); s->Unref(); - md->Unref(); + md->Unref(&md); } static void TestByteStream() { upb::StringSource stringsrc; stringsrc.Reset("testing", 7); upb::ByteRegion* byteregion = stringsrc.AllBytes(); - assert(byteregion->FetchAll() == UPB_BYTE_OK); + ASSERT(byteregion->FetchAll() == UPB_BYTE_OK); char* str = byteregion->StrDup(); - assert(strcmp(str, "testing") == 0); + ASSERT(strcmp(str, "testing") == 0); free(str); } diff --git a/tests/test_decoder.c b/tests/test_decoder.cc index 14d0e2d..13403bb 100644 --- a/tests/test_decoder.c +++ b/tests/test_decoder.cc @@ -21,6 +21,10 @@ * of submsg/sequences, etc. */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS // For PRIuS, etc. +#endif + #include <inttypes.h> #include <stdarg.h> #include <stdint.h> @@ -32,95 +36,133 @@ #include "upb/upb.h" #include "upb_test.h" +// Copied from decoder.c, since this is not a public interface. typedef struct { - char *buf; - size_t len; -} buffer; + 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 +}; + + +class buffer { + public: + buffer(const void *data, size_t len) : len_(0) { append(data, len); } + explicit buffer(const char *data) : len_(0) { append(data); } + explicit buffer(size_t len) : len_(len) { memset(buf_, 0, len); } + buffer(const buffer& buf) : len_(0) { append(buf); } + buffer() : len_(0) {} + + void append(const void *data, size_t len) { + ASSERT_NOCOUNT(len + len_ < sizeof(buf_)); + memcpy(buf_ + len_, data, len); + len_ += len; + buf_[len_] = NULL; + } -// 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; -} + void append(const buffer& buf) { + append(buf.buf_, buf.len_); + } -buffer *buffer_new2(const void *data, size_t len) { - buffer *buf = buffer_new(len); - memcpy(buf->buf, data, len); - return buf; -} + void append(const char *str) { + append(str, strlen(str)); + } -buffer *buffer_new3(const char *data) { - return buffer_new2(data, strlen(data)); -} + void vappendf(const char *fmt, va_list args) { + size_t avail = sizeof(buf_) - len_; + size_t size = vsnprintf(buf_ + len_, avail, fmt, args); + ASSERT_NOCOUNT(avail > size); + len_ += size; + } -buffer *buffer_dup(buffer *buf) { return buffer_new2(buf->buf, buf->len); } + void appendf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vappendf(fmt, args); + va_end(args); + } -void buffer_free(buffer *buf) { - free(buf->buf); - free(buf); -} + void assign(const buffer& buf) { + clear(); + append(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); -} + bool eql(const buffer& other) const { + return len_ == other.len_ && memcmp(buf_, other.buf_, len_) == 0; + } -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); -} + void clear() { len_ = 0; } + size_t len() const { return len_; } + const char *buf() const { return buf_; } -bool buffer_eql(buffer *buf, buffer *buf2) { - return buf->len == buf2->len && memcmp(buf->buf, buf2->buf, buf->len) == 0; -} + private: + // Has to be big enough for the largest string used in the test. + char buf_[32768]; + size_t len_; +}; /* 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; +const buffer empty; + +buffer cat(const buffer& a, const buffer& b, + const buffer& c = empty, + const buffer& d = empty, + const buffer& e = empty) { + buffer ret; + ret.append(a); + ret.append(b); + ret.append(c); + ret.append(d); + ret.append(e); + return ret; } -buffer *varint(uint64_t x) { - buffer *buf = buffer_new(UPB_PB_VARINT_MAX_LEN + 1); - buf->len = upb_vencode64(x, buf->buf); - return buf; +buffer varint(uint64_t x) { + char buf[UPB_PB_VARINT_MAX_LEN]; + size_t len = upb_vencode64(x, buf); + return buffer(buf, len); } // 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) { +buffer fixed32(void *data) { return buffer(data, 4); } +buffer fixed64(void *data) { return buffer(data, 8); } + +buffer delim(const buffer& buf) { return cat(varint(buf.len()), buf); } +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 ); +buffer submsg(uint32_t fn, const buffer& buf) { + return cat( tag(fn, UPB_WIRE_TYPE_DELIMITED), delim(buf) ); } @@ -128,11 +170,26 @@ buffer *submsg(uint32_t fn, buffer *buf) { // 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. +// 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[UPB_MAX_NESTING]; +buffer output; + +void indentbuf(buffer *buf, int depth) { + for (int i = 0; i < depth; i++) + buf->append(" ", 2); +} + +void indent(void *depth) { + indentbuf(&output, *(int*)depth); +} #define VALUE_HANDLER(member, fmt) \ upb_flow_t value_ ## member(void *closure, upb_value fval, upb_value val) { \ - buffer_appendf(closure, "%" PRIu32 ":%" fmt "; ", \ + indent(closure); \ + output.appendf("%" PRIu32 ":%" fmt "\n", \ upb_value_getuint32(fval), upb_value_get ## member(val)); \ return UPB_CONTINUE; \ } @@ -145,7 +202,8 @@ 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; ", + indent(closure); + output.appendf("%" PRIu32 ":%s\n", upb_value_getuint32(fval), upb_value_getbool(val) ? "true" : "false"); return UPB_CONTINUE; @@ -153,34 +211,49 @@ upb_flow_t value_bool(void *closure, upb_value fval, upb_value val) { upb_flow_t value_string(void *closure, upb_value fval, upb_value val) { // Note: won't work with strings that contain NULL. + indent(closure); char *str = upb_byteregion_strdup(upb_value_getbyteregion(val)); - buffer_appendf(closure, "%" PRIu32 ":%s; ", upb_value_getuint32(fval), str); + output.appendf("%" PRIu32 ":%s\n", 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); + indent(closure); + output.appendf("%" PRIu32 ":{\n", upb_value_getuint32(fval)); + return UPB_CONTINUE_WITH(((int*)closure) + 1); } upb_flow_t endsubmsg(void *closure, upb_value fval) { - (void)fval; - buffer_appendf(closure, "} "); + indent(closure); + output.append("}\n"); 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); + indent(closure); + output.appendf("%" PRIu32 ":[\n", upb_value_getuint32(fval)); + return UPB_CONTINUE_WITH(((int*)closure) + 1); } upb_flow_t endseq(void *closure, upb_value fval) { - (void)fval; - buffer_appendf(closure, "] "); + indent(closure); + output.append("]\n"); return UPB_CONTINUE; } +upb_flow_t startmsg(void *closure) { + indent(closure); + output.append("<\n"); + return UPB_CONTINUE; +} + +void endmsg(void *closure, upb_status *status) { + (void)status; + indent(closure); + output.append(">\n"); +} + 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); @@ -221,6 +294,9 @@ void reg_subm(upb_mhandlers *m, uint32_t num, upb_fieldtype_t type, } void reghandlers(upb_mhandlers *m) { + upb_mhandlers_setstartmsg(m, &startmsg); + upb_mhandlers_setendmsg(m, &endmsg); + // Register handlers for each type. reg(m, UPB_TYPE(DOUBLE), &value_double); reg(m, UPB_TYPE(FLOAT), &value_float); @@ -267,7 +343,7 @@ size_t upb_seamsrc_avail(const upb_seamsrc *src, size_t ofs) { } upb_bytesuccess_t upb_seamsrc_fetch(void *_src, uint64_t ofs, size_t *read) { - upb_seamsrc *src = _src; + upb_seamsrc *src = (upb_seamsrc*)_src; assert(ofs < src->len); if (ofs == src->len) { upb_status_seteof(&src->bytesrc.status); @@ -279,7 +355,7 @@ upb_bytesuccess_t upb_seamsrc_fetch(void *_src, uint64_t ofs, size_t *read) { void upb_seamsrc_copy(const void *_src, uint64_t ofs, size_t len, char *dst) { - const upb_seamsrc *src = _src; + const upb_seamsrc *src = (const upb_seamsrc*)_src; assert(ofs + len <= src->len); memcpy(dst, src->str + ofs, len); } @@ -290,7 +366,7 @@ void upb_seamsrc_discard(void *src, uint64_t ofs) { } const char *upb_seamsrc_getptr(const void *_s, uint64_t ofs, size_t *len) { - const upb_seamsrc *src = _s; + const upb_seamsrc *src = (const upb_seamsrc*)_s; *len = upb_seamsrc_avail(src, ofs); return src->str + ofs; } @@ -314,7 +390,7 @@ void upb_seamsrc_init(upb_seamsrc *s, const char *str, size_t len) { } void upb_seamsrc_resetseams(upb_seamsrc *s, size_t seam1, size_t seam2) { - ASSERT(seam1 <= seam2); + assert(seam1 <= seam2); s->seam1 = seam1; s->seam2 = seam2; s->byteregion.discard = 0; @@ -337,83 +413,68 @@ upb_byteregion *upb_seamsrc_allbytes(upb_seamsrc *s) { /* Running of test cases ******************************************************/ upb_decoderplan *plan; - -void run_decoder(buffer *proto, buffer *expected_output) { +#define LINE(x) x "\n" +void run_decoder(const buffer& proto, const buffer* expected_output) { upb_seamsrc src; - upb_seamsrc_init(&src, proto->buf, proto->len); + 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++) { + for (size_t i = 0; i < proto.len(); i++) { + for (size_t j = i; j < UPB_MIN(proto.len(), i + 5); 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); + output.clear(); + upb_decoder_resetinput(&d, input, &closures[0]); 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); + ASSERT_STATUS(success == UPB_OK, upb_decoder_status(&d)); // 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)) { + if (!output.eql(*expected_output)) { fprintf(stderr, "Text mismatch: '%s' vs '%s'\n", - output->buf, expected_output->buf); + output.buf(), expected_output->buf()); } - ASSERT(strcmp(output->buf, expected_output->buf) == 0); + ASSERT(output.eql(*expected_output)); } 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); + upb_seamsrc_uninit(&src); } -void assert_does_not_parse_at_eof(buffer *proto) { - run_decoder(proto, NULL); -} +const static buffer thirty_byte_nop = buffer(cat( + tag(NOP_FIELD, UPB_WIRE_TYPE_DELIMITED), delim(buffer(30)) )); -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; +void assert_successful_parse(const buffer& proto, + const char *expected_fmt, ...) { + buffer expected_text; + va_list args; 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); + expected_text.vappendf(expected_fmt, args); va_end(args); - va_end(args2); + // 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. + run_decoder(proto, &expected_text); + run_decoder(cat( proto, thirty_byte_nop ), &expected_text); } -void assert_does_not_parse(buffer *proto) { +void assert_does_not_parse_at_eof(const buffer& proto) { + run_decoder(proto, NULL); +} + +void assert_does_not_parse(const 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 )); + assert_does_not_parse_at_eof(proto); + assert_does_not_parse_at_eof(cat( proto, thirty_byte_nop )); } @@ -421,19 +482,19 @@ void assert_does_not_parse(buffer *proto) { 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 + static const buffer incompletes[6] = { + buffer("\x80"), // UPB_WIRE_TYPE_VARINT + buffer("abcdefg"), // UPB_WIRE_TYPE_64BIT + buffer("\x80"), // UPB_WIRE_TYPE_DELIMITED (partial length) + buffer(), // UPB_WIRE_TYPE_START_GROUP (no value required) + buffer(), // UPB_WIRE_TYPE_END_GROUP (no value required) + buffer("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]; + int wire_type = upb_decoder_types[type].native_wire_type; + const buffer& incomplete = incompletes[wire_type]; // EOF before a known non-repeated value. assert_does_not_parse_at_eof(tag(fieldnum, wire_type)); @@ -446,108 +507,128 @@ void test_premature_eof_for_type(upb_fieldtype_t type) { // EOF inside a known non-repeated value. assert_does_not_parse_at_eof( - cat( tag(fieldnum, wire_type), buffer_new3(incomplete), NULL )); + cat( tag(fieldnum, wire_type), incomplete )); // EOF inside a known repeated value. assert_does_not_parse_at_eof( - cat( tag(rep_fieldnum, wire_type), buffer_new3(incomplete), NULL )); + cat( tag(rep_fieldnum, wire_type), incomplete )); // EOF inside an unknown value. assert_does_not_parse_at_eof( - cat( tag(UNKNOWN_FIELD, wire_type), buffer_new3(incomplete), NULL )); + 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), NULL )); + 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), NULL )); + 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), NULL )); + cat( tag(UNKNOWN_FIELD, wire_type), varint(1) )); if (type == UPB_TYPE(MESSAGE)) { // Submessage ends in the middle of a value. - buffer *incomplete_submsg = + buffer incomplete_submsg = cat ( tag(UPB_TYPE(INT32), UPB_WIRE_TYPE_VARINT), - buffer_new3(incompletes[UPB_WIRE_TYPE_VARINT]), NULL ); + incompletes[UPB_WIRE_TYPE_VARINT] ); assert_does_not_parse( cat( tag(fieldnum, UPB_WIRE_TYPE_DELIMITED), - varint(incomplete_submsg->len), - incomplete_submsg, NULL )); + varint(incomplete_submsg.len()), + 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(strlen(incomplete)), - buffer_new3(incomplete), NULL )); + varint(incomplete.len()), + incomplete )); // EOF in the middle of packed region. assert_does_not_parse_at_eof( - cat( tag(rep_fieldnum, UPB_WIRE_TYPE_DELIMITED), varint(1), NULL )); + 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_fieldtype_t type, - buffer *enc33, buffer *enc66) { + const buffer& enc33, const buffer& enc66) { uint32_t fieldnum = type; uint32_t rep_fieldnum = rep_fn(type); - int wire_type = upb_types[type].native_wire_type; + int wire_type = upb_decoder_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); + 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), buffer_dup(enc33), - tag(rep_fieldnum, wire_type), buffer_dup(enc66), NULL ), - "%u:[ %u:33; %u:66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum); + 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( 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); + 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_fieldtype_t type, - buffer *enc33, buffer *enc66) { + const buffer& enc33, const buffer& enc66) { uint32_t fieldnum = type; uint32_t rep_fieldnum = rep_fn(type); - int wire_type = upb_types[type].native_wire_type; + int wire_type = upb_decoder_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); + 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), buffer_dup(enc33), - tag(rep_fieldnum, wire_type), buffer_dup(enc66), NULL ), - "%u:[ %u:33; %u:-66; ] ", rep_fieldnum, rep_fieldnum, rep_fieldnum); + 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( 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); + 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 @@ -571,7 +652,7 @@ void test_invalid() { test_premature_eof_for_type(UPB_TYPE(SINT64)); // EOF inside a tag's varint. - assert_does_not_parse_at_eof( buffer_new3("\x80") ); + assert_does_not_parse_at_eof( buffer("\x80") ); // EOF inside a known group. assert_does_not_parse_at_eof( tag(4, UPB_WIRE_TYPE_START_GROUP) ); @@ -584,33 +665,19 @@ void test_invalid() { // Field number is 0. assert_does_not_parse( - cat( tag(0, UPB_WIRE_TYPE_DELIMITED), varint(0), NULL )); + cat( tag(0, UPB_WIRE_TYPE_DELIMITED), varint(0) )); // Field number is too large. assert_does_not_parse( cat( tag(UPB_MAX_FIELDNUMBER + 1, UPB_WIRE_TYPE_DELIMITED), - varint(0), NULL )); + varint(0) )); // Test exceeding the resource limit of stack depth. - buffer *buf = buffer_new3(""); + buffer buf; for (int i = 0; i < UPB_MAX_NESTING; i++) { - buf = submsg(UPB_TYPE(MESSAGE), buf); + buf.assign(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() { @@ -629,16 +696,80 @@ void test_valid() { test_valid_data_for_type(UPB_TYPE(FIXED64), uint64(33), uint64(66)); test_valid_data_for_type(UPB_TYPE(FIXED32), uint32(33), uint32(66)); + // Test implicit startseq/endseq. + uint32_t repfl_fn = rep_fn(UPB_TYPE(FLOAT)); + uint32_t repdb_fn = rep_fn(UPB_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. 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); + submsg(msg_fn, submsg(msg_fn, submsg(msg_fn, buffer()))), + 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_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); + submsg(repm_fn, submsg(repm_fn, buffer())), + 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); + + // Staying within the stack limit should work properly. + buffer buf; + buffer textbuf; + int total = UPB_MAX_NESTING - 1; + for (int i = 0; i < total; i++) { + buf.assign(submsg(UPB_TYPE(MESSAGE), buf)); + indentbuf(&textbuf, i); + textbuf.append("<\n"); + indentbuf(&textbuf, i); + textbuf.appendf("%u:{\n", UPB_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"); + } + assert_successful_parse(buf, "%s", textbuf.buf()); } void run_tests() { @@ -647,10 +778,17 @@ void run_tests() { } int main() { + for (int i = 0; i < UPB_MAX_NESTING; i++) { + closures[i] = i; + } // Construct decoder plan. upb_handlers *h = upb_handlers_new(); reghandlers(upb_handlers_newmhandlers(h)); + // Create an empty handlers to make sure that the decoder can handle empty + // messages. + upb_handlers_newmhandlers(h); + // Test without JIT. plan = upb_decoderplan_new(h, false); run_tests(); @@ -658,6 +796,11 @@ int main() { // Test JIT. plan = upb_decoderplan_new(h, true); +#ifdef UPB_USE_JIT_X64 + ASSERT(upb_decoderplan_hasjitcode(plan)); +#else + ASSERT(!upb_decoderplan_hasjitcode(plan)); +#endif run_tests(); upb_decoderplan_unref(plan); diff --git a/tests/test_def.c b/tests/test_def.c index 3ca3064..698532e 100644 --- a/tests/test_def.c +++ b/tests/test_def.c @@ -1,19 +1,174 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2011 Google Inc. See LICENSE for details. + * + * Test of defs and symtab. There should be far more tests of edge conditions + * (like attempts to link defs that don't have required properties set). + */ -#undef NDEBUG /* ensure tests always assert. */ #include "upb/def.h" +#include "upb/pb/glue.h" +#include "upb_test.h" #include <stdlib.h> +#include <string.h> -int main() { - upb_symtab *s = upb_symtab_new(); +const char *descriptor_file; - // Will be empty atm since we haven't added anything to the symtab. +static void test_empty_symtab() { + upb_symtab *s = upb_symtab_new(); int count; - const upb_def **defs = upb_symtab_getdefs(s, &count, UPB_DEF_ANY); - for (int i = 0; i < count; i++) { - upb_def_unref(defs[i]); - } + const upb_def **defs = upb_symtab_getdefs(s, &count, UPB_DEF_ANY, NULL); + ASSERT(count == 0); free(defs); + upb_symtab_unref(s); +} +static upb_symtab *load_test_proto() { + upb_symtab *s = upb_symtab_new(); + ASSERT(s); + upb_status status = UPB_STATUS_INIT; + if (!upb_load_descriptor_file_into_symtab(s, descriptor_file, &status)) { + fprintf(stderr, "Error loading descriptor file: %s\n", + upb_status_getstr(&status)); + exit(1); + } + upb_status_uninit(&status); + return s; +} + +static void test_cycles() { + upb_symtab *s = load_test_proto(); + + // Test cycle detection by making a cyclic def's main refcount go to zero + // and then be incremented to one again. + const upb_def *def = upb_symtab_lookup(s, "A", &def); + ASSERT(def); + ASSERT(upb_def_isfinalized(def)); upb_symtab_unref(s); + + // Message A has only one subfield: "optional B b = 1". + const upb_msgdef *m = upb_downcast_msgdef_const(def); + upb_fielddef *f = upb_msgdef_itof(m, 1); + ASSERT(f); + ASSERT(upb_hassubdef(f)); + const upb_def *def2 = upb_fielddef_subdef(f); + ASSERT(upb_downcast_msgdef_const(def2)); + ASSERT(strcmp(upb_def_fullname(def2), "B") == 0); + + upb_def_ref(def2, &def2); + upb_def_unref(def, &def); + upb_def_unref(def2, &def2); +} + +static void test_fielddef_unref() { + upb_symtab *s = load_test_proto(); + const upb_msgdef *md = upb_symtab_lookupmsg(s, "A", &md); + upb_fielddef *f = upb_msgdef_itof(md, 1); + upb_fielddef_ref(f, &f); + + // Unref symtab and msgdef; now fielddef is the only thing keeping the msgdef + // alive. + upb_symtab_unref(s); + upb_msgdef_unref(md, &md); + // Check that md is still alive. + ASSERT(strcmp(upb_def_fullname(UPB_UPCAST(md)), "A") == 0); + + // Check that unref of fielddef frees the whole remaining graph. + upb_fielddef_unref(f, &f); +} + +static void test_fielddef_accessors() { + upb_fielddef *f1 = upb_fielddef_new(&f1); + upb_fielddef *f2 = upb_fielddef_new(&f2); + + ASSERT(upb_fielddef_ismutable(f1)); + upb_fielddef_setname(f1, "f1"); + upb_fielddef_setnumber(f1, 1937); + upb_fielddef_settype(f1, UPB_TYPE(FIXED64)); + upb_fielddef_setlabel(f1, UPB_LABEL(REPEATED)); + ASSERT(upb_fielddef_number(f1) == 1937); + + ASSERT(upb_fielddef_ismutable(f2)); + upb_fielddef_setname(f2, "f2"); + upb_fielddef_setnumber(f2, 1572); + upb_fielddef_settype(f2, UPB_TYPE(BYTES)); + upb_fielddef_setlabel(f2, UPB_LABEL(REPEATED)); + ASSERT(upb_fielddef_number(f2) == 1572); + + upb_fielddef_unref(f1, &f1); + upb_fielddef_unref(f2, &f2); +} + +static upb_fielddef *newfield( + const char *name, int32_t num, uint8_t type, uint8_t label, + const char *type_name, void *owner) { + upb_fielddef *f = upb_fielddef_new(owner); + upb_fielddef_setname(f, name); + upb_fielddef_setnumber(f, num); + upb_fielddef_settype(f, type); + upb_fielddef_setlabel(f, label); + upb_fielddef_setsubtypename(f, type_name); + return f; +} + +static upb_msgdef *upb_msgdef_newnamed(const char *name, void *owner) { + upb_msgdef *m = upb_msgdef_new(owner); + upb_def_setfullname(UPB_UPCAST(m), name); + return m; +} + +INLINE upb_enumdef *upb_enumdef_newnamed(const char *name, void *owner) { + upb_enumdef *e = upb_enumdef_new(owner); + upb_def_setfullname(UPB_UPCAST(e), name); + return e; +} + +void test_replacement() { + upb_symtab *s = upb_symtab_new(); + + upb_msgdef *m = upb_msgdef_newnamed("MyMessage", &s); + upb_msgdef_addfield(m, newfield( + "field1", 1, UPB_TYPE(ENUM), UPB_LABEL(OPTIONAL), ".MyEnum", &s), &s); + upb_msgdef *m2 = upb_msgdef_newnamed("MyMessage2", &s); + upb_enumdef *e = upb_enumdef_newnamed("MyEnum", &s); + + upb_def *newdefs[] = {UPB_UPCAST(m), UPB_UPCAST(m2), UPB_UPCAST(e)}; + upb_status status = UPB_STATUS_INIT; + ASSERT_STATUS(upb_symtab_add(s, newdefs, 3, &s, &status), &status); + + // Try adding a new definition of MyEnum, MyMessage should get replaced with + // a new version. + upb_enumdef *e2 = upb_enumdef_new(&s); + upb_def_setfullname(UPB_UPCAST(e2), "MyEnum"); + upb_def *newdefs2[] = {UPB_UPCAST(e2)}; + ASSERT_STATUS(upb_symtab_add(s, newdefs2, 1, &s, &status), &status); + + const upb_msgdef *m3 = upb_symtab_lookupmsg(s, "MyMessage", &m3); + ASSERT(m3); + // Must be different because it points to MyEnum which was replaced. + ASSERT(m3 != m); + upb_msgdef_unref(m3, &m3); + + m3 = upb_symtab_lookupmsg(s, "MyMessage2", &m3); + // Should be the same because it was not replaced, nor were any defs that + // are reachable from it. + ASSERT(m3 == m2); + upb_msgdef_unref(m3, &m3); + + upb_symtab_unref(s); +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: test_def <test.proto.pb>\n"); + return 1; + } + descriptor_file = argv[1]; + test_empty_symtab(); + test_cycles(); + test_fielddef_accessors(); + test_fielddef_unref(); + test_replacement(); return 0; } diff --git a/tests/test_table.cc b/tests/test_table.cc index 47e083f..2538e35 100644 --- a/tests/test_table.cc +++ b/tests/test_table.cc @@ -1,8 +1,11 @@ +/* + * upb - a minimalist implementation of protocol buffers. + * + * Copyright (c) 2012 Google Inc. See LICENSE for details. + * + * Tests for upb_table. + */ -#undef NDEBUG /* ensure tests always assert. */ -#include "upb/table.h" -#include "test_util.h" -#include <assert.h> #include <string.h> #include <sys/resource.h> #include <ext/hash_map> @@ -11,55 +14,45 @@ #include <set> #include <string> #include <vector> +#include "tests/test_util.h" +#include "tests/upb_test.h" +#include "upb/table.h" bool benchmark = false; #define CPU_TIME_PER_TEST 0.5 using std::vector; -typedef struct { - uint32_t value; /* key*2 */ -} inttable_entry; - -typedef struct { - int32_t value; /* ASCII Value of first letter */ -} strtable_entry; - -double get_usertime() -{ +double get_usertime() { struct rusage usage; getrusage(RUSAGE_SELF, &usage); return usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec/1000000.0); } /* num_entries must be a power of 2. */ -void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) -{ +void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) { /* Initialize structures. */ upb_strtable table; std::map<std::string, int32_t> m; - upb_strtable_init(&table, 0, sizeof(strtable_entry)); + upb_strtable_init(&table); std::set<std::string> all; for(size_t i = 0; i < num_to_insert; i++) { const std::string& key = keys[i]; all.insert(key); - strtable_entry e; - e.value = key[0]; - upb_strtable_insert(&table, key.c_str(), &e); + upb_strtable_insert(&table, key.c_str(), upb_value_int32(key[0])); m[key] = key[0]; } /* Test correctness. */ for(uint32_t i = 0; i < keys.size(); i++) { const std::string& key = keys[i]; - strtable_entry *e = - (strtable_entry*)upb_strtable_lookup(&table, key.c_str()); + const upb_value *v = upb_strtable_lookup(&table, key.c_str()); if(m.find(key) != m.end()) { /* Assume map implementation is correct. */ - assert(e); - assert(e->value == key[0]); - assert(m[key] == key[0]); + ASSERT(v); + ASSERT(upb_value_getint32(*v) == key[0]); + ASSERT(m[key] == key[0]); } else { - assert(e == NULL); + ASSERT(v == NULL); } } @@ -69,66 +62,83 @@ void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) const char *key = upb_strtable_iter_key(&iter); std::string tmp(key, strlen(key)); std::set<std::string>::iterator i = all.find(tmp); - assert(i != all.end()); + ASSERT(i != all.end()); all.erase(i); } - assert(all.empty()); + ASSERT(all.empty()); - upb_strtable_free(&table); + upb_strtable_uninit(&table); } /* num_entries must be a power of 2. */ -void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) -{ +void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) { /* Initialize structures. */ upb_inttable table; uint32_t largest_key = 0; std::map<uint32_t, uint32_t> m; __gnu_cxx::hash_map<uint32_t, uint32_t> hm; - upb_inttable_init(&table, num_entries, sizeof(inttable_entry)); + upb_inttable_init(&table); for(size_t i = 0; i < num_entries; i++) { int32_t key = keys[i]; largest_key = UPB_MAX((int32_t)largest_key, key); - inttable_entry e; - e.value = (key*2) << 1; - upb_inttable_insert(&table, key, &e); + upb_inttable_insert(&table, key, upb_value_uint32(key * 2)); m[key] = key*2; hm[key] = key*2; } /* Test correctness. */ for(uint32_t i = 0; i <= largest_key; i++) { - inttable_entry *e = (inttable_entry*)upb_inttable_lookup( - &table, i); + const upb_value *v = upb_inttable_lookup(&table, i); + if(m.find(i) != m.end()) { /* Assume map implementation is correct. */ + ASSERT(v); + ASSERT(upb_value_getuint32(*v) == i*2); + ASSERT(m[i] == i*2); + ASSERT(hm[i] == i*2); + } else { + ASSERT(v == NULL); + } + } + + for(uint16_t i = 0; i < num_entries; i += 2) { + upb_value val; + bool ret = upb_inttable_remove(&table, keys[i], &val); + ASSERT(ret == (m.erase(keys[i]) == 1)); + if (ret) ASSERT(upb_value_getuint32(val) == keys[i] * 2); + hm.erase(keys[i]); + m.erase(keys[i]); + } + + ASSERT(upb_inttable_count(&table) == hm.size()); + + /* Test correctness. */ + for(uint32_t i = 0; i <= largest_key; i++) { + const upb_value *v = upb_inttable_lookup(&table, i); if(m.find(i) != m.end()) { /* Assume map implementation is correct. */ - assert(e); - //printf("addr: %p, expected: %d, actual: %d\n", e, i*2, e->value); - assert(((e->value) >> 1) == i*2); - assert(m[i] == i*2); - assert(hm[i] == i*2); + ASSERT(v); + ASSERT(upb_value_getuint32(*v) == i*2); + ASSERT(m[i] == i*2); + ASSERT(hm[i] == i*2); } else { - assert(e == NULL); + ASSERT(v == NULL); } } // Compact and test correctness again. upb_inttable_compact(&table); for(uint32_t i = 0; i <= largest_key; i++) { - inttable_entry *e = (inttable_entry*)upb_inttable_lookup( - &table, i); + const upb_value *v = upb_inttable_lookup(&table, i); if(m.find(i) != m.end()) { /* Assume map implementation is correct. */ - assert(e); - //printf("addr: %p, expected: %d, actual: %d\n", e, i*2, e->value); - assert(((e->value) >> 1) == i*2); - assert(m[i] == i*2); - assert(hm[i] == i*2); + ASSERT(v); + ASSERT(upb_value_getuint32(*v) == i*2); + ASSERT(m[i] == i*2); + ASSERT(hm[i] == i*2); } else { - assert(e == NULL); + ASSERT(v == NULL); } } if(!benchmark) { - upb_inttable_free(&table); + upb_inttable_uninit(&table); return; } @@ -141,7 +151,7 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) } for(uint16_t i = num_entries - 1; i >= 1; i--) { uint16_t rand_i = (random() / (double)RAND_MAX) * i; - assert(rand_i <= i); + ASSERT(rand_i <= i); uint16_t tmp = rand_order[rand_i]; rand_order[rand_i] = rand_order[i]; rand_order[i] = tmp; @@ -162,8 +172,8 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) for(i = 0; true; i++) { MAYBE_BREAK; int32_t key = keys[i & mask]; - inttable_entry *e = (inttable_entry*)upb_inttable_lookup(&table, key); - x += (uintptr_t)e; + const upb_value *v = upb_inttable_lookup32(&table, key); + x += (uintptr_t)v; } double total = get_usertime() - before; printf("%s/s\n", eng(i/total, 3, false)); @@ -174,8 +184,8 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) for(i = 0; true; i++) { MAYBE_BREAK; int32_t key = keys[rand_order[i & mask]]; - inttable_entry *e = (inttable_entry*)upb_inttable_lookup(&table, key); - x += (uintptr_t)e; + const upb_value *v = upb_inttable_lookup32(&table, key); + x += (uintptr_t)v; } total = get_usertime() - before; printf("%s/s\n", eng(i/total, 3, false)); @@ -223,20 +233,18 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) } total = get_usertime() - before; printf("%s/s\n\n", eng(i/total, 3, false)); - upb_inttable_free(&table); + upb_inttable_uninit(&table); delete rand_order; } -int32_t *get_contiguous_keys(int32_t num) -{ +int32_t *get_contiguous_keys(int32_t num) { int32_t *buf = new int32_t[num]; for(int32_t i = 0; i < num; i++) - buf[i] = i+1; + buf[i] = i; return buf; } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--benchmark") == 0) benchmark = true; } diff --git a/tests/test_vs_proto2.cc b/tests/test_vs_proto2.cc index 53b2498..020dca5 100644 --- a/tests/test_vs_proto2.cc +++ b/tests/test_vs_proto2.cc @@ -1,7 +1,7 @@ /* * upb - a minimalist implementation of protocol buffers. * - * Copyright (c) 2011 Google Inc. See LICENSE for details. + * Copyright (c) 2011-2012 Google Inc. See LICENSE for details. * * A test that verifies that our results are identical to proto2 for a * given proto type and input protobuf. @@ -9,230 +9,87 @@ #define __STDC_LIMIT_MACROS // So we get UINT32_MAX #include <assert.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/wire_format_lite.h> #include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#include <google/protobuf/descriptor.h> -#include <google/protobuf/wire_format_lite.h> #include "benchmarks/google_messages.pb.h" -#include "upb/def.h" -#include "upb/msg.h" +#include "upb/def.hpp" +#include "upb/handlers.hpp" +#include "upb/msg.hpp" +#include "upb/pb/decoder.hpp" #include "upb/pb/glue.h" #include "upb/pb/varint.h" +#include "upb/proto2_bridge.hpp" #include "upb_test.h" -size_t string_size; - -void compare(const google::protobuf::Message& proto2_msg, - void *upb_msg, const upb_msgdef *upb_md); - -void compare_arrays(const google::protobuf::Reflection *r, - const google::protobuf::Message& proto2_msg, - const google::protobuf::FieldDescriptor *proto2_f, - void *upb_msg, upb_fielddef *upb_f) -{ - ASSERT(upb_msg_has(upb_msg, upb_f)); - ASSERT(upb_isseq(upb_f)); - const void *arr = upb_value_getptr(upb_msg_getseq(upb_msg, upb_f)); - const void *iter = upb_seq_begin(arr, upb_f); - for(int i = 0; - i < r->FieldSize(proto2_msg, proto2_f); - i++, iter = upb_seq_next(arr, iter, upb_f)) { - ASSERT(!upb_seq_done(iter)); - upb_value v = upb_seq_get(iter, upb_f); - switch(upb_f->type) { - default: - ASSERT(false); - case UPB_TYPE(DOUBLE): - ASSERT(r->GetRepeatedDouble(proto2_msg, proto2_f, i) == upb_value_getdouble(v)); - break; - case UPB_TYPE(FLOAT): - ASSERT(r->GetRepeatedFloat(proto2_msg, proto2_f, i) == upb_value_getfloat(v)); - break; - case UPB_TYPE(INT64): - case UPB_TYPE(SINT64): - case UPB_TYPE(SFIXED64): - ASSERT(r->GetRepeatedInt64(proto2_msg, proto2_f, i) == upb_value_getint64(v)); - break; - case UPB_TYPE(UINT64): - case UPB_TYPE(FIXED64): - ASSERT(r->GetRepeatedUInt64(proto2_msg, proto2_f, i) == upb_value_getuint64(v)); - break; - case UPB_TYPE(SFIXED32): - case UPB_TYPE(SINT32): - case UPB_TYPE(INT32): - case UPB_TYPE(ENUM): - ASSERT(r->GetRepeatedInt32(proto2_msg, proto2_f, i) == upb_value_getint32(v)); - break; - case UPB_TYPE(FIXED32): - case UPB_TYPE(UINT32): - ASSERT(r->GetRepeatedUInt32(proto2_msg, proto2_f, i) == upb_value_getuint32(v)); - break; - case UPB_TYPE(BOOL): - ASSERT(r->GetRepeatedBool(proto2_msg, proto2_f, i) == upb_value_getbool(v)); - break; - case UPB_TYPE(STRING): - case UPB_TYPE(BYTES): { - std::string str = r->GetRepeatedString(proto2_msg, proto2_f, i); - upb_stdarray *upbstr = (upb_stdarray*)upb_value_getptr(v); - std::string str2(upbstr->ptr, upbstr->len); - string_size += upbstr->len; - ASSERT(str == str2); - break; - } - case UPB_TYPE(GROUP): - case UPB_TYPE(MESSAGE): - ASSERT(upb_dyncast_msgdef(upb_f->def) != NULL); - compare(r->GetRepeatedMessage(proto2_msg, proto2_f, i), - upb_value_getptr(v), upb_downcast_msgdef(upb_f->def)); - } - } - ASSERT(upb_seq_done(iter)); -} - -void compare_values(const google::protobuf::Reflection *r, - const google::protobuf::Message& proto2_msg, - const google::protobuf::FieldDescriptor *proto2_f, - void *upb_msg, upb_fielddef *upb_f) -{ - upb_value v = upb_msg_get(upb_msg, upb_f); - switch(upb_f->type) { - default: - ASSERT(false); - case UPB_TYPE(DOUBLE): - ASSERT(r->GetDouble(proto2_msg, proto2_f) == upb_value_getdouble(v)); - break; - case UPB_TYPE(FLOAT): - ASSERT(r->GetFloat(proto2_msg, proto2_f) == upb_value_getfloat(v)); - break; - case UPB_TYPE(INT64): - case UPB_TYPE(SINT64): - case UPB_TYPE(SFIXED64): - ASSERT(r->GetInt64(proto2_msg, proto2_f) == upb_value_getint64(v)); - break; - case UPB_TYPE(UINT64): - case UPB_TYPE(FIXED64): - ASSERT(r->GetUInt64(proto2_msg, proto2_f) == upb_value_getuint64(v)); - break; - case UPB_TYPE(SFIXED32): - case UPB_TYPE(SINT32): - case UPB_TYPE(INT32): - case UPB_TYPE(ENUM): - ASSERT(r->GetInt32(proto2_msg, proto2_f) == upb_value_getint32(v)); - break; - case UPB_TYPE(FIXED32): - case UPB_TYPE(UINT32): - ASSERT(r->GetUInt32(proto2_msg, proto2_f) == upb_value_getuint32(v)); - break; - case UPB_TYPE(BOOL): - ASSERT(r->GetBool(proto2_msg, proto2_f) == upb_value_getbool(v)); - break; - case UPB_TYPE(STRING): - case UPB_TYPE(BYTES): { - std::string str = r->GetString(proto2_msg, proto2_f); - upb_stdarray *upbstr = (upb_stdarray*)upb_value_getptr(v); - std::string str2(upbstr->ptr, upbstr->len); - string_size += upbstr->len; - ASSERT(str == str2); - break; - } - case UPB_TYPE(GROUP): - case UPB_TYPE(MESSAGE): - // XXX: getstr - compare(r->GetMessage(proto2_msg, proto2_f), - upb_value_getptr(v), upb_downcast_msgdef(upb_f->def)); - } -} - -void compare(const google::protobuf::Message& proto2_msg, - void *upb_msg, const upb_msgdef *upb_md) -{ - const google::protobuf::Reflection *r = proto2_msg.GetReflection(); - const google::protobuf::Descriptor *d = proto2_msg.GetDescriptor(); - - ASSERT(d->field_count() == upb_msgdef_numfields(upb_md)); - upb_msg_iter i; - for(i = upb_msg_begin(upb_md); !upb_msg_done(i); i = upb_msg_next(upb_md, i)) { - upb_fielddef *upb_f = upb_msg_iter_field(i); +void compare_metadata(const google::protobuf::Descriptor* d, + const upb::MessageDef *upb_md) { + ASSERT(d->field_count() == upb_md->field_count()); + for (upb::MessageDef::ConstIterator i(upb_md); !i.Done(); i.Next()) { + const upb::FieldDef* upb_f = i.field(); const google::protobuf::FieldDescriptor *proto2_f = - d->FindFieldByNumber(upb_f->number); - // Make sure the definitions are equal. + d->FindFieldByNumber(upb_f->number()); ASSERT(upb_f); ASSERT(proto2_f); - ASSERT(upb_f->number == proto2_f->number()); - ASSERT(std::string(upb_f->name) == proto2_f->name()); - ASSERT(upb_f->type == proto2_f->type()); - ASSERT(upb_isseq(upb_f) == proto2_f->is_repeated()); - - if(!upb_msg_has(upb_msg, upb_f)) { - if(upb_isseq(upb_f)) - ASSERT(r->FieldSize(proto2_msg, proto2_f) == 0); - else - ASSERT(r->HasField(proto2_msg, proto2_f) == false); - } else { - if(upb_isseq(upb_f)) { - compare_arrays(r, proto2_msg, proto2_f, upb_msg, upb_f); - } else { - ASSERT(r->HasField(proto2_msg, proto2_f) == true); - compare_values(r, proto2_msg, proto2_f, upb_msg, upb_f); - } - } + ASSERT(upb_f->number() == proto2_f->number()); + ASSERT(std::string(upb_f->name()) == proto2_f->name()); + ASSERT(upb_f->type() == static_cast<upb::FieldType>(proto2_f->type())); + ASSERT(upb_f->IsSequence() == proto2_f->is_repeated()); } } -void parse_and_compare(MESSAGE_CIDENT *proto2_msg, - void *upb_msg, const upb_msgdef *upb_md, - const char *str, size_t len, bool allow_jit) -{ +void parse_and_compare(MESSAGE_CIDENT *msg1, MESSAGE_CIDENT *msg2, + const upb::MessageDef *upb_md, + const char *str, size_t len, bool allow_jit) { // Parse to both proto2 and upb. - ASSERT(proto2_msg->ParseFromArray(str, len)); - upb_status status = UPB_STATUS_INIT; - upb_msg_clear(upb_msg, upb_md); - upb_strtomsg(str, len, upb_msg, upb_md, allow_jit, &status); - if (!upb_ok(&status)) { - fprintf(stderr, "Error parsing protobuf: %s", upb_status_getstr(&status)); - exit(1); - } - string_size = 0; - compare(*proto2_msg, upb_msg, upb_md); - printf("Total size: %zd, string size: %zd (%0.2f%%)\n", len, - string_size, (double)string_size / len * 100); - upb_status_uninit(&status); + ASSERT(msg1->ParseFromArray(str, len)); + + upb::Handlers* handlers = upb::Handlers::New(); + upb::RegisterWriteHandlers(handlers, upb_md); + upb::DecoderPlan* plan = upb::DecoderPlan::New(handlers, allow_jit); + upb::StringSource src(str, len); + upb::Decoder decoder; + decoder.ResetPlan(plan, 0); + decoder.ResetInput(src.AllBytes(), msg2); + msg2->Clear(); + ASSERT(decoder.Decode() == UPB_OK); + plan->Unref(); + handlers->Unref(); + + // Would like to just compare the message objects themselves, but + // unfortunately MessageDifferencer is not part of the open-source release of + // proto2, so we compare their serialized strings, which we expect will be + // equivalent. + std::string str1; + std::string str2; + msg1->SerializeToString(&str1); + msg2->SerializeToString(&str2); + ASSERT(str1 == str2); + ASSERT(std::string(str, len) == str2); } -int main(int argc, char *argv[]) -{ - if (argc < 3) { - fprintf(stderr, "Usage: test_vs_proto2 <descriptor file> <message file>\n"); - return 1; +void test_zig_zag() { + for (uint64_t num = 5; num * 1.5 > num; num *= 1.5) { + ASSERT(upb_zzenc_64(num) == + google::protobuf::internal::WireFormatLite::ZigZagEncode64(num)); + if (num < UINT32_MAX) { + ASSERT(upb_zzenc_32(num) == + google::protobuf::internal::WireFormatLite::ZigZagEncode32(num)); + } } - const char *descriptor_file = argv[1]; - const char *message_file = argv[2]; - // Initialize upb state, parse descriptor. - upb_status status = UPB_STATUS_INIT; - upb_symtab *symtab = upb_symtab_new(); - size_t fds_len; - const char *fds = upb_readfile(descriptor_file, &fds_len); - if(fds == NULL) { - fprintf(stderr, "Couldn't read %s.\n", descriptor_file); - return 1; - } - upb_load_descriptor_into_symtab(symtab, fds, fds_len, &status); - if(!upb_ok(&status)) { - fprintf(stderr, "Error importing %s: %s", descriptor_file, - upb_status_getstr(&status)); - return 1; - } - free((void*)fds); +} - const upb_def *def = upb_symtab_lookup(symtab, MESSAGE_NAME); - const upb_msgdef *msgdef; - if(!def || !(msgdef = upb_dyncast_msgdef_const(def))) { - fprintf(stderr, "Error finding symbol '%s'.\n", MESSAGE_NAME); +int main(int argc, char *argv[]) +{ + if (argc < 2) { + fprintf(stderr, "Usage: test_vs_proto2 <message file>\n"); return 1; } + const char *message_file = argv[1]; // Read the message data itself. size_t len; @@ -242,32 +99,25 @@ int main(int argc, char *argv[]) return 1; } + MESSAGE_CIDENT msg1; + MESSAGE_CIDENT msg2; + + const upb::MessageDef* m = upb::proto2_bridge::NewFinalMessageDef(msg1, &m); + + compare_metadata(msg1.GetDescriptor(), m); + // Run twice to test proper object reuse. - MESSAGE_CIDENT proto2_msg; - void *upb_msg = upb_stdmsg_new(msgdef); - parse_and_compare(&proto2_msg, upb_msg, msgdef, str, len, true); - parse_and_compare(&proto2_msg, upb_msg, msgdef, str, len, false); - parse_and_compare(&proto2_msg, upb_msg, msgdef, str, len, true); - parse_and_compare(&proto2_msg, upb_msg, msgdef, str, len, false); + parse_and_compare(&msg1, &msg2, m, str, len, true); + parse_and_compare(&msg1, &msg2, m, str, len, false); + parse_and_compare(&msg1, &msg2, m, str, len, true); + parse_and_compare(&msg1, &msg2, m, str, len, false); printf("All tests passed, %d assertions.\n", num_assertions); - upb_stdmsg_free(upb_msg, msgdef); - upb_def_unref(UPB_UPCAST(msgdef)); + m->Unref(&m); free((void*)str); - upb_symtab_unref(symtab); - upb_status_uninit(&status); - // Test Zig-Zag encoding/decoding. - for (uint64_t num = 5; num * 1.5 > num; num *= 1.5) { - ASSERT(upb_zzenc_64(num) == - google::protobuf::internal::WireFormatLite::ZigZagEncode64(num)); - if (num < UINT32_MAX) { - ASSERT(upb_zzenc_32(num) == - google::protobuf::internal::WireFormatLite::ZigZagEncode32(num)); - } - } + test_zig_zag(); google::protobuf::ShutdownProtobufLibrary(); - return 0; } diff --git a/tests/tests.c b/tests/tests.c deleted file mode 100644 index 12ff4bb..0000000 --- a/tests/tests.c +++ /dev/null @@ -1,121 +0,0 @@ - - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include "upb/def.h" -#include "upb/handlers.h" -#include "upb/pb/decoder.h" -#include "upb/pb/glue.h" -#include "upb_test.h" - -const char *descriptor_file; - -static upb_symtab *load_test_proto() { - upb_symtab *s = upb_symtab_new(); - ASSERT(s); - upb_status status = UPB_STATUS_INIT; - if (!upb_load_descriptor_file_into_symtab(s, descriptor_file, &status)) { - fprintf(stderr, "Error loading descriptor file: %s\n", - upb_status_getstr(&status)); - exit(1); - } - upb_status_uninit(&status); - return s; -} - -static upb_flow_t upb_test_onvalue(void *c, upb_value fval, upb_value val) { - (void)c; - (void)fval; - (void)val; - return UPB_CONTINUE; -} - -static void test_upb_jit() { - upb_symtab *s = load_test_proto(); - const upb_def *def = upb_symtab_lookup(s, "SimplePrimitives"); - ASSERT(def); - - upb_handlers *h = upb_handlers_new(); - upb_handlerset hset = {NULL, NULL, &upb_test_onvalue, NULL, NULL, NULL, NULL}; - upb_handlers_reghandlerset(h, upb_downcast_msgdef_const(def), &hset); - upb_decoderplan *p = upb_decoderplan_new(h, true); -#ifdef UPB_USE_JIT_X64 - ASSERT(upb_decoderplan_hasjitcode(p)); -#else - ASSERT(!upb_decoderplan_hasjitcode(p)); -#endif - upb_decoderplan_unref(p); - upb_symtab_unref(s); - upb_def_unref(def); - upb_handlers_unref(h); -} - -static void test_upb_symtab() { - upb_symtab *s = load_test_proto(); - - // Test cycle detection by making a cyclic def's main refcount go to zero - // and then be incremented to one again. - const upb_def *def = upb_symtab_lookup(s, "A"); - ASSERT(def); - upb_symtab_unref(s); - const upb_msgdef *m = upb_downcast_msgdef_const(def); - upb_msg_iter i = upb_msg_begin(m); - ASSERT(!upb_msg_done(i)); - upb_fielddef *f = upb_msg_iter_field(i); - ASSERT(upb_hassubdef(f)); - upb_def *def2 = f->def; - - i = upb_msg_next(m, i); - ASSERT(upb_msg_done(i)); // "A" should only have one field. - - ASSERT(upb_downcast_msgdef(def2)); - upb_def_ref(def2); - upb_def_unref(def); - upb_def_unref(def2); -} - -static void test_upb_two_fielddefs() { - upb_fielddef *f1 = upb_fielddef_new(); - upb_fielddef *f2 = upb_fielddef_new(); - - ASSERT(upb_fielddef_ismutable(f1)); - upb_fielddef_setname(f1, ""); - upb_fielddef_setnumber(f1, 1937); - upb_fielddef_settype(f1, UPB_TYPE(FIXED64)); - upb_fielddef_setlabel(f1, UPB_LABEL(REPEATED)); - upb_fielddef_settypename(f1, ""); - ASSERT(upb_fielddef_number(f1) == 1937); - - ASSERT(upb_fielddef_ismutable(f2)); - upb_fielddef_setname(f2, ""); - upb_fielddef_setnumber(f2, 1572); - upb_fielddef_settype(f2, UPB_TYPE(BYTES)); - upb_fielddef_setlabel(f2, UPB_LABEL(REPEATED)); - upb_fielddef_settypename(f2, ""); - ASSERT(upb_fielddef_number(f2) == 1572); - - upb_fielddef_unref(f1); - upb_fielddef_unref(f2); -} - -int main(int argc, char *argv[]) -{ - if (argc < 2) { - fprintf(stderr, "Usage: test_cpp <descriptor file>\n"); - return 1; - } - descriptor_file = argv[1]; -#define TEST(func) do { \ - int assertions_before = num_assertions; \ - printf("Running " #func "..."); fflush(stdout); \ - func(); \ - printf("ok (%d assertions).\n", num_assertions - assertions_before); \ - } while (0) - - TEST(test_upb_symtab); - TEST(test_upb_jit); - TEST(test_upb_two_fielddefs); - printf("All tests passed (%d assertions).\n", num_assertions); - return 0; -} diff --git a/tests/upb_test.h b/tests/upb_test.h index 2bd340e..652977b 100644 --- a/tests/upb_test.h +++ b/tests/upb_test.h @@ -7,6 +7,7 @@ #ifndef UPB_TEST_H_ #define UPB_TEST_H_ +#include <stdio.h> #include <stdlib.h> #ifdef __cplusplus @@ -18,9 +19,28 @@ int num_assertions = 0; ++num_assertions; \ if (!(expr)) { \ fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, "expr: %s\n", #expr); \ abort(); \ } \ -} while(0) +} while (0) + +#define ASSERT_NOCOUNT(expr) do { \ + if (!(expr)) { \ + fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, "expr: %s\n", #expr); \ + abort(); \ + } \ +} while (0) + +#define ASSERT_STATUS(expr, status) do { \ + ++num_assertions; \ + if (!(expr)) { \ + fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, "expr: %s\n", #expr); \ + fprintf(stderr, "failed status: %s\n", upb_status_getstr(status)); \ + abort(); \ + } \ +} while (0) #ifdef __cplusplus } /* extern "C" */ |