diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_cpp.cc | 20 | ||||
-rw-r--r-- | tests/test_decoder.cc | 258 | ||||
-rw-r--r-- | tests/test_decoder_schema.proto | 64 | ||||
-rw-r--r-- | tests/test_def.c | 137 | ||||
-rw-r--r-- | tests/test_table.cc | 13 | ||||
-rw-r--r-- | tests/test_varint.c | 13 | ||||
-rw-r--r-- | tests/test_vs_proto2.cc | 39 | ||||
-rw-r--r-- | tests/testmain.cc | 18 | ||||
-rw-r--r-- | tests/upb_test.h | 21 |
9 files changed, 429 insertions, 154 deletions
diff --git a/tests/test_cpp.cc b/tests/test_cpp.cc index fb0916d..59603d9 100644 --- a/tests/test_cpp.cc +++ b/tests/test_cpp.cc @@ -8,20 +8,20 @@ */ #include <stdio.h> +#include <string.h> #include <iostream> -#include "upb/bytestream.hpp" -#include "upb/def.hpp" -#include "upb/handlers.hpp" -#include "upb/upb.hpp" -#include "upb/pb/decoder.hpp" -#include "upb/pb/glue.hpp" +#include "upb/bytestream.h" +#include "upb/def.h" +#include "upb/handlers.h" +#include "upb/pb/glue.h" #include "upb_test.h" +#include "upb/upb.h" static void TestSymbolTable(const char *descriptor_file) { upb::SymbolTable *s = upb::SymbolTable::New(&s); upb::Status status; if (!upb::LoadDescriptorFileIntoSymtab(s, descriptor_file, &status)) { - std::cerr << "Couldn't load descriptor: " << status; + std::cerr << "Couldn't load descriptor: " << status.GetString(); exit(1); } const upb::MessageDef *md = s->LookupMessage("A", &md); @@ -41,7 +41,9 @@ static void TestByteStream() { free(str); } -int main(int argc, char *argv[]) { +extern "C" { + +int run_tests(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: test_cpp <descriptor file>\n"); return 1; @@ -50,3 +52,5 @@ int main(int argc, char *argv[]) { TestByteStream(); return 0; } + +} diff --git a/tests/test_decoder.cc b/tests/test_decoder.cc index 13403bb..d42c0fe 100644 --- a/tests/test_decoder.cc +++ b/tests/test_decoder.cc @@ -7,6 +7,7 @@ * 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 @@ -35,6 +36,9 @@ #include "upb/pb/varint.h" #include "upb/upb.h" #include "upb_test.h" +#include "third_party/upb/tests/test_decoder_schema.upb.h" + +uint32_t filter_hash = 0; // Copied from decoder.c, since this is not a public interface. typedef struct { @@ -186,66 +190,78 @@ 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) { \ - indent(closure); \ - output.appendf("%" PRIu32 ":%" fmt "\n", \ - upb_value_getuint32(fval), upb_value_get ## member(val)); \ - return UPB_CONTINUE; \ +#define NUMERIC_VALUE_HANDLER(member, ctype, fmt) \ + bool value_ ## member(void *closure, void *fval, ctype val) { \ + indent(closure); \ + uint32_t *num = static_cast<uint32_t*>(fval); \ + output.appendf("%" PRIu32 ":%" fmt "\n", *num, val); \ + return true; \ } -VALUE_HANDLER(uint32, PRIu32) -VALUE_HANDLER(uint64, PRIu64) -VALUE_HANDLER(int32, PRId32) -VALUE_HANDLER(int64, PRId64) -VALUE_HANDLER(float, "g") -VALUE_HANDLER(double, "g") +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") -upb_flow_t value_bool(void *closure, upb_value fval, upb_value val) { +bool value_bool(void *closure, void *fval, bool val) { indent(closure); - output.appendf("%" PRIu32 ":%s\n", - upb_value_getuint32(fval), - upb_value_getbool(val) ? "true" : "false"); - return UPB_CONTINUE; + uint32_t *num = static_cast<uint32_t*>(fval); + output.appendf("%" PRIu32 ":%s\n", *num, val ? "true" : "false"); + return true; } -upb_flow_t value_string(void *closure, upb_value fval, upb_value val) { - // Note: won't work with strings that contain NULL. +void* startstr(void *closure, void *fval, size_t size_hint) { indent(closure); - char *str = upb_byteregion_strdup(upb_value_getbyteregion(val)); - output.appendf("%" PRIu32 ":%s\n", upb_value_getuint32(fval), str); - free(str); - return UPB_CONTINUE; + uint32_t *num = static_cast<uint32_t*>(fval); + output.appendf("%" PRIu32 ":(%zu)\"", *num, size_hint); + return ((int*)closure) + 1; +} + +size_t value_string(void *closure, void *fval, const char *buf, size_t n) { + output.append(buf, n); + return n; +} + +bool endstr(void *closure, void *fval) { + UPB_UNUSED(fval); + output.append("\"\n"); + return true; } -upb_sflow_t startsubmsg(void *closure, upb_value fval) { +void* startsubmsg(void *closure, void *fval) { indent(closure); - output.appendf("%" PRIu32 ":{\n", upb_value_getuint32(fval)); - return UPB_CONTINUE_WITH(((int*)closure) + 1); + uint32_t *num = static_cast<uint32_t*>(fval); + output.appendf("%" PRIu32 ":{\n", *num); + return ((int*)closure) + 1; } -upb_flow_t endsubmsg(void *closure, upb_value fval) { +bool endsubmsg(void *closure, void *fval) { + UPB_UNUSED(fval); indent(closure); output.append("}\n"); - return UPB_CONTINUE; + return true; } -upb_sflow_t startseq(void *closure, upb_value fval) { +void* startseq(void *closure, void *fval) { indent(closure); - output.appendf("%" PRIu32 ":[\n", upb_value_getuint32(fval)); - return UPB_CONTINUE_WITH(((int*)closure) + 1); + uint32_t *num = static_cast<uint32_t*>(fval); + output.appendf("%" PRIu32 ":[\n", *num); + return ((int*)closure) + 1; } -upb_flow_t endseq(void *closure, upb_value fval) { +bool endseq(void *closure, void *fval) { + UPB_UNUSED(fval); indent(closure); output.append("]\n"); - return UPB_CONTINUE; + return true; } -upb_flow_t startmsg(void *closure) { +bool startmsg(void *closure) { indent(closure); output.append("<\n"); - return UPB_CONTINUE; + return true; } void endmsg(void *closure, upb_status *status) { @@ -254,14 +270,23 @@ void endmsg(void *closure, upb_status *status) { 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); +void free_uint32(void *val) { + uint32_t *u32 = static_cast<uint32_t*>(val); + delete u32; +} + +template<class T> +void doreg(upb_handlers *h, uint32_t num, + typename upb::Handlers::Value<T>::Handler *handler) { + const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), num); 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)); + ASSERT(h->SetValueHandler<T>(f, handler, new uint32_t(num), free_uint32)); + if (f->IsSequence()) { + ASSERT(h->SetStartSequenceHandler( + f, &startseq, new uint32_t(num), free_uint32)); + ASSERT(h->SetEndSequenceHandler( + f, &endseq, new uint32_t(num), free_uint32)); + } } // The repeated field number to correspond to the given non-repeated field @@ -273,57 +298,81 @@ uint32_t rep_fn(uint32_t fn) { #define NOP_FIELD 40 #define UNKNOWN_FIELD 666 -void reg(upb_mhandlers *m, upb_fieldtype_t type, upb_value_handler *handler) { +template <class T> +void reg(upb_handlers *h, upb_fieldtype_t type, + typename upb::Handlers::Value<T>::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); + doreg<T>(h, type, handler); + doreg<T>(h, rep_fn(type), 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); +void reg_subm(upb_handlers *h, uint32_t num) { + const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), num); 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)); + if (f->IsSequence()) { + ASSERT(h->SetStartSequenceHandler( + f, &startseq, new uint32_t(num), free_uint32)); + ASSERT(h->SetEndSequenceHandler( + f, &endseq, new uint32_t(num), free_uint32)); + } + ASSERT(h->SetStartSubMessageHandler( + f, &startsubmsg, new uint32_t(num), free_uint32)); + ASSERT(h->SetEndSubMessageHandler( + f, &endsubmsg, new uint32_t(num), free_uint32)); + ASSERT(upb_handlers_setsubhandlers(h, f, h)); } -void reghandlers(upb_mhandlers *m) { - upb_mhandlers_setstartmsg(m, &startmsg); - upb_mhandlers_setendmsg(m, &endmsg); +void reg_str(upb_handlers *h, uint32_t num) { + const upb_fielddef *f = upb_msgdef_itof(upb_handlers_msgdef(h), num); + ASSERT(f); + if (f->IsSequence()) { + ASSERT(h->SetStartSequenceHandler( + f, &startseq, new uint32_t(num), free_uint32)); + ASSERT(h->SetEndSequenceHandler( + f, &endseq, new uint32_t(num), free_uint32)); + } + ASSERT(h->SetStartStringHandler( + f, &startstr, new uint32_t(num), free_uint32)); + ASSERT(h->SetEndStringHandler( + f, &endstr, new uint32_t(num), free_uint32)); + ASSERT(h->SetStringHandler( + f, &value_string, new uint32_t(num), free_uint32)); +} + +void reghandlers(upb_handlers *h) { + upb_handlers_setstartmsg(h, &startmsg); + upb_handlers_setendmsg(h, &endmsg); // 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); + reg<double> (h, UPB_TYPE(DOUBLE), &value_double); + reg<float> (h, UPB_TYPE(FLOAT), &value_float); + reg<int64_t> (h, UPB_TYPE(INT64), &value_int64); + reg<uint64_t>(h, UPB_TYPE(UINT64), &value_uint64); + reg<int32_t> (h, UPB_TYPE(INT32) , &value_int32); + reg<uint64_t>(h, UPB_TYPE(FIXED64), &value_uint64); + reg<uint32_t>(h, UPB_TYPE(FIXED32), &value_uint32); + reg<bool> (h, UPB_TYPE(BOOL), &value_bool); + reg<uint32_t>(h, UPB_TYPE(UINT32), &value_uint32); + reg<int32_t> (h, UPB_TYPE(ENUM), &value_int32); + reg<int32_t> (h, UPB_TYPE(SFIXED32), &value_int32); + reg<int64_t> (h, UPB_TYPE(SFIXED64), &value_int64); + reg<int32_t> (h, UPB_TYPE(SINT32), &value_int32); + reg<int64_t> (h, UPB_TYPE(SINT64), &value_int64); + + reg_str(h, UPB_TYPE(STRING)); + reg_str(h, UPB_TYPE(BYTES)); + reg_str(h, rep_fn(UPB_TYPE(STRING))); + reg_str(h, rep_fn(UPB_TYPE(BYTES))); // 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); + reg_subm(h, UPB_TYPE(MESSAGE)); + reg_subm(h, rep_fn(UPB_TYPE(MESSAGE))); - // 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); + // For NOP_FIELD we register no handlers, so we can pad a proto freely without + // changing the output. } @@ -413,22 +462,32 @@ upb_byteregion *upb_seamsrc_allbytes(upb_seamsrc *s) { /* Running of test cases ******************************************************/ upb_decoderplan *plan; + +uint32_t Hash(const buffer& proto, const buffer* expected_output) { + uint32_t hash = MurmurHash2(proto.buf(), proto.len(), 0); + if (expected_output) + hash = MurmurHash2(expected_output->buf(), expected_output->len(), hash); + bool hasjit = upb_decoderplan_hasjitcode(plan); + hash = MurmurHash2(&hasjit, 1, hash); + return hash; +} + #define LINE(x) x "\n" void run_decoder(const buffer& proto, const buffer* expected_output) { + testhash = Hash(proto, expected_output); + if (filter_hash && testhash != filter_hash) return; upb_seamsrc src; upb_seamsrc_init(&src, proto.buf(), proto.len()); upb_decoder d; upb_decoder_init(&d); - upb_decoder_resetplan(&d, plan, 0); + upb_decoder_resetplan(&d, plan); 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); output.clear(); upb_decoder_resetinput(&d, input, &closures[0]); - upb_success_t success = UPB_SUSPENDED; - while (success == UPB_SUSPENDED) - success = upb_decoder_decode(&d); + upb_success_t success = upb_decoder_decode(&d); ASSERT(upb_ok(upb_decoder_status(&d)) == (success == UPB_OK)); if (expected_output) { ASSERT_STATUS(success == UPB_OK, upb_decoder_status(&d)); @@ -448,6 +507,7 @@ void run_decoder(const buffer& proto, const buffer* expected_output) { } upb_decoder_uninit(&d); upb_seamsrc_uninit(&src); + testhash = 0; } const static buffer thirty_byte_nop = buffer(cat( @@ -777,35 +837,47 @@ void run_tests() { test_valid(); } -int main() { +extern "C" { + +int run_tests(int argc, char *argv[]) { + if (argc > 1) + filter_hash = strtol(argv[1], NULL, 16); 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); + upb_handlers *h = upb_handlers_new(UPB_TEST_DECODER_EMPTYMESSAGE, &h); + bool ok = upb_handlers_freeze(&h, 1, NULL); + ASSERT(ok); + plan = upb_decoderplan_new(h, true); + upb_handlers_unref(h, &h); + upb_decoderplan_unref(plan); + + // Construct decoder plan. + h = upb_handlers_new(UPB_TEST_DECODER_DECODERTEST, &h); + reghandlers(h); + ok = upb_handlers_freeze(&h, 1, NULL); // Test without JIT. plan = upb_decoderplan_new(h, false); + ASSERT(!upb_decoderplan_hasjitcode(plan)); run_tests(); upb_decoderplan_unref(plan); +#ifdef UPB_USE_JIT_X64 // 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); +#endif plan = NULL; printf("All tests passed, %d assertions.\n", num_assertions); - upb_handlers_unref(h); + upb_handlers_unref(h, &h); return 0; } + +} diff --git a/tests/test_decoder_schema.proto b/tests/test_decoder_schema.proto new file mode 100644 index 0000000..50bfca9 --- /dev/null +++ b/tests/test_decoder_schema.proto @@ -0,0 +1,64 @@ +// +// upb - a minimalist implementation of protocol buffers. +// +// Copyright (c) 2012 Google Inc. See LICENSE for details. +// Author: Josh Haberman <jhaberman@gmail.com> +// +// Schema used in test_decoder.cc. It contains two fields (one optional +// and one repeated) for each type. + +package upb.test_decoder; + +message M { + optional M m = 1; +} + +enum E { + FOO = 1; +} + +message EmptyMessage {} + +message DecoderTest { + optional double f_double = 1; + optional float f_float = 2; + optional int64 f_int64 = 3; + optional uint64 f_uint64 = 4; + optional int32 f_int32 = 5; + optional fixed64 f_fixed64 = 6; + optional fixed32 f_fixed32 = 7; + optional bool f_bool = 8; + optional string f_string = 9; + optional bytes f_bytes = 12; + optional uint32 f_uint32 = 13; + optional sfixed32 f_sfixed32 = 15; + optional sfixed64 f_sfixed64 = 16; + optional sint32 f_sint32 = 17; + optional sint64 f_sint64 = 18; + + optional DecoderTest f_message = 11; + optional E f_enum = 14; + + + repeated double r_double = 536869912; + repeated float r_float = 536869913; + repeated int64 r_int64 = 536869914; + repeated uint64 r_uint64 = 536869915; + repeated int32 r_int32 = 536869916; + repeated fixed64 r_fixed64 = 536869917; + repeated fixed32 r_fixed32 = 536869918; + repeated bool r_bool = 536869919; + repeated string r_string = 536869920; + repeated bytes r_bytes = 536869923; + repeated uint32 r_uint32 = 536869924; + repeated sfixed32 r_sfixed32 = 536869926; + repeated sfixed64 r_sfixed64 = 536869927; + repeated sint32 r_sint32 = 536869928; + repeated sint64 r_sint64 = 536869929; + + repeated DecoderTest r_message = 536869922; + repeated E r_enum = 536869925; + + // To allow arbitrary padding. + optional string nop_field = 40; +} diff --git a/tests/test_def.c b/tests/test_def.c index f60d556..7f089d7 100644 --- a/tests/test_def.c +++ b/tests/test_def.c @@ -18,7 +18,7 @@ const char *descriptor_file; static void test_empty_symtab() { upb_symtab *s = upb_symtab_new(&s); int count; - const upb_def **defs = upb_symtab_getdefs(s, &count, UPB_DEF_ANY, NULL); + const upb_def **defs = upb_symtab_getdefs(s, UPB_DEF_ANY, NULL, &count); ASSERT(count == 0); free(defs); upb_symtab_unref(s, &s); @@ -31,7 +31,7 @@ static upb_symtab *load_test_proto(void *owner) { 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); + ASSERT(false); } upb_status_uninit(&status); return s; @@ -44,27 +44,30 @@ static void test_cycles() { // and then be incremented to one again. const upb_def *def = upb_symtab_lookup(s, "A", &def); ASSERT(def); - ASSERT(upb_def_isfinalized(def)); + ASSERT(upb_def_isfrozen(def)); upb_symtab_unref(s, &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); + const upb_msgdef *m = upb_downcast_msgdef(def); + const upb_fielddef *f = upb_msgdef_itof(m, 1); ASSERT(f); - ASSERT(upb_hassubdef(f)); + ASSERT(upb_fielddef_hassubdef(f)); const upb_def *def2 = upb_fielddef_subdef(f); - ASSERT(upb_downcast_msgdef_const(def2)); + ASSERT(upb_downcast_msgdef(def2)); ASSERT(strcmp(upb_def_fullname(def2), "B") == 0); upb_def_ref(def2, &def2); upb_def_unref(def, &def); + + // We know "def" is still alive because it's reachable from def2. + ASSERT(strcmp(upb_def_fullname(def), "A") == 0); upb_def_unref(def2, &def2); } static void test_fielddef_unref() { upb_symtab *s = load_test_proto(&s); const upb_msgdef *md = upb_symtab_lookupmsg(s, "A", &md); - upb_fielddef *f = upb_msgdef_itof(md, 1); + const 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 @@ -72,7 +75,7 @@ static void test_fielddef_unref() { upb_symtab_unref(s, &s); upb_msgdef_unref(md, &md); // Check that md is still alive. - ASSERT(strcmp(upb_def_fullname(UPB_UPCAST(md)), "A") == 0); + 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); @@ -82,14 +85,14 @@ static void test_fielddef_accessors() { upb_fielddef *f1 = upb_fielddef_new(&f1); upb_fielddef *f2 = upb_fielddef_new(&f2); - ASSERT(upb_fielddef_ismutable(f1)); + ASSERT(!upb_fielddef_isfrozen(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)); + ASSERT(!upb_fielddef_isfrozen(f2)); upb_fielddef_setname(f2, "f2"); upb_fielddef_setnumber(f2, 1572); upb_fielddef_settype(f2, UPB_TYPE(BYTES)); @@ -98,6 +101,12 @@ static void test_fielddef_accessors() { upb_fielddef_unref(f1, &f1); upb_fielddef_unref(f2, &f2); + + // Test that we don't leak an unresolved subdef name. + f1 = upb_fielddef_new(&f1); + upb_fielddef_settype(f1, UPB_TYPE(MESSAGE)); + upb_fielddef_setsubdefname(f1, "YO"); + upb_fielddef_unref(f1, &f1); } static upb_fielddef *newfield( @@ -108,23 +117,23 @@ static upb_fielddef *newfield( upb_fielddef_setnumber(f, num); upb_fielddef_settype(f, type); upb_fielddef_setlabel(f, label); - upb_fielddef_setsubtypename(f, type_name); + upb_fielddef_setsubdefname(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); + 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); + upb_def_setfullname(upb_upcast(e), name); return e; } -void test_replacement() { +static void test_replacement() { upb_symtab *s = upb_symtab_new(&s); upb_msgdef *m = upb_msgdef_newnamed("MyMessage", &s); @@ -133,15 +142,15 @@ void test_replacement() { 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_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)}; + 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); @@ -159,7 +168,95 @@ void test_replacement() { upb_symtab_unref(s, &s); } -int main(int argc, char *argv[]) { +static void test_freeze_free() { + // Test that freeze frees defs that were only being kept alive by virtue of + // sharing a group with other defs that are being frozen. + upb_msgdef *m1 = upb_msgdef_newnamed("M1", &m1); + upb_msgdef *m2 = upb_msgdef_newnamed("M2", &m2); + upb_msgdef *m3 = upb_msgdef_newnamed("M3", &m3); + upb_msgdef *m4 = upb_msgdef_newnamed("M4", &m4); + + // Freeze M4 and make M1 point to it. + upb_def_freeze((upb_def*const*)&m4, 1, NULL); + + upb_fielddef *f = upb_fielddef_new(&f); + upb_fielddef_settype(f, UPB_TYPE_MESSAGE); + ASSERT(upb_fielddef_setnumber(f, 1)); + ASSERT(upb_fielddef_setname(f, "foo")); + ASSERT(upb_fielddef_setsubdef(f, upb_upcast(m4))); + + ASSERT(upb_msgdef_addfield(m1, f, &f)); + + // After this unref, M1 is the only thing keeping M4 alive. + upb_msgdef_unref(m4, &m4); + + // Force M1/M2/M3 into a single mutable refcounting group. + f = upb_fielddef_new(&f); + upb_fielddef_settype(f, UPB_TYPE_MESSAGE); + ASSERT(upb_fielddef_setnumber(f, 1)); + ASSERT(upb_fielddef_setname(f, "foo")); + + ASSERT(upb_fielddef_setsubdef(f, upb_upcast(m1))); + ASSERT(upb_fielddef_setsubdef(f, upb_upcast(m2))); + ASSERT(upb_fielddef_setsubdef(f, upb_upcast(m3))); + + // Make M3 cyclic with itself. + ASSERT(upb_msgdef_addfield(m3, f, &f)); + + // These will be kept alive since they are in the same refcounting group as + // M3, which still has a ref. Note: this behavior is not guaranteed by the + // API, but true in practice with its current implementation. + upb_msgdef_unref(m1, &m1); + upb_msgdef_unref(m2, &m2); + + // Test that they are still alive (NOT allowed by the API). + ASSERT(strcmp("M1", upb_def_fullname(upb_upcast(m1))) == 0); + ASSERT(strcmp("M2", upb_def_fullname(upb_upcast(m2))) == 0); + + // Freeze M3. If the test leaked no memory, then freeing m1 and m2 was + // successful. + ASSERT(upb_def_freeze((upb_def*const*)&m3, 1, NULL)); + + upb_msgdef_unref(m3, &m3); +} + +static void test_partial_freeze() { + // Test that freeze of only part of the graph correctly adjusts objects that + // point to the newly-frozen objects. + upb_msgdef *m1 = upb_msgdef_newnamed("M1", &m1); + upb_msgdef *m2 = upb_msgdef_newnamed("M2", &m2); + upb_msgdef *m3 = upb_msgdef_newnamed("M3", &m3); + + upb_fielddef *f1 = upb_fielddef_new(&f1); + upb_fielddef_settype(f1, UPB_TYPE_MESSAGE); + ASSERT(upb_fielddef_setnumber(f1, 1)); + ASSERT(upb_fielddef_setname(f1, "f1")); + ASSERT(upb_fielddef_setsubdef(f1, upb_upcast(m1))); + + upb_fielddef *f2 = upb_fielddef_new(&f2); + upb_fielddef_settype(f2, UPB_TYPE_MESSAGE); + ASSERT(upb_fielddef_setnumber(f2, 2)); + ASSERT(upb_fielddef_setname(f2, "f2")); + ASSERT(upb_fielddef_setsubdef(f2, upb_upcast(m2))); + + ASSERT(upb_msgdef_addfield(m3, f1, &f1)); + ASSERT(upb_msgdef_addfield(m3, f2, &f2)); + + // Freeze M1 and M2, which should cause the group to be split + // and m3 (left mutable) to take references on m1 and m2. + upb_def *defs[] = {upb_upcast(m1), upb_upcast(m2)}; + ASSERT(upb_def_freeze(defs, 2, NULL)); + + ASSERT(upb_msgdef_isfrozen(m1)); + ASSERT(upb_msgdef_isfrozen(m2)); + ASSERT(!upb_msgdef_isfrozen(m3)); + + upb_msgdef_unref(m1, &m1); + upb_msgdef_unref(m2, &m2); + upb_msgdef_unref(m3, &m3); +} + +int run_tests(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: test_def <test.proto.pb>\n"); return 1; @@ -170,5 +267,7 @@ int main(int argc, char *argv[]) { test_fielddef_accessors(); test_fielddef_unref(); test_replacement(); + test_freeze_free(); + test_partial_freeze(); return 0; } diff --git a/tests/test_table.cc b/tests/test_table.cc index 2538e35..bb75fc4 100644 --- a/tests/test_table.cc +++ b/tests/test_table.cc @@ -34,7 +34,7 @@ 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); + upb_strtable_init(&table, UPB_CTYPE_INT32); std::set<std::string> all; for(size_t i = 0; i < num_to_insert; i++) { const std::string& key = keys[i]; @@ -77,7 +77,7 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) { 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); + upb_inttable_init(&table, UPB_CTYPE_UINT32); for(size_t i = 0; i < num_entries; i++) { int32_t key = keys[i]; largest_key = UPB_MAX((int32_t)largest_key, key); @@ -103,7 +103,7 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) { 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); + if (ret) ASSERT(upb_value_getuint32(val) == (uint32_t)keys[i] * 2); hm.erase(keys[i]); m.erase(keys[i]); } @@ -244,7 +244,9 @@ int32_t *get_contiguous_keys(int32_t num) { return buf; } -int main(int argc, char *argv[]) { +extern "C" { + +int run_tests(int argc, char *argv[]) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--benchmark") == 0) benchmark = true; } @@ -292,4 +294,7 @@ int main(int argc, char *argv[]) { } test_inttable(keys4, 64, "Table size: 64, keys: 1-32 and 10133-10164 ====\n"); delete[] keys4; + return 0; +} + } diff --git a/tests/test_varint.c b/tests/test_varint.c index 0fc93f0..bdbc573 100644 --- a/tests/test_varint.c +++ b/tests/test_varint.c @@ -86,16 +86,19 @@ static void test_varint_decoder(upb_decoderet (*decoder)(const char*)) { printf("ok.\n"); \ } \ -TEST_VARINT_DECODER(branch32); -TEST_VARINT_DECODER(branch64); +TEST_VARINT_DECODER(check2_branch32); +TEST_VARINT_DECODER(check2_branch64); TEST_VARINT_DECODER(check2_wright); TEST_VARINT_DECODER(check2_massimino); -int main() { - test_branch32(); - test_branch64(); +int run_tests(int argc, char *argv[]) { + UPB_UNUSED(argc); + UPB_UNUSED(argv); + test_check2_branch32(); + test_check2_branch64(); test_check2_wright(); test_check2_massimino(); + return 0; } #if 0 diff --git a/tests/test_vs_proto2.cc b/tests/test_vs_proto2.cc index 020dca5..5eca399 100644 --- a/tests/test_vs_proto2.cc +++ b/tests/test_vs_proto2.cc @@ -10,19 +10,19 @@ #define __STDC_LIMIT_MACROS // So we get UINT32_MAX #include <assert.h> #include <google/protobuf/descriptor.h> +#include <google/protobuf/message.h> #include <google/protobuf/wire_format_lite.h> #include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include "benchmarks/google_messages.pb.h" -#include "upb/def.hpp" -#include "upb/handlers.hpp" -#include "upb/msg.hpp" -#include "upb/pb/decoder.hpp" +#include "bindings/cpp/upb/pb/decoder.hpp" +#include "upb/def.h" +#include "upb/google/bridge.h" +#include "upb/handlers.h" #include "upb/pb/glue.h" #include "upb/pb/varint.h" -#include "upb/proto2_bridge.hpp" #include "upb_test.h" void compare_metadata(const google::protobuf::Descriptor* d, @@ -36,28 +36,25 @@ void compare_metadata(const google::protobuf::Descriptor* d, ASSERT(proto2_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->type() == static_cast<upb::FieldDef::Type>(proto2_f->type())); ASSERT(upb_f->IsSequence() == proto2_f->is_repeated()); } } void parse_and_compare(MESSAGE_CIDENT *msg1, MESSAGE_CIDENT *msg2, - const upb::MessageDef *upb_md, + const upb::Handlers *handlers, const char *str, size_t len, bool allow_jit) { // Parse to both proto2 and upb. 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.ResetPlan(plan); 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 @@ -83,7 +80,9 @@ void test_zig_zag() { } -int main(int argc, char *argv[]) +extern "C" { + +int run_tests(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "Usage: test_vs_proto2 <message file>\n"); @@ -102,18 +101,18 @@ int main(int argc, char *argv[]) MESSAGE_CIDENT msg1; MESSAGE_CIDENT msg2; - const upb::MessageDef* m = upb::proto2_bridge::NewFinalMessageDef(msg1, &m); + const upb::Handlers* h = upb::google::NewWriteHandlers(msg1, &h); - compare_metadata(msg1.GetDescriptor(), m); + compare_metadata(msg1.GetDescriptor(), h->message_def()); // Run twice to test proper object reuse. - 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); + parse_and_compare(&msg1, &msg2, h, str, len, false); + parse_and_compare(&msg1, &msg2, h, str, len, true); + parse_and_compare(&msg1, &msg2, h, str, len, false); + parse_and_compare(&msg1, &msg2, h, str, len, true); printf("All tests passed, %d assertions.\n", num_assertions); - m->Unref(&m); + h->Unref(&h); free((void*)str); test_zig_zag(); @@ -121,3 +120,5 @@ int main(int argc, char *argv[]) google::protobuf::ShutdownProtobufLibrary(); return 0; } + +} diff --git a/tests/testmain.cc b/tests/testmain.cc new file mode 100644 index 0000000..ac0b313 --- /dev/null +++ b/tests/testmain.cc @@ -0,0 +1,18 @@ +// Copyright 2012 Google Inc. All Rights Reserved. +// Author: haberman@google.com (Josh Haberman) + +#include <stdlib.h> +#ifdef USE_GOOGLE +#include "base/init_google.h" +#endif + +extern "C" { +int run_tests(int argc, char *argv[]); +} + +int main(int argc, char *argv[]) { +#ifdef USE_GOOGLE + InitGoogle(NULL, &argc, &argv, true); +#endif + run_tests(argc, argv); +} diff --git a/tests/upb_test.h b/tests/upb_test.h index 652977b..60714ba 100644 --- a/tests/upb_test.h +++ b/tests/upb_test.h @@ -9,25 +9,35 @@ #include <stdio.h> #include <stdlib.h> +#include <stdint.h> #ifdef __cplusplus extern "C" { #endif int num_assertions = 0; +uint32_t testhash = 0; + +#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. " \ + "Run with the arg %x to run only this test.\n", \ + testhash, testhash); \ + } + #define ASSERT(expr) do { \ ++num_assertions; \ if (!(expr)) { \ - fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \ - fprintf(stderr, "expr: %s\n", #expr); \ + PRINT_FAILURE(expr) \ abort(); \ } \ } while (0) #define ASSERT_NOCOUNT(expr) do { \ if (!(expr)) { \ - fprintf(stderr, "Assertion failed: %s:%d\n", __FILE__, __LINE__); \ - fprintf(stderr, "expr: %s\n", #expr); \ + PRINT_FAILURE(expr) \ abort(); \ } \ } while (0) @@ -35,8 +45,7 @@ int num_assertions = 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); \ + PRINT_FAILURE(expr) \ fprintf(stderr, "failed status: %s\n", upb_status_getstr(status)); \ abort(); \ } \ |