Commit 1ac3accc authored by Kenton Varda's avatar Kenton Varda

Make tests pass with -fno-rtti and -fno-exceptions.

parent 77abb6e6
...@@ -316,6 +316,12 @@ TEST(DynamicApi, UnionsWrite) { ...@@ -316,6 +316,12 @@ TEST(DynamicApi, UnionsWrite) {
EXPECT_EQ(1234567890123456789ll, reader.getUnion3().getU3f0s64()); EXPECT_EQ(1234567890123456789ll, reader.getUnion3().getU3f0s64());
} }
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
// All exceptions should be non-fatal, so when exceptions are disabled the code should return.
#define EXPECT_ANY_THROW(code) code
#endif
TEST(DynamicApi, ConversionFailures) { TEST(DynamicApi, ConversionFailures) {
MallocMessageBuilder builder; MallocMessageBuilder builder;
auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>()); auto root = builder.initRoot<DynamicStruct>(Schema::from<TestAllTypes>());
......
...@@ -1444,12 +1444,16 @@ HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast ...@@ -1444,12 +1444,16 @@ HANDLE_NUMERIC_TYPE(double, kj::implicitCast, kj::implicitCast, kj::implicitCast
#define HANDLE_TYPE(name, discrim, typeName) \ #define HANDLE_TYPE(name, discrim, typeName) \
ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \ ReaderFor<typeName> DynamicValue::Reader::AsImpl<typeName>::apply(const Reader& reader) { \
KJ_REQUIRE(reader.type == discrim, \ KJ_REQUIRE(reader.type == discrim, \
"Type mismatch when using DynamicValue::Reader::as()."); \ "Type mismatch when using DynamicValue::Reader::as().") { \
return ReaderFor<typeName>(); \
} \
return reader.name##Value; \ return reader.name##Value; \
} \ } \
BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \ BuilderFor<typeName> DynamicValue::Builder::AsImpl<typeName>::apply(Builder& builder) { \
KJ_REQUIRE(builder.type == discrim, \ KJ_REQUIRE(builder.type == discrim, \
"Type mismatch when using DynamicValue::Builder::as()."); \ "Type mismatch when using DynamicValue::Builder::as().") { \
return BuilderFor<typeName>(); \
} \
return builder.name##Value; \ return builder.name##Value; \
} }
......
...@@ -212,6 +212,14 @@ TEST(Encoding, GenericObjects) { ...@@ -212,6 +212,14 @@ TEST(Encoding, GenericObjects) {
} }
} }
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#define EXPECT_NONFATAL_FAILURE(code) code
#else
#define EXPECT_NONFATAL_FAILURE EXPECT_ANY_THROW
#endif
#ifdef NDEBUG #ifdef NDEBUG
#define EXPECT_DEBUG_ANY_THROW(EXP) #define EXPECT_DEBUG_ANY_THROW(EXP)
#else #else
...@@ -947,12 +955,12 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -947,12 +955,12 @@ TEST(Encoding, UpgradeListInBuilder) {
root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID}); root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID});
checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID}); checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
EXPECT_ANY_THROW(root.getObjectField<List<bool>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<bool>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint8_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint8_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint16_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
checkUpgradedList(root, {0, 0, 0, 0}, {"", "", "", ""}); checkUpgradedList(root, {0, 0, 0, 0}, {"", "", "", ""});
// ----------------------------------------------------------------- // -----------------------------------------------------------------
...@@ -962,11 +970,11 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -962,11 +970,11 @@ TEST(Encoding, UpgradeListInBuilder) {
auto orig = root.asReader().getObjectField<List<bool>>(); auto orig = root.asReader().getObjectField<List<bool>>();
checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID}); checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
checkList(root.getObjectField<List<bool>>(), {true, false, true, true}); checkList(root.getObjectField<List<bool>>(), {true, false, true, true});
EXPECT_ANY_THROW(root.getObjectField<List<uint8_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint8_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint16_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
checkList(orig, {true, false, true, true}); checkList(orig, {true, false, true, true});
checkUpgradedList(root, {1, 0, 1, 1}, {"", "", "", ""}); checkUpgradedList(root, {1, 0, 1, 1}, {"", "", "", ""});
...@@ -981,10 +989,10 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -981,10 +989,10 @@ TEST(Encoding, UpgradeListInBuilder) {
checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID}); checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID, Void::VOID});
checkList(root.getObjectField<List<bool>>(), {false, true, true, false}); checkList(root.getObjectField<List<bool>>(), {false, true, true, false});
checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44}); checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44});
EXPECT_ANY_THROW(root.getObjectField<List<uint16_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
checkList(orig, {0x12, 0x23, 0x33, 0x44}); checkList(orig, {0x12, 0x23, 0x33, 0x44});
checkUpgradedList(root, {0x12, 0x23, 0x33, 0x44}, {"", "", "", ""}); checkUpgradedList(root, {0x12, 0x23, 0x33, 0x44}, {"", "", "", ""});
...@@ -1000,9 +1008,9 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1000,9 +1008,9 @@ TEST(Encoding, UpgradeListInBuilder) {
checkList(root.getObjectField<List<bool>>(), {false, true, true, false}); checkList(root.getObjectField<List<bool>>(), {false, true, true, false});
checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44}); checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x33, 0x44});
checkList(root.getObjectField<List<uint16_t>>(), {0x5612, 0x7823, 0xab33, 0xcd44}); checkList(root.getObjectField<List<uint16_t>>(), {0x5612, 0x7823, 0xab33, 0xcd44});
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
checkList(orig, {0x5612, 0x7823, 0xab33, 0xcd44}); checkList(orig, {0x5612, 0x7823, 0xab33, 0xcd44});
checkUpgradedList(root, {0x5612, 0x7823, 0xab33, 0xcd44}, {"", "", "", ""}); checkUpgradedList(root, {0x5612, 0x7823, 0xab33, 0xcd44}, {"", "", "", ""});
...@@ -1019,8 +1027,8 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1019,8 +1027,8 @@ TEST(Encoding, UpgradeListInBuilder) {
checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x32, 0x45}); checkList(root.getObjectField<List<uint8_t>>(), {0x12, 0x23, 0x32, 0x45});
checkList(root.getObjectField<List<uint16_t>>(), {0x5612, 0x7823, 0xab32, 0xcd45}); checkList(root.getObjectField<List<uint16_t>>(), {0x5612, 0x7823, 0xab32, 0xcd45});
checkList(root.getObjectField<List<uint32_t>>(), {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u}); checkList(root.getObjectField<List<uint32_t>>(), {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u});
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
checkList(orig, {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u}); checkList(orig, {0x17595612u, 0x29347823u, 0x5923ab32u, 0x1a39cd45u});
checkUpgradedList(root, {0x17595612, 0x29347823, 0x5923ab32, 0x1a39cd45}, {"", "", "", ""}); checkUpgradedList(root, {0x17595612, 0x29347823, 0x5923ab32, 0x1a39cd45}, {"", "", "", ""});
...@@ -1038,7 +1046,7 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1038,7 +1046,7 @@ TEST(Encoding, UpgradeListInBuilder) {
checkList(root.getObjectField<List<uint16_t>>(), {0xfe21, 0xaf36}); checkList(root.getObjectField<List<uint16_t>>(), {0xfe21, 0xaf36});
checkList(root.getObjectField<List<uint32_t>>(), {0x8735fe21u, 0x1923af36u}); checkList(root.getObjectField<List<uint32_t>>(), {0x8735fe21u, 0x1923af36u});
checkList(root.getObjectField<List<uint64_t>>(), {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}); checkList(root.getObjectField<List<uint64_t>>(), {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull});
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
checkList(orig, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}); checkList(orig, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull});
checkUpgradedList(root, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}, {"", ""}); checkUpgradedList(root, {0x1234abcd8735fe21ull, 0x7173bc0e1923af36ull}, {"", ""});
...@@ -1051,11 +1059,11 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1051,11 +1059,11 @@ TEST(Encoding, UpgradeListInBuilder) {
root.setObjectField<List<Text>>({"foo", "bar", "baz"}); root.setObjectField<List<Text>>({"foo", "bar", "baz"});
auto orig = root.asReader().getObjectField<List<Text>>(); auto orig = root.asReader().getObjectField<List<Text>>();
checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID}); checkList(root.getObjectField<List<Void>>(), {Void::VOID, Void::VOID, Void::VOID});
EXPECT_ANY_THROW(root.getObjectField<List<bool>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<bool>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint8_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint8_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint16_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz"}); checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz"});
checkList(orig, {"foo", "bar", "baz"}); checkList(orig, {"foo", "bar", "baz"});
...@@ -1117,9 +1125,9 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1117,9 +1125,9 @@ TEST(Encoding, UpgradeListInBuilder) {
} }
checkList(root.getObjectField<List<bool>>(), {true, true, false, false}); checkList(root.getObjectField<List<bool>>(), {true, true, false, false});
checkList(root.getObjectField<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u}); checkList(root.getObjectField<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u});
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
// Upgrade from multi-byte, sub-word data. // Upgrade from multi-byte, sub-word data.
root.setObjectField<List<uint16_t>>({12u, 34u, 56u, 78u}); root.setObjectField<List<uint16_t>>({12u, 34u, 56u, 78u});
...@@ -1143,8 +1151,8 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1143,8 +1151,8 @@ TEST(Encoding, UpgradeListInBuilder) {
checkList(root.getObjectField<List<uint16_t>>(), {0x1235u, 0x2879u, 0x3082u, 0x8948u}); checkList(root.getObjectField<List<uint16_t>>(), {0x1235u, 0x2879u, 0x3082u, 0x8948u});
checkList(root.getObjectField<List<uint32_t>>(), checkList(root.getObjectField<List<uint32_t>>(),
{0x65ac1235u, 0x13f12879u, 0x33423082u, 0x12988948u}); {0x65ac1235u, 0x13f12879u, 0x33423082u, 0x12988948u});
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
// Upgrade from void -> data struct // Upgrade from void -> data struct
root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID}); root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID});
...@@ -1162,9 +1170,9 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1162,9 +1170,9 @@ TEST(Encoding, UpgradeListInBuilder) {
} }
checkList(root.getObjectField<List<bool>>(), {true, true, false, false}); checkList(root.getObjectField<List<bool>>(), {true, true, false, false});
checkList(root.getObjectField<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u}); checkList(root.getObjectField<List<uint16_t>>(), {12573u, 3251u, 9238u, 5832u});
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
// Upgrade from void -> pointer struct // Upgrade from void -> pointer struct
root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID}); root.setObjectField<List<Void>>({Void::VOID, Void::VOID, Void::VOID, Void::VOID});
...@@ -1180,18 +1188,18 @@ TEST(Encoding, UpgradeListInBuilder) { ...@@ -1180,18 +1188,18 @@ TEST(Encoding, UpgradeListInBuilder) {
l[2].setF("baz"); l[2].setF("baz");
l[3].setF("qux"); l[3].setF("qux");
} }
EXPECT_ANY_THROW(root.getObjectField<List<bool>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<bool>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint16_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint16_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint32_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint32_t>>());
EXPECT_ANY_THROW(root.getObjectField<List<uint64_t>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<uint64_t>>());
checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz", "qux"}); checkList(root.getObjectField<List<Text>>(), {"foo", "bar", "baz", "qux"});
// Verify that we cannot "side-grade" a pointer list to a data struct list, or a data list to // Verify that we cannot "side-grade" a pointer list to a data struct list, or a data list to
// a pointer struct list. // a pointer struct list.
root.setObjectField<List<Text>>({"foo", "bar", "baz", "qux"}); root.setObjectField<List<Text>>({"foo", "bar", "baz", "qux"});
EXPECT_ANY_THROW(root.getObjectField<List<test::TestLists::Struct32>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<test::TestLists::Struct32>>());
root.setObjectField<List<uint32_t>>({12, 34, 56, 78}); root.setObjectField<List<uint32_t>>({12, 34, 56, 78});
EXPECT_ANY_THROW(root.getObjectField<List<Text>>()); EXPECT_NONFATAL_FAILURE(root.getObjectField<List<Text>>());
} }
// ======================================================================================= // =======================================================================================
......
...@@ -56,6 +56,14 @@ TEST(SchemaLoader, Load) { ...@@ -56,6 +56,14 @@ TEST(SchemaLoader, Load) {
EXPECT_EQ(0u, struct16Schema.getProto().getBody().getStructNode().getMembers().size()); EXPECT_EQ(0u, struct16Schema.getProto().getBody().getStructNode().getMembers().size());
} }
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#define EXPECT_NONFATAL_FAILURE(code) code
#else
#define EXPECT_NONFATAL_FAILURE EXPECT_ANY_THROW
#endif
TEST(SchemaLoader, Use) { TEST(SchemaLoader, Use) {
SchemaLoader loader; SchemaLoader loader;
...@@ -196,7 +204,7 @@ TEST(SchemaLoader, Downgrade) { ...@@ -196,7 +204,7 @@ TEST(SchemaLoader, Downgrade) {
TEST(SchemaLoader, Incompatible) { TEST(SchemaLoader, Incompatible) {
SchemaLoader loader; SchemaLoader loader;
loader.loadCompiledTypeAndDependencies<test::TestListDefaults>(); loader.loadCompiledTypeAndDependencies<test::TestListDefaults>();
EXPECT_ANY_THROW( EXPECT_NONFATAL_FAILURE(
loadUnderAlternateTypeId<test::TestAllTypes>(loader, typeId<test::TestListDefaults>())); loadUnderAlternateTypeId<test::TestAllTypes>(loader, typeId<test::TestListDefaults>()));
} }
......
...@@ -52,6 +52,11 @@ namespace capnp { ...@@ -52,6 +52,11 @@ namespace capnp {
namespace _ { // private namespace _ { // private
namespace { namespace {
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#endif
TEST(Schema, Structs) { TEST(Schema, Structs) {
StructSchema schema = Schema::from<TestAllTypes>(); StructSchema schema = Schema::from<TestAllTypes>();
......
...@@ -124,6 +124,7 @@ TEST(Array, ComplexConstructor) { ...@@ -124,6 +124,7 @@ TEST(Array, ComplexConstructor) {
EXPECT_EQ(0, TestObject::count); EXPECT_EQ(0, TestObject::count);
} }
#if !KJ_NO_EXCEPTIONS
TEST(Array, ThrowingConstructor) { TEST(Array, ThrowingConstructor) {
TestObject::count = 0; TestObject::count = 0;
TestObject::throwAt = 16; TestObject::throwAt = 16;
...@@ -145,6 +146,7 @@ TEST(Array, ThrowingDestructor) { ...@@ -145,6 +146,7 @@ TEST(Array, ThrowingDestructor) {
EXPECT_ANY_THROW(array = nullptr); EXPECT_ANY_THROW(array = nullptr);
EXPECT_EQ(0, TestObject::count); EXPECT_EQ(0, TestObject::count);
} }
#endif // !KJ_NO_EXCEPTIONS
TEST(Array, AraryBuilder) { TEST(Array, AraryBuilder) {
TestObject::count = 0; TestObject::count = 0;
...@@ -260,6 +262,7 @@ TEST(Array, AraryBuilderAddAll) { ...@@ -260,6 +262,7 @@ TEST(Array, AraryBuilderAddAll) {
EXPECT_EQ(0, TestObject::count); EXPECT_EQ(0, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount); EXPECT_EQ(0, TestObject::copiedCount);
#if !KJ_NO_EXCEPTIONS
{ {
// Complex case, exceptions occur. // Complex case, exceptions occur.
TestObject::count = 0; TestObject::count = 0;
...@@ -283,6 +286,7 @@ TEST(Array, AraryBuilderAddAll) { ...@@ -283,6 +286,7 @@ TEST(Array, AraryBuilderAddAll) {
} }
EXPECT_EQ(0, TestObject::count); EXPECT_EQ(0, TestObject::count);
EXPECT_EQ(0, TestObject::copiedCount); EXPECT_EQ(0, TestObject::copiedCount);
#endif // !KJ_NO_EXCEPTIONS
} }
TEST(Array, HeapCopy) { TEST(Array, HeapCopy) {
......
...@@ -204,8 +204,14 @@ TEST(Common, Downcast) { ...@@ -204,8 +204,14 @@ TEST(Common, Downcast) {
EXPECT_EQ(&bar, &downcast<Bar>(foo)); EXPECT_EQ(&bar, &downcast<Bar>(foo));
#if !defined(NDEBUG) && !KJ_NO_RTTI #if !defined(NDEBUG) && !KJ_NO_RTTI
#if KJ_NO_EXCEPTIONS
#ifndef NDEBUG
EXPECT_DEATH_IF_SUPPORTED(downcast<Baz>(foo), "Value cannot be downcast");
#endif
#else
EXPECT_ANY_THROW(downcast<Baz>(foo)); EXPECT_ANY_THROW(downcast<Baz>(foo));
#endif #endif
#endif
#if KJ_NO_RTTI #if KJ_NO_RTTI
EXPECT_TRUE(dynamicDowncastIfAvailable<Bar>(foo) == nullptr); EXPECT_TRUE(dynamicDowncastIfAvailable<Bar>(foo) == nullptr);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <string> #include <string>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <exception> #include <exception>
...@@ -43,6 +44,83 @@ public: ...@@ -43,6 +44,83 @@ public:
std::string text; std::string text;
int outputPipe = -1;
bool forkForDeathTest() {
// This is called when exceptions are disabled. We fork the process instead and then expect
// the child to die.
int pipeFds[2];
pipe(pipeFds);
pid_t child = fork();
if (child == 0) {
// This is the child!
close(pipeFds[0]);
outputPipe = pipeFds[1];
return true;
} else {
close(pipeFds[1]);
// Read child error messages into our local buffer.
char buf[1024];
for (;;) {
ssize_t n = read(pipeFds[0], buf, sizeof(buf));
if (n < 0) {
if (errno == EINTR) {
continue;
} else {
break;
}
} else if (n == 0) {
break;
} else {
text.append(buf, n);
}
}
close(pipeFds[0]);
// Get exit status.
int status;
do {
if (waitpid(child, &status, 0) < 0) {
if (errno == EINTR) {
continue;
} else {
ADD_FAILURE() << "waidpid: " << strerror(errno);
return false;
}
}
} while (false);
EXPECT_TRUE(WIFEXITED(status));
EXPECT_EQ(74, WEXITSTATUS(status));
return false;
}
}
void flush() {
if (outputPipe != -1) {
const char* pos = &*text.begin();
const char* end = pos + text.size();
while (pos < end) {
ssize_t n = write(outputPipe, pos, end - pos);
if (n < 0) {
if (errno == EINTR) {
continue;
} else {
break; // Give up on error.
}
}
pos += n;
}
text.clear();
}
}
void onRecoverableException(Exception&& exception) override { void onRecoverableException(Exception&& exception) override {
text += "recoverable exception: "; text += "recoverable exception: ";
auto what = str(exception); auto what = str(exception);
...@@ -54,6 +132,7 @@ public: ...@@ -54,6 +132,7 @@ public:
text.append(what.cStr(), end); text.append(what.cStr(), end);
} }
text += '\n'; text += '\n';
flush();
} }
void onFatalException(Exception&& exception) override { void onFatalException(Exception&& exception) override {
...@@ -67,7 +146,17 @@ public: ...@@ -67,7 +146,17 @@ public:
text.append(what.cStr(), end); text.append(what.cStr(), end);
} }
text += '\n'; text += '\n';
flush();
#if KJ_NO_EXCEPTIONS
if (outputPipe >= 0) {
// This is a child process. We got what we want, now exit quickly without writing any
// additional messages, with a status code that the parent will interpret as "exited in the
// way we expected".
_exit(74);
}
#else
throw MockException(); throw MockException();
#endif
} }
void logMessage(const char* file, int line, int contextDepth, String&& text) override { void logMessage(const char* file, int line, int contextDepth, String&& text) override {
...@@ -77,6 +166,12 @@ public: ...@@ -77,6 +166,12 @@ public:
} }
}; };
#if KJ_NO_EXCEPTIONS
#define EXPECT_FATAL(code) if (mockCallback.forkForDeathTest()) { code; abort(); }
#else
#define EXPECT_FATAL(code) EXPECT_THROW(code, MockException);
#endif
std::string fileLine(std::string file, int line) { std::string fileLine(std::string file, int line) {
file += ':'; file += ':';
char buffer[32]; char buffer[32];
...@@ -123,7 +218,7 @@ TEST(Debug, Log) { ...@@ -123,7 +218,7 @@ TEST(Debug, Log) {
Debug::setLogLevel(Debug::Severity::WARNING); Debug::setLogLevel(Debug::Severity::WARNING);
KJ_ASSERT(1 == 1); KJ_ASSERT(1 == 1);
EXPECT_THROW(KJ_ASSERT(1 == 2), MockException); line = __LINE__; EXPECT_FATAL(KJ_ASSERT(1 == 2)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2\n", mockCallback.text); "1 == 2\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
...@@ -140,17 +235,17 @@ TEST(Debug, Log) { ...@@ -140,17 +235,17 @@ TEST(Debug, Log) {
EXPECT_TRUE(recovered); EXPECT_TRUE(recovered);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_THROW(KJ_ASSERT(1 == 2, i, "hi", str), MockException); line = __LINE__; EXPECT_FATAL(KJ_ASSERT(1 == 2, i, "hi", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text); "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_THROW(KJ_REQUIRE(1 == 2, i, "hi", str), MockException); line = __LINE__; EXPECT_FATAL(KJ_REQUIRE(1 == 2, i, "hi", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": requirement not met: expected " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": requirement not met: expected "
"1 == 2; i = 123; hi; str = foo\n", mockCallback.text); "1 == 2; i = 123; hi; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_THROW(KJ_FAIL_ASSERT("foo"), MockException); line = __LINE__; EXPECT_FATAL(KJ_FAIL_ASSERT("foo")); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: foo\n", EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": bug in code: foo\n",
mockCallback.text); mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
...@@ -159,28 +254,47 @@ TEST(Debug, Log) { ...@@ -159,28 +254,47 @@ TEST(Debug, Log) {
TEST(Debug, Catch) { TEST(Debug, Catch) {
int line; int line;
// Catch as kj::Exception. {
Maybe<Exception> exception = kj::runCatchingExceptions([&](){ // Catch recoverable as kj::Exception.
line = __LINE__; KJ_FAIL_ASSERT("foo"); Maybe<Exception> exception = kj::runCatchingExceptions([&](){
}); line = __LINE__; KJ_FAIL_ASSERT("foo") { break; }
});
KJ_IF_MAYBE(e, exception) {
String what = str(*e); KJ_IF_MAYBE(e, exception) {
std::string text(what.cStr(), strchr(what.cStr(), '\n') - what.cStr()); String what = str(*e);
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text); std::string text(what.cStr(), strchr(what.cStr(), '\n') - what.cStr());
} else { EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
ADD_FAILURE() << "Expected exception."; } else {
ADD_FAILURE() << "Expected exception.";
}
} }
#if !KJ_NO_EXCEPTIONS #if !KJ_NO_EXCEPTIONS
// Catch as std::exception. {
try { // Catch fatal as kj::Exception.
line = __LINE__; KJ_FAIL_ASSERT("foo"); Maybe<Exception> exception = kj::runCatchingExceptions([&](){
ADD_FAILURE() << "Expected exception."; line = __LINE__; KJ_FAIL_ASSERT("foo");
} catch (const std::exception& e) { });
const char* what = e.what();
std::string text(what, strchr(what, '\n') - what); KJ_IF_MAYBE(e, exception) {
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text); String what = str(*e);
std::string text(what.cStr(), strchr(what.cStr(), '\n') - what.cStr());
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
} else {
ADD_FAILURE() << "Expected exception.";
}
}
{
// Catch as std::exception.
try {
line = __LINE__; KJ_FAIL_ASSERT("foo");
ADD_FAILURE() << "Expected exception.";
} catch (const std::exception& e) {
const char* what = e.what();
std::string text(what, strchr(what, '\n') - what);
EXPECT_EQ(fileLine(__FILE__, line) + ": bug in code: foo", text);
}
} }
#endif #endif
} }
...@@ -195,7 +309,7 @@ TEST(Debug, Syscall) { ...@@ -195,7 +309,7 @@ TEST(Debug, Syscall) {
int fd; int fd;
KJ_SYSCALL(fd = dup(STDIN_FILENO)); KJ_SYSCALL(fd = dup(STDIN_FILENO));
KJ_SYSCALL(close(fd)); KJ_SYSCALL(close(fd));
EXPECT_THROW(KJ_SYSCALL(close(fd), i, "bar", str), MockException); line = __LINE__; EXPECT_FATAL(KJ_SYSCALL(close(fd), i, "bar", str)); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": error from OS: close(fd): " EXPECT_EQ("fatal exception: " + fileLine(__FILE__, line) + ": error from OS: close(fd): "
+ strerror(EBADF) + "; i = 123; bar; str = foo\n", mockCallback.text); + strerror(EBADF) + "; i = 123; bar; str = foo\n", mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
...@@ -221,7 +335,7 @@ TEST(Debug, Context) { ...@@ -221,7 +335,7 @@ TEST(Debug, Context) {
mockCallback.text); mockCallback.text);
mockCallback.text.clear(); mockCallback.text.clear();
EXPECT_THROW(KJ_FAIL_ASSERT("bar"), MockException); line = __LINE__; EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n" EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
+ fileLine(__FILE__, line) + ": bug in code: bar\n", + fileLine(__FILE__, line) + ": bug in code: bar\n",
mockCallback.text); mockCallback.text);
...@@ -231,7 +345,7 @@ TEST(Debug, Context) { ...@@ -231,7 +345,7 @@ TEST(Debug, Context) {
int i = 123; int i = 123;
const char* str = "qux"; const char* str = "qux";
KJ_CONTEXT("baz", i, "corge", str); int cline2 = __LINE__; KJ_CONTEXT("baz", i, "corge", str); int cline2 = __LINE__;
EXPECT_THROW(KJ_FAIL_ASSERT("bar"), MockException); line = __LINE__; EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n" EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
+ fileLine(__FILE__, cline2) + ": context: baz; i = 123; corge; str = qux\n" + fileLine(__FILE__, cline2) + ": context: baz; i = 123; corge; str = qux\n"
...@@ -242,7 +356,7 @@ TEST(Debug, Context) { ...@@ -242,7 +356,7 @@ TEST(Debug, Context) {
{ {
KJ_CONTEXT("grault"); int cline2 = __LINE__; KJ_CONTEXT("grault"); int cline2 = __LINE__;
EXPECT_THROW(KJ_FAIL_ASSERT("bar"), MockException); line = __LINE__; EXPECT_FATAL(KJ_FAIL_ASSERT("bar")); line = __LINE__;
EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n" EXPECT_EQ("fatal exception: " + fileLine(__FILE__, cline) + ": context: foo\n"
+ fileLine(__FILE__, cline2) + ": context: grault\n" + fileLine(__FILE__, cline2) + ": context: grault\n"
......
...@@ -88,7 +88,11 @@ TEST(Exception, UnwindDetector) { ...@@ -88,7 +88,11 @@ TEST(Exception, UnwindDetector) {
} }
TEST(Exception, ExceptionCallbackMustBeOnStack) { TEST(Exception, ExceptionCallbackMustBeOnStack) {
#if KJ_NO_EXCEPTIONS
EXPECT_DEATH_IF_SUPPORTED(new ExceptionCallback, "must be allocated on the stack");
#else
EXPECT_ANY_THROW(new ExceptionCallback); EXPECT_ANY_THROW(new ExceptionCallback);
#endif
} }
} // namespace } // namespace
......
...@@ -348,7 +348,7 @@ private: ...@@ -348,7 +348,7 @@ private:
getExceptionCallback().logMessage(e.getFile(), e.getLine(), 0, str( getExceptionCallback().logMessage(e.getFile(), e.getLine(), 0, str(
e.getNature(), e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "", e.getNature(), e.getDurability() == Exception::Durability::TEMPORARY ? " (temporary)" : "",
e.getDescription() == nullptr ? "" : ": ", e.getDescription(), e.getDescription() == nullptr ? "" : ": ", e.getDescription(),
"\nstack: ", strArray(e.getStackTrace(), " "))); "\nstack: ", strArray(e.getStackTrace(), " "), "\n"));
} }
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment