Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
C
capnproto
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
capnproto
Commits
8f858fc4
Commit
8f858fc4
authored
Apr 07, 2017
by
Kenton Varda
Committed by
GitHub
Apr 07, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #441 from sandstorm-io/fix-no-exceptions
Fix build with -fno-exceptions
parents
dec6bafa
429d796c
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
253 additions
and
131 deletions
+253
-131
json-test.c++
c++/src/capnp/compat/json-test.c++
+23
-16
fuzz-test.c++
c++/src/capnp/fuzz-test.c++
+4
-4
layout.c++
c++/src/capnp/layout.c++
+35
-14
layout.h
c++/src/capnp/layout.h
+3
-1
rpc-twoparty-test.c++
c++/src/capnp/rpc-twoparty-test.c++
+5
-4
rpc.c++
c++/src/capnp/rpc.c++
+18
-13
test-util.h
c++/src/capnp/test-util.h
+0
-14
async-inl.h
c++/src/kj/async-inl.h
+20
-0
async-test.c++
c++/src/kj/async-test.c++
+0
-5
common-test.c++
c++/src/kj/common-test.c++
+1
-7
gtest.h
c++/src/kj/compat/gtest.h
+14
-0
exception-test.c++
c++/src/kj/exception-test.c++
+1
-5
mutex-test.c++
c++/src/kj/mutex-test.c++
+0
-14
string-test.c++
c++/src/kj/string-test.c++
+20
-20
string.c++
c++/src/kj/string.c++
+12
-11
test.c++
c++/src/kj/test.c++
+72
-1
test.h
c++/src/kj/test.h
+25
-2
No files found.
c++/src/capnp/compat/json-test.c++
View file @
8f858fc4
...
...
@@ -198,42 +198,48 @@ KJ_TEST("decode all types") {
auto root = message.initRoot<TestAllTypes>(); \
KJ_EXPECT_THROW_MESSAGE(errorMessage, json.decode(s, root)); \
}
#define CASE_THROW_RECOVERABLE(s, errorMessage) \
{ \
MallocMessageBuilder message; \
auto root = message.initRoot<TestAllTypes>(); \
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE(errorMessage, json.decode(s, root)); \
}
CASE
(
R"({})"
,
root
.
getBoolField
()
==
false
);
CASE
(
R"({"unknownField":7})"
,
root
.
getBoolField
()
==
false
);
CASE
(
R"({"boolField":true})"
,
root
.
getBoolField
()
==
true
);
CASE
(
R"({"int8Field":-128})"
,
root
.
getInt8Field
()
==
-
128
);
CASE
(
R"({"int8Field":"127"})"
,
root
.
getInt8Field
()
==
127
);
CASE_THROW
(
R"({"int8Field":"-129"})"
,
"Value out-of-range"
);
CASE_THROW
(
R"({"int8Field":128})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"int8Field":"-129"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"int8Field":128})"
,
"Value out-of-range"
);
CASE
(
R"({"int16Field":-32768})"
,
root
.
getInt16Field
()
==
-
32768
);
CASE
(
R"({"int16Field":"32767"})"
,
root
.
getInt16Field
()
==
32767
);
CASE_THROW
(
R"({"int16Field":"-32769"})"
,
"Value out-of-range"
);
CASE_THROW
(
R"({"int16Field":32768})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"int16Field":"-32769"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"int16Field":32768})"
,
"Value out-of-range"
);
CASE
(
R"({"int32Field":-2147483648})"
,
root
.
getInt32Field
()
==
-
2147483648
);
CASE
(
R"({"int32Field":"2147483647"})"
,
root
.
getInt32Field
()
==
2147483647
);
CASE
(
R"({"int64Field":-9007199254740992})"
,
root
.
getInt64Field
()
==
-
9007199254740992LL
);
CASE
(
R"({"int64Field":9007199254740991})"
,
root
.
getInt64Field
()
==
9007199254740991LL
);
CASE
(
R"({"int64Field":"-9223372036854775808"})"
,
root
.
getInt64Field
()
==
-
9223372036854775808ULL
);
CASE
(
R"({"int64Field":"9223372036854775807"})"
,
root
.
getInt64Field
()
==
9223372036854775807LL
);
CASE_THROW
(
R"({"int64Field":"-9223372036854775809"})"
,
"Value out-of-range"
);
CASE_THROW
(
R"({"int64Field":"9223372036854775808"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"int64Field":"-9223372036854775809"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"int64Field":"9223372036854775808"})"
,
"Value out-of-range"
);
CASE
(
R"({"uInt8Field":255})"
,
root
.
getUInt8Field
()
==
255
);
CASE
(
R"({"uInt8Field":"0"})"
,
root
.
getUInt8Field
()
==
0
);
CASE_THROW
(
R"({"uInt8Field":"256"})"
,
"Value out-of-range"
);
CASE_THROW
(
R"({"uInt8Field":-1})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt8Field":"256"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt8Field":-1})"
,
"Value out-of-range"
);
CASE
(
R"({"uInt16Field":65535})"
,
root
.
getUInt16Field
()
==
65535
);
CASE
(
R"({"uInt16Field":"0"})"
,
root
.
getUInt16Field
()
==
0
);
CASE_THROW
(
R"({"uInt16Field":"655356"})"
,
"Value out-of-range"
);
CASE_THROW
(
R"({"uInt16Field":-1})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt16Field":"655356"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt16Field":-1})"
,
"Value out-of-range"
);
CASE
(
R"({"uInt32Field":4294967295})"
,
root
.
getUInt32Field
()
==
4294967295
);
CASE
(
R"({"uInt32Field":"0"})"
,
root
.
getUInt32Field
()
==
0
);
CASE_THROW
(
R"({"uInt32Field":"42949672956"})"
,
"Value out-of-range"
);
CASE_THROW
(
R"({"uInt32Field":-1})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt32Field":"42949672956"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt32Field":-1})"
,
"Value out-of-range"
);
CASE
(
R"({"uInt64Field":9007199254740991})"
,
root
.
getUInt64Field
()
==
9007199254740991ULL
);
CASE
(
R"({"uInt64Field":"18446744073709551615"})"
,
root
.
getUInt64Field
()
==
18446744073709551615ULL
);
CASE
(
R"({"uInt64Field":"0"})"
,
root
.
getUInt64Field
()
==
0
);
CASE_THROW
(
R"({"uInt64Field":"18446744073709551616"})"
,
"Value out-of-range"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt64Field":"18446744073709551616"})"
,
"Value out-of-range"
);
CASE
(
R"({"float32Field":0})"
,
root
.
getFloat32Field
()
==
0
);
CASE
(
R"({"float32Field":4.5})"
,
root
.
getFloat32Field
()
==
4.5
);
CASE
(
R"({"float32Field":null})"
,
kj
::
isNaN
(
root
.
getFloat32Field
()));
...
...
@@ -258,9 +264,9 @@ KJ_TEST("decode all types") {
CASE
(
R"({"structField":{"boolField":true}})"
,
root
.
getStructField
().
getBoolField
()
==
true
);
CASE
(
R"({"enumField":"bar"})"
,
root
.
getEnumField
()
==
TestEnum
::
BAR
);
CASE_THROW
(
R"({"int64Field":"177a"})"
,
"String does not contain valid"
);
CASE_THROW
(
R"({"uInt64Field":"177a"})"
,
"String does not contain valid"
);
CASE_THROW
(
R"({"float64Field":"177a"})"
,
"String does not contain valid"
);
CASE_THROW
_RECOVERABLE
(
R"({"int64Field":"177a"})"
,
"String does not contain valid"
);
CASE_THROW
_RECOVERABLE
(
R"({"uInt64Field":"177a"})"
,
"String does not contain valid"
);
CASE_THROW
_RECOVERABLE
(
R"({"float64Field":"177a"})"
,
"String does not contain valid"
);
CASE
(
R"({})"
,
root
.
hasBoolList
()
==
false
);
CASE
(
R"({"boolList":null})"
,
root
.
hasBoolList
()
==
false
);
...
...
@@ -309,6 +315,7 @@ KJ_TEST("decode all types") {
CASE
(
R"({"enumList":["bar"]})"
,
root
.
getEnumList
()[
0
]
==
TestEnum
::
BAR
);
#undef CASE
#undef CASE_THROW
#undef CASE_THROW_RECOVERABLE
}
KJ_TEST
(
"decode test message"
)
{
...
...
c++/src/capnp/fuzz-test.c++
View file @
8f858fc4
...
...
@@ -107,19 +107,19 @@ void traverseCatchingExceptions(kj::ArrayPtr<const word> data) {
// Try traversing through Checker.
kj
::
runCatchingExceptions
([
&
]()
{
FlatArrayMessageReader
reader
(
data
);
KJ_ASSERT
(
Checker
::
check
(
reader
)
!=
0
)
;
KJ_ASSERT
(
Checker
::
check
(
reader
)
!=
0
)
{
break
;
}
});
// Try traversing through AnyPointer.
kj
::
runCatchingExceptions
([
&
]()
{
FlatArrayMessageReader
reader
(
data
);
KJ_ASSERT
(
traverse
(
reader
.
getRoot
<
AnyPointer
>
())
!=
0
)
;
KJ_ASSERT
(
traverse
(
reader
.
getRoot
<
AnyPointer
>
())
!=
0
)
{
break
;
}
});
// Try counting the size..
kj
::
runCatchingExceptions
([
&
]()
{
FlatArrayMessageReader
reader
(
data
);
KJ_ASSERT
(
reader
.
getRoot
<
AnyPointer
>
().
targetSize
().
wordCount
!=
0
)
;
KJ_ASSERT
(
reader
.
getRoot
<
AnyPointer
>
().
targetSize
().
wordCount
!=
0
)
{
break
;
}
});
// Try copying into a builder, and if that works, traversing it with Checker.
...
...
@@ -128,7 +128,7 @@ void traverseCatchingExceptions(kj::ArrayPtr<const word> data) {
FlatArrayMessageReader
reader
(
data
);
MallocMessageBuilder
copyBuilder
(
buffer
);
copyBuilder
.
setRoot
(
reader
.
getRoot
<
AnyPointer
>
());
KJ_ASSERT
(
Checker
::
check
(
copyBuilder
)
!=
0
)
;
KJ_ASSERT
(
Checker
::
check
(
copyBuilder
)
!=
0
)
{
break
;
}
});
}
...
...
c++/src/capnp/layout.c++
View file @
8f858fc4
...
...
@@ -555,11 +555,17 @@ struct WireHelpers {
// object.
ref
=
pad
+
1
;
segment
=
segment
->
getArena
()
->
tryGetSegment
(
pad
->
farRef
.
segmentId
.
get
());
KJ_REQUIRE
(
segment
!=
nullptr
,
"Message contains double-far pointer to unknown segment."
)
{
SegmentReader
*
newSegment
=
segment
->
getArena
()
->
tryGetSegment
(
pad
->
farRef
.
segmentId
.
get
());
KJ_REQUIRE
(
newSegment
!=
nullptr
,
"Message contains double-far pointer to unknown segment."
)
{
return
nullptr
;
}
KJ_REQUIRE
(
pad
->
kind
()
==
WirePointer
::
FAR
,
"Second word of double-far pad must be far pointer."
)
{
return
nullptr
;
}
segment
=
newSegment
;
return
segment
->
getStartPtr
()
+
pad
->
farPositionInSegment
();
}
else
{
return
refTarget
;
...
...
@@ -1611,20 +1617,30 @@ struct WireHelpers {
}
}
else
{
word
*
ptr
=
followFars
(
ref
,
refTarget
,
segment
);
char
*
cptr
=
reinterpret_cast
<
char
*>
(
ptr
);
byte
*
bptr
=
reinterpret_cast
<
byte
*>
(
ptr
);
KJ_REQUIRE
(
ref
->
kind
()
==
WirePointer
::
LIST
,
"Called getText{Field,Element}() but existing pointer is not a list."
);
"Called getText{Field,Element}() but existing pointer is not a list."
)
{
goto
useDefault
;
}
KJ_REQUIRE
(
ref
->
listRef
.
elementSize
()
==
ElementSize
::
BYTE
,
"Called getText{Field,Element}() but existing list pointer is not byte-sized."
);
size_t
size
=
unbound
(
subtractChecked
(
ref
->
listRef
.
elementCount
()
/
ELEMENTS
,
ONE
,
[]()
{
KJ_FAIL_REQUIRE
(
"zero-size blob can't be text (need NUL terminator)"
);
}));
KJ_REQUIRE
(
cptr
[
size
]
==
'\0'
,
"Text blob missing NUL terminator."
)
{
"Called getText{Field,Element}() but existing list pointer is not byte-sized."
)
{
goto
useDefault
;
}
return
Text
::
Builder
(
cptr
,
size
);
auto
maybeSize
=
trySubtract
(
ref
->
listRef
.
elementCount
()
*
(
ONE
*
BYTES
/
ELEMENTS
),
ONE
*
BYTES
);
KJ_IF_MAYBE
(
size
,
maybeSize
)
{
KJ_REQUIRE
(
*
(
bptr
+
*
size
)
==
'\0'
,
"Text blob missing NUL terminator."
)
{
goto
useDefault
;
}
return
Text
::
Builder
(
reinterpret_cast
<
char
*>
(
bptr
),
unbound
(
*
size
/
BYTES
));
}
else
{
KJ_FAIL_REQUIRE
(
"zero-size blob can't be text (need NUL terminator)"
)
{
goto
useDefault
;
};
}
}
}
...
...
@@ -1663,6 +1679,7 @@ struct WireHelpers {
WirePointer
*
ref
,
word
*
refTarget
,
SegmentBuilder
*
segment
,
CapTableBuilder
*
capTable
,
const
void
*
defaultValue
,
BlobSize
defaultSize
))
{
if
(
ref
->
isNull
())
{
useDefault
:
if
(
defaultSize
==
ZERO
*
BYTES
)
{
return
nullptr
;
}
else
{
...
...
@@ -1674,9 +1691,13 @@ struct WireHelpers {
word
*
ptr
=
followFars
(
ref
,
refTarget
,
segment
);
KJ_REQUIRE
(
ref
->
kind
()
==
WirePointer
::
LIST
,
"Called getData{Field,Element}() but existing pointer is not a list."
);
"Called getData{Field,Element}() but existing pointer is not a list."
)
{
goto
useDefault
;
}
KJ_REQUIRE
(
ref
->
listRef
.
elementSize
()
==
ElementSize
::
BYTE
,
"Called getData{Field,Element}() but existing list pointer is not byte-sized."
);
"Called getData{Field,Element}() but existing list pointer is not byte-sized."
)
{
goto
useDefault
;
}
return
Data
::
Builder
(
reinterpret_cast
<
byte
*>
(
ptr
),
unbound
(
ref
->
listRef
.
elementCount
()
/
ELEMENTS
));
...
...
@@ -2653,13 +2674,13 @@ PointerType PointerReader::getPointerType() const {
WireHelpers
::
followFars
(
ptr
,
refTarget
,
sgmt
);
switch
(
ptr
->
kind
())
{
case
WirePointer
:
:
FAR
:
KJ_FAIL_ASSERT
(
"far pointer not followed?"
)
;
KJ_FAIL_ASSERT
(
"far pointer not followed?"
)
{
return
PointerType
::
NULL_
;
}
case
WirePointer
:
:
STRUCT
:
return
PointerType
::
STRUCT
;
case
WirePointer
:
:
LIST
:
return
PointerType
::
LIST
;
case
WirePointer
:
:
OTHER
:
KJ_REQUIRE
(
ptr
->
isCapability
(),
"unknown pointer type"
)
;
KJ_REQUIRE
(
ptr
->
isCapability
(),
"unknown pointer type"
)
{
return
PointerType
::
NULL_
;
}
return
PointerType
::
CAPABILITY
;
}
KJ_UNREACHABLE
;
...
...
c++/src/capnp/layout.h
View file @
8f858fc4
...
...
@@ -104,7 +104,9 @@ inline KJ_CONSTEXPR() BitsPerElementTableType dataBitsPerElement(ElementSize siz
}
inline
constexpr
PointersPerElementN
<
1
>
pointersPerElement
(
ElementSize
size
)
{
return
size
==
ElementSize
::
POINTER
?
ONE
*
POINTERS
/
ELEMENTS
:
ZERO
*
POINTERS
/
ELEMENTS
;
return
size
==
ElementSize
::
POINTER
?
PointersPerElementN
<
1
>
(
ONE
*
POINTERS
/
ELEMENTS
)
:
PointersPerElementN
<
1
>
(
ZERO
*
POINTERS
/
ELEMENTS
);
}
static
constexpr
BitsPerElementTableType
BITS_PER_ELEMENT_INCLUDING_PONITERS_TABLE
[
8
]
=
{
...
...
c++/src/capnp/rpc-twoparty-test.c++
View file @
8f858fc4
...
...
@@ -351,13 +351,14 @@ TEST(TwoPartyNetwork, HugeMessage) {
{
auto
req
=
client
.
methodWithDefaultsRequest
();
req
.
initA
(
100000000
);
// 100 MB
KJ_EXPECT_THROW_MESSAGE
(
"larger than the single-message size limit"
,
req
.
send
().
wait
(
ioContext
.
waitScope
));
KJ_EXPECT_THROW_RECOVERABLE_MESSAGE
(
"larger than the single-message size limit"
,
req
.
send
().
ignoreResult
().
wait
(
ioContext
.
waitScope
));
}
// Oversized response fails.
KJ_EXPECT_THROW_MESSAGE
(
"larger than the single-message size limit"
,
client
.
getEnormousStringRequest
().
send
().
wait
(
ioContext
.
waitScope
));
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"larger than the single-message size limit"
,
client
.
getEnormousStringRequest
().
send
().
ignoreResult
().
wait
(
ioContext
.
waitScope
));
// Connection is still up.
{
...
...
c++/src/capnp/rpc.c++
View file @
8f858fc4
...
...
@@ -448,6 +448,9 @@ private:
bool
isTailCall
=
false
;
// Is this a tail call? If so, we don't expect to receive results in the `Return`.
bool
skipFinish
=
false
;
// If true, don't send a Finish message.
inline
bool
operator
==
(
decltype
(
nullptr
))
const
{
return
!
isAwaitingReturn
&&
selfRef
==
nullptr
;
}
...
...
@@ -1331,7 +1334,7 @@ private:
connectionState
->
questions
.
find
(
id
),
"Question ID no longer on table?"
);
// Send the "Finish" message (if the connection is not already broken).
if
(
connectionState
->
connection
.
is
<
Connected
>
())
{
if
(
connectionState
->
connection
.
is
<
Connected
>
()
&&
!
question
.
skipFinish
)
{
auto
message
=
connectionState
->
connection
.
get
<
Connected
>
()
->
newOutgoingMessage
(
messageSizeHint
<
rpc
::
Finish
>
());
auto
builder
=
message
->
getBody
().
getAs
<
rpc
::
Message
>
().
initFinish
();
...
...
@@ -1504,6 +1507,14 @@ private:
question
.
paramExports
=
kj
::
mv
(
exports
);
question
.
isTailCall
=
isTailCall
;
// Make the QuentionRef and result promise.
SendInternalResult
result
;
auto
paf
=
kj
::
newPromiseAndFulfiller
<
kj
::
Promise
<
kj
::
Own
<
RpcResponse
>>>
();
result
.
questionRef
=
kj
::
refcounted
<
QuestionRef
>
(
*
connectionState
,
questionId
,
kj
::
mv
(
paf
.
fulfiller
));
question
.
selfRef
=
*
result
.
questionRef
;
result
.
promise
=
paf
.
promise
.
attach
(
kj
::
addRef
(
*
result
.
questionRef
));
// Finish and send.
callBuilder
.
setQuestionId
(
questionId
);
if
(
isTailCall
)
{
...
...
@@ -1514,18 +1525,13 @@ private:
callBuilder
.
getInterfaceId
(),
callBuilder
.
getMethodId
());
message
->
send
();
}))
{
KJ_LOG
(
WARNING
,
*
exception
);
kj
::
throwRecoverableException
(
kj
::
mv
(
*
exception
));
// We can't safely throw the exception from here since we've already modified the question
// table state. We'll have to reject the promise instead.
question
.
isAwaitingReturn
=
false
;
question
.
skipFinish
=
true
;
result
.
questionRef
->
reject
(
kj
::
mv
(
*
exception
));
}
// Make the result promise.
SendInternalResult
result
;
auto
paf
=
kj
::
newPromiseAndFulfiller
<
kj
::
Promise
<
kj
::
Own
<
RpcResponse
>>>
();
result
.
questionRef
=
kj
::
refcounted
<
QuestionRef
>
(
*
connectionState
,
questionId
,
kj
::
mv
(
paf
.
fulfiller
));
question
.
selfRef
=
*
result
.
questionRef
;
result
.
promise
=
paf
.
promise
.
attach
(
kj
::
addRef
(
*
result
.
questionRef
));
// Send and return.
return
kj
::
mv
(
result
);
}
...
...
@@ -1819,7 +1825,6 @@ private:
KJ_CONTEXT
(
"returning from RPC call"
,
interfaceId
,
methodId
);
exports
=
kj
::
downcast
<
RpcServerResponseImpl
>
(
*
KJ_ASSERT_NONNULL
(
response
)).
send
();
}))
{
KJ_LOG
(
WARNING
,
*
exception
);
responseSent
=
false
;
sendErrorReturn
(
kj
::
mv
(
*
exception
));
return
;
...
...
@@ -2268,7 +2273,7 @@ private:
// Add the answer to the answer table for pipelining and send the response.
auto
&
answer
=
answers
[
answerId
];
KJ_REQUIRE
(
!
answer
.
active
,
"questionId is already in use"
)
{
KJ_REQUIRE
(
!
answer
.
active
,
"questionId is already in use"
,
answerId
)
{
return
;
}
...
...
c++/src/capnp/test-util.h
View file @
8f858fc4
...
...
@@ -35,20 +35,6 @@
#include "dynamic.h"
#endif // !CAPNP_LITE
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#endif
#define EXPECT_NONFATAL_FAILURE(code) \
EXPECT_TRUE(kj::runCatchingExceptions([&]() { code; }) != nullptr);
#ifdef KJ_DEBUG
#define EXPECT_DEBUG_ANY_THROW EXPECT_ANY_THROW
#else
#define EXPECT_DEBUG_ANY_THROW(EXP)
#endif
// TODO(cleanup): Auto-generate stringification functions for union discriminants.
namespace
capnproto_test
{
namespace
capnp
{
...
...
c++/src/kj/async-inl.h
View file @
8f858fc4
...
...
@@ -869,6 +869,26 @@ T Promise<T>::wait(WaitScope& waitScope) {
}
}
template
<>
inline
void
Promise
<
void
>::
wait
(
WaitScope
&
waitScope
)
{
// Override <void> case to use throwRecoverableException().
_
::
ExceptionOr
<
_
::
Void
>
result
;
waitImpl
(
kj
::
mv
(
node
),
result
,
waitScope
);
if
(
result
.
value
!=
nullptr
)
{
KJ_IF_MAYBE
(
exception
,
result
.
exception
)
{
throwRecoverableException
(
kj
::
mv
(
*
exception
));
}
}
else
KJ_IF_MAYBE
(
exception
,
result
.
exception
)
{
throwRecoverableException
(
kj
::
mv
(
*
exception
));
}
else
{
// Result contained neither a value nor an exception?
KJ_UNREACHABLE
;
}
}
template
<
typename
T
>
ForkedPromise
<
T
>
Promise
<
T
>::
fork
()
{
return
ForkedPromise
<
T
>
(
false
,
refcounted
<
_
::
ForkHub
<
_
::
FixVoid
<
T
>>>
(
kj
::
mv
(
node
)));
...
...
c++/src/kj/async-test.c++
View file @
8f858fc4
...
...
@@ -357,11 +357,6 @@ TEST(Async, SeparateFulfillerChained) {
EXPECT_EQ
(
123
,
pair
.
promise
.
wait
(
waitScope
));
}
#if KJ_NO_EXCEPTIONS
#undef EXPECT_ANY_THROW
#define EXPECT_ANY_THROW(code) EXPECT_DEATH(code, ".")
#endif
TEST
(
Async
,
SeparateFulfillerDiscarded
)
{
EventLoop
loop
;
WaitScope
waitScope
(
loop
);
...
...
c++/src/kj/common-test.c++
View file @
8f858fc4
...
...
@@ -245,13 +245,7 @@ TEST(Common, Downcast) {
EXPECT_EQ
(
&
bar
,
&
downcast
<
Bar
>
(
foo
));
#if defined(KJ_DEBUG) && !KJ_NO_RTTI
#if KJ_NO_EXCEPTIONS
#ifdef KJ_DEBUG
EXPECT_DEATH_IF_SUPPORTED
(
downcast
<
Baz
>
(
foo
),
"Value cannot be downcast"
);
#endif
#else
EXPECT_ANY_THROW
(
downcast
<
Baz
>
(
foo
));
#endif
KJ_EXPECT_THROW_MESSAGE
(
"Value cannot be downcast"
,
downcast
<
Baz
>
(
foo
));
#endif
#if KJ_NO_RTTI
...
...
c++/src/kj/compat/gtest.h
View file @
8f858fc4
...
...
@@ -98,8 +98,22 @@ private:
#define ADD_FAILURE() ::kj::AddFailureAdapter(__FILE__, __LINE__)
#if KJ_NO_EXCEPTIONS
#define EXPECT_ANY_THROW(code) \
KJ_EXPECT(::kj::_::expectFatalThrow(nullptr, nullptr, [&]() { code; }))
#else
#define EXPECT_ANY_THROW(code) \
KJ_EXPECT(::kj::runCatchingExceptions([&]() { code; }) != nullptr)
#endif
#define EXPECT_NONFATAL_FAILURE(code) \
EXPECT_TRUE(kj::runCatchingExceptions([&]() { code; }) != nullptr);
#ifdef KJ_DEBUG
#define EXPECT_DEBUG_ANY_THROW EXPECT_ANY_THROW
#else
#define EXPECT_DEBUG_ANY_THROW(EXP)
#endif
#define TEST(x, y) KJ_TEST("legacy test: " #x "/" #y)
...
...
c++/src/kj/exception-test.c++
View file @
8f858fc4
...
...
@@ -97,11 +97,7 @@ TEST(Exception, UnwindDetector) {
#if !__MINGW32__ // Inexplicably crashes when exception is thrown from constructor.
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
);
#endif
KJ_EXPECT_THROW_MESSAGE
(
"must be allocated on the stack"
,
new
ExceptionCallback
);
}
#endif // !__MINGW32__
...
...
c++/src/kj/mutex-test.c++
View file @
8f858fc4
...
...
@@ -42,20 +42,6 @@ inline void delay() { Sleep(10); }
inline
void
delay
()
{
usleep
(
10000
);
}
#endif
#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 KJ_DEBUG
#define EXPECT_DEBUG_ANY_THROW EXPECT_ANY_THROW
#else
#define EXPECT_DEBUG_ANY_THROW(EXP)
#endif
TEST
(
Mutex
,
MutexGuarded
)
{
MutexGuarded
<
uint
>
value
(
123
);
...
...
c++/src/kj/string-test.c++
View file @
8f858fc4
...
...
@@ -86,22 +86,22 @@ TEST(String, parseAs) {
EXPECT_TRUE
(
isNaN
(
StringPtr
(
"nan"
).
parseAs
<
double
>
()));
EXPECT_TRUE
(
isNaN
(
StringPtr
(
"NAN"
).
parseAs
<
double
>
()));
EXPECT_TRUE
(
isNaN
(
StringPtr
(
"NaN"
).
parseAs
<
double
>
()));
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
""
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"a"
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"1a"
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"+-1"
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
""
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"a"
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"1a"
).
parseAs
<
double
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"+-1"
).
parseAs
<
double
>
());
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
float
>
(),
1.0
);
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
int64_t
>
(),
1
);
EXPECT_EQ
(
StringPtr
(
"9223372036854775807"
).
parseAs
<
int64_t
>
(),
9223372036854775807LL
);
EXPECT_EQ
(
StringPtr
(
"-9223372036854775808"
).
parseAs
<
int64_t
>
(),
-
9223372036854775808ULL
);
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"9223372036854775808"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"-9223372036854775809"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
""
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"a"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"1a"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"+-1"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"9223372036854775808"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"-9223372036854775809"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
""
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"a"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"1a"
).
parseAs
<
int64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"+-1"
).
parseAs
<
int64_t
>
());
EXPECT_EQ
(
StringPtr
(
"010"
).
parseAs
<
int64_t
>
(),
10
);
EXPECT_EQ
(
StringPtr
(
"0010"
).
parseAs
<
int64_t
>
(),
10
);
EXPECT_EQ
(
StringPtr
(
"0x10"
).
parseAs
<
int64_t
>
(),
16
);
...
...
@@ -113,24 +113,24 @@ TEST(String, parseAs) {
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
uint64_t
>
(),
1
);
EXPECT_EQ
(
StringPtr
(
"0"
).
parseAs
<
uint64_t
>
(),
0
);
EXPECT_EQ
(
StringPtr
(
"18446744073709551615"
).
parseAs
<
uint64_t
>
(),
18446744073709551615ULL
);
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"-1"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"18446744073709551616"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
""
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"a"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"1a"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"not contain valid"
,
StringPtr
(
"+-1"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"-1"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"18446744073709551616"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
""
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"a"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"1a"
).
parseAs
<
uint64_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"not contain valid"
,
StringPtr
(
"+-1"
).
parseAs
<
uint64_t
>
());
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
int32_t
>
(),
1
);
EXPECT_EQ
(
StringPtr
(
"2147483647"
).
parseAs
<
int32_t
>
(),
2147483647
);
EXPECT_EQ
(
StringPtr
(
"-2147483648"
).
parseAs
<
int32_t
>
(),
-
2147483648
);
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"2147483648"
).
parseAs
<
int32_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"-2147483649"
).
parseAs
<
int32_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"2147483648"
).
parseAs
<
int32_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"-2147483649"
).
parseAs
<
int32_t
>
());
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
uint32_t
>
(),
1
);
EXPECT_EQ
(
StringPtr
(
"0"
).
parseAs
<
uint32_t
>
(),
0U
);
EXPECT_EQ
(
StringPtr
(
"4294967295"
).
parseAs
<
uint32_t
>
(),
4294967295U
);
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"-1"
).
parseAs
<
uint32_t
>
());
KJ_EXPECT_THROW_MESSAGE
(
"out-of-range"
,
StringPtr
(
"4294967296"
).
parseAs
<
uint32_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"-1"
).
parseAs
<
uint32_t
>
());
KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE
(
"out-of-range"
,
StringPtr
(
"4294967296"
).
parseAs
<
uint32_t
>
());
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
int16_t
>
(),
1
);
EXPECT_EQ
(
StringPtr
(
"1"
).
parseAs
<
uint16_t
>
(),
1
);
...
...
c++/src/kj/string.c++
View file @
8f858fc4
...
...
@@ -40,25 +40,26 @@ bool isHex(const char *s) {
}
long
long
parseSigned
(
const
StringPtr
&
s
,
long
long
min
,
long
long
max
)
{
KJ_REQUIRE
(
s
!=
nullptr
,
"String does not contain valid number"
,
s
)
;
KJ_REQUIRE
(
s
!=
nullptr
,
"String does not contain valid number"
,
s
)
{
return
0
;
}
char
*
endPtr
;
errno
=
0
;
auto
value
=
strtoll
(
s
.
begin
(),
&
endPtr
,
isHex
(
s
.
cStr
())
?
16
:
10
);
KJ_REQUIRE
(
endPtr
==
s
.
end
(),
"String does not contain valid number"
,
s
)
;
KJ_REQUIRE
(
errno
!=
ERANGE
,
"Value out-of-range"
,
s
)
;
KJ_REQUIRE
(
value
>=
min
&&
value
<=
max
,
"Value out-of-range"
,
value
,
min
,
max
)
;
KJ_REQUIRE
(
endPtr
==
s
.
end
(),
"String does not contain valid number"
,
s
)
{
return
0
;
}
KJ_REQUIRE
(
errno
!=
ERANGE
,
"Value out-of-range"
,
s
)
{
return
0
;
}
KJ_REQUIRE
(
value
>=
min
&&
value
<=
max
,
"Value out-of-range"
,
value
,
min
,
max
)
{
return
0
;
}
return
value
;
}
unsigned
long
long
parseUnsigned
(
const
StringPtr
&
s
,
unsigned
long
long
max
)
{
KJ_REQUIRE
(
s
!=
nullptr
,
"String does not contain valid number"
,
s
)
;
KJ_REQUIRE
(
s
!=
nullptr
,
"String does not contain valid number"
,
s
)
{
return
0
;
}
char
*
endPtr
;
errno
=
0
;
auto
value
=
strtoull
(
s
.
begin
(),
&
endPtr
,
isHex
(
s
.
cStr
())
?
16
:
10
);
KJ_REQUIRE
(
endPtr
==
s
.
end
(),
"String does not contain valid number"
,
s
);
KJ_REQUIRE
(
errno
!=
ERANGE
,
"Value out-of-range"
,
s
);
KJ_REQUIRE
(
value
<=
max
,
"Value out-of-range"
,
value
,
max
);
KJ_REQUIRE
(
s
[
0
]
!=
'-'
,
"Value out-of-range"
,
s
);
//strtoull("-1") does not fail with ERANGE
KJ_REQUIRE
(
endPtr
==
s
.
end
(),
"String does not contain valid number"
,
s
)
{
return
0
;
}
KJ_REQUIRE
(
errno
!=
ERANGE
,
"Value out-of-range"
,
s
)
{
return
0
;
}
KJ_REQUIRE
(
value
<=
max
,
"Value out-of-range"
,
value
,
max
)
{
return
0
;
}
//strtoull("-1") does not fail with ERANGE
KJ_REQUIRE
(
s
[
0
]
!=
'-'
,
"Value out-of-range"
,
s
)
{
return
0
;
}
return
value
;
}
...
...
@@ -75,11 +76,11 @@ T parseInteger(const StringPtr& s) {
}
double
parseDouble
(
const
StringPtr
&
s
)
{
KJ_REQUIRE
(
s
!=
nullptr
,
"String does not contain valid number"
,
s
)
;
KJ_REQUIRE
(
s
!=
nullptr
,
"String does not contain valid number"
,
s
)
{
return
0
;
}
char
*
endPtr
;
errno
=
0
;
auto
value
=
strtod
(
s
.
begin
(),
&
endPtr
);
KJ_REQUIRE
(
endPtr
==
s
.
end
(),
"String does not contain valid floating number"
,
s
)
;
KJ_REQUIRE
(
endPtr
==
s
.
end
(),
"String does not contain valid floating number"
,
s
)
{
return
0
;
}
return
value
;
}
...
...
c++/src/kj/test.c++
View file @
8f858fc4
...
...
@@ -28,6 +28,9 @@
#include <string.h>
#ifndef _WIN32
#include <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif
namespace
kj
{
...
...
@@ -59,7 +62,7 @@ TestCase::~TestCase() {
namespace
_
{
// private
bool
hasSubstring
(
kj
::
StringPtr
haystack
,
kj
::
StringPtr
needle
)
{
bool
hasSubstring
(
StringPtr
haystack
,
StringPtr
needle
)
{
// TODO(perf): This is not the best algorithm for substring matching.
if
(
needle
.
size
()
<=
haystack
.
size
())
{
for
(
size_t
i
=
0
;
i
<=
haystack
.
size
()
-
needle
.
size
();
i
++
)
{
...
...
@@ -96,6 +99,74 @@ void LogExpectation::logMessage(
// =======================================================================================
namespace
{
class
FatalThrowExpectation
:
public
ExceptionCallback
{
public
:
FatalThrowExpectation
(
Maybe
<
Exception
::
Type
>
type
,
Maybe
<
StringPtr
>
message
)
:
type
(
type
),
message
(
message
)
{}
virtual
void
onFatalException
(
Exception
&&
exception
)
{
KJ_IF_MAYBE
(
expectedType
,
type
)
{
if
(
exception
.
getType
()
!=
*
expectedType
)
{
KJ_LOG
(
ERROR
,
"threw exception of wrong type"
,
exception
,
*
expectedType
);
_exit
(
1
);
}
}
KJ_IF_MAYBE
(
expectedSubstring
,
message
)
{
if
(
!
hasSubstring
(
exception
.
getDescription
(),
*
expectedSubstring
))
{
KJ_LOG
(
ERROR
,
"threw exception with wrong message"
,
exception
,
*
expectedSubstring
);
_exit
(
1
);
}
}
_exit
(
0
);
}
private
:
Maybe
<
Exception
::
Type
>
type
;
Maybe
<
StringPtr
>
message
;
};
}
// namespace
bool
expectFatalThrow
(
kj
::
Maybe
<
Exception
::
Type
>
type
,
kj
::
Maybe
<
StringPtr
>
message
,
Function
<
void
()
>
code
)
{
#if _WIN32
// We don't support death tests on Windows due to lack of efficient fork.
return
true
;
#else
pid_t
child
;
KJ_SYSCALL
(
child
=
fork
());
if
(
child
==
0
)
{
KJ_DEFER
(
_exit
(
1
));
FatalThrowExpectation
expectation
(
type
,
message
);
KJ_IF_MAYBE
(
e
,
kj
::
runCatchingExceptions
([
&
]()
{
code
();
}))
{
KJ_LOG
(
ERROR
,
"a non-fatal exception was thrown, but we expected fatal"
,
*
e
);
}
else
{
KJ_LOG
(
ERROR
,
"no fatal exception was thrown"
);
}
}
int
status
;
KJ_SYSCALL
(
waitpid
(
child
,
&
status
,
0
));
if
(
WIFEXITED
(
status
))
{
return
WEXITSTATUS
(
status
)
==
0
;
}
else
if
(
WIFSIGNALED
(
status
))
{
KJ_FAIL_EXPECT
(
"subprocess crashed without throwing exception"
,
WTERMSIG
(
status
));
return
false
;
}
else
{
KJ_FAIL_EXPECT
(
"subprocess neiter excited nor crashed?"
,
status
);
return
false
;
}
#endif
}
// =======================================================================================
GlobFilter
::
GlobFilter
(
const
char
*
pattern
)
:
pattern
(
heapString
(
pattern
))
{}
GlobFilter
::
GlobFilter
(
ArrayPtr
<
const
char
>
pattern
)
:
pattern
(
heapString
(
pattern
))
{}
...
...
c++/src/kj/test.h
View file @
8f858fc4
...
...
@@ -28,6 +28,7 @@
#include "debug.h"
#include "vector.h"
#include "function.h"
namespace
kj
{
...
...
@@ -74,7 +75,7 @@ private:
if (cond); else KJ_FAIL_EXPECT("failed: expected " #cond, ##__VA_ARGS__)
#endif
#define KJ_EXPECT_THROW(type, code) \
#define KJ_EXPECT_THROW
_RECOVERABLE
(type, code) \
do { \
KJ_IF_MAYBE(e, ::kj::runCatchingExceptions([&]() { code; })) { \
KJ_EXPECT(e->getType() == ::kj::Exception::Type::type, \
...
...
@@ -84,7 +85,7 @@ private:
} \
} while (false)
#define KJ_EXPECT_THROW_MESSAGE(message, code) \
#define KJ_EXPECT_THROW_
RECOVERABLE_
MESSAGE(message, code) \
do { \
KJ_IF_MAYBE(e, ::kj::runCatchingExceptions([&]() { code; })) { \
KJ_EXPECT(::kj::_::hasSubstring(e->getDescription(), message), \
...
...
@@ -94,6 +95,20 @@ private:
} \
} while (false)
#if KJ_NO_EXCEPTIONS
#define KJ_EXPECT_THROW(type, code) \
do { \
KJ_EXPECT(::kj::_::expectFatalThrow(type, nullptr, [&]() { code; })); \
} while (false)
#define KJ_EXPECT_THROW_MESSAGE(message, code) \
do { \
KJ_EXPECT(::kj::_::expectFatalThrow(nullptr, kj::StringPtr(message), [&]() { code; })); \
} while (false)
#else
#define KJ_EXPECT_THROW KJ_EXPECT_THROW_RECOVERABLE
#define KJ_EXPECT_THROW_MESSAGE KJ_EXPECT_THROW_RECOVERABLE_MESSAGE
#endif
#define KJ_EXPECT_LOG(level, substring) \
::kj::_::LogExpectation KJ_UNIQUE_NAME(_kjLogExpectation)(::kj::LogSeverity::level, substring)
// Expects that a log message with the given level and substring text will be printed within
...
...
@@ -105,6 +120,14 @@ namespace _ { // private
bool
hasSubstring
(
kj
::
StringPtr
haystack
,
kj
::
StringPtr
needle
);
#if KJ_NO_EXCEPTIONS
bool
expectFatalThrow
(
Maybe
<
Exception
::
Type
>
type
,
Maybe
<
StringPtr
>
message
,
Function
<
void
()
>
code
);
// Expects that the given code will throw a fatal exception matching the given type and/or message.
// Since exceptions are disabled, the test will fork() and run in a subprocess. On Windows, where
// fork() is not available, this always returns true.
#endif
class
LogExpectation
:
public
ExceptionCallback
{
public
:
LogExpectation
(
LogSeverity
severity
,
StringPtr
substring
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment