Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
07e53a43
Commit
07e53a43
authored
Jan 22, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add JSON reader/writer to serialization tests
parent
74034160
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
275 additions
and
126 deletions
+275
-126
libcaf_core/caf/detail/parse.hpp
libcaf_core/caf/detail/parse.hpp
+46
-18
libcaf_core/caf/inspector_access.hpp
libcaf_core/caf/inspector_access.hpp
+1
-4
libcaf_core/caf/json_writer.hpp
libcaf_core/caf/json_writer.hpp
+2
-4
libcaf_core/src/detail/json.cpp
libcaf_core/src/detail/json.cpp
+17
-5
libcaf_core/src/json_reader.cpp
libcaf_core/src/json_reader.cpp
+3
-3
libcaf_core/src/json_writer.cpp
libcaf_core/src/json_writer.cpp
+77
-48
libcaf_core/test/core-test.hpp
libcaf_core/test/core-test.hpp
+41
-0
libcaf_core/test/json_reader.cpp
libcaf_core/test/json_reader.cpp
+0
-30
libcaf_core/test/json_writer.cpp
libcaf_core/test/json_writer.cpp
+33
-0
libcaf_core/test/serialization.cpp
libcaf_core/test/serialization.cpp
+55
-14
No files found.
libcaf_core/caf/detail/parse.hpp
View file @
07e53a43
...
...
@@ -77,6 +77,33 @@ parse(string_parser_state& ps, T& x) {
return
parse
(
ps
,
reinterpret_cast
<
squashed_type
&>
(
x
));
}
// When parsing regular integers, a "071" is 57 because the parser reads it as
// an octal number. This wrapper forces the parser to ignore leading zeros
// instead and always read numbers as decimals.
template
<
class
Integer
>
struct
zero_padded_integer
{
Integer
val
=
0
;
};
template
<
class
Integer
>
void
parse
(
string_parser_state
&
ps
,
zero_padded_integer
<
Integer
>&
x
)
{
x
.
val
=
0
;
ps
.
skip_whitespaces
();
if
(
ps
.
at_end
())
{
// Let the actual parser implementation set an appropriate error code.
parse
(
ps
,
x
.
val
);
}
else
{
// Skip all leading zeros and then dispatch to the matching parser.
auto
c
=
ps
.
current
();
auto
j
=
ps
.
i
+
1
;
while
(
c
==
'0'
&&
j
!=
ps
.
e
&&
isdigit
(
*
j
))
{
c
=
ps
.
next
();
++
j
;
}
parse
(
ps
,
x
.
val
);
}
}
// -- floating point types -----------------------------------------------------
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
float
&
x
);
...
...
@@ -111,7 +138,7 @@ CAF_CORE_EXPORT void parse(string_parser_state&, dictionary<config_value>&);
template
<
class
...
Ts
>
bool
parse_sequence
(
string_parser_state
&
ps
,
Ts
&&
...
xs
)
{
auto
parse_one
=
[
&
](
auto
&
x
)
{
auto
parse_one
=
[
&
ps
](
auto
&
x
)
{
parse
(
ps
,
x
);
return
ps
.
code
<=
pec
::
trailing_character
;
};
...
...
@@ -162,13 +189,14 @@ void parse(string_parser_state& ps,
namespace
sc
=
std
::
chrono
;
using
value_type
=
sc
::
time_point
<
sc
::
system_clock
,
Duration
>
;
// Parse ISO 8601 as generated by detail::print.
int32_t
year
=
0
;
int32_t
month
=
0
;
int32_t
day
=
0
;
int32_t
hour
=
0
;
int32_t
minute
=
0
;
int32_t
second
=
0
;
int32_t
milliseconds
=
0
;
using
int_wrapper
=
zero_padded_integer
<
int32_t
>
;
auto
year
=
int_wrapper
{};
auto
month
=
int_wrapper
{};
auto
day
=
int_wrapper
{};
auto
hour
=
int_wrapper
{};
auto
minute
=
int_wrapper
{};
auto
second
=
int_wrapper
{};
auto
milliseconds
=
int_wrapper
{};
if
(
!
parse_sequence
(
ps
,
// YYYY-MM-DD
year
,
literal
{{
"-"
}},
month
,
literal
{{
"-"
}},
day
,
...
...
@@ -183,19 +211,19 @@ void parse(string_parser_state& ps,
return
;
// Fill tm record.
tm
record
;
record
.
tm_sec
=
second
;
record
.
tm_min
=
minute
;
record
.
tm_hour
=
hour
;
record
.
tm_mday
=
day
;
record
.
tm_mon
=
month
-
1
;
// months since January
record
.
tm_year
=
year
-
1900
;
// years since 1900
record
.
tm_sec
=
second
.
val
;
record
.
tm_min
=
minute
.
val
;
record
.
tm_hour
=
hour
.
val
;
record
.
tm_mday
=
day
.
val
;
record
.
tm_mon
=
month
.
val
-
1
;
// months since January
record
.
tm_year
=
year
.
val
-
1900
;
// years since 1900
record
.
tm_wday
=
-
1
;
// n/a
record
.
tm_yday
=
-
1
;
// n/a
record
.
tm_isdst
=
-
1
;
// n/a
// Convert to chrono time and add the milliseconds.
auto
tstamp
=
sc
::
system_clock
::
from_time_t
(
mktime
(
&
record
));
auto
since_epoch
=
sc
::
duration_cast
<
Duration
>
(
tstamp
.
time_since_epoch
());
since_epoch
+=
sc
::
milliseconds
{
milliseconds
};
since_epoch
+=
sc
::
milliseconds
{
milliseconds
.
val
};
x
=
value_type
{
since_epoch
};
}
...
...
libcaf_core/caf/inspector_access.hpp
View file @
07e53a43
...
...
@@ -764,10 +764,7 @@ struct inspector_access<
detail
::
print
(
str
,
x
);
return
str
;
};
auto
set
=
[
&
x
](
std
::
string
str
)
{
auto
err
=
detail
::
parse
(
str
,
x
);
return
!
err
;
};
auto
set
=
[
&
x
](
std
::
string
str
)
{
return
detail
::
parse
(
str
,
x
);
};
return
f
.
apply
(
get
,
set
);
}
else
{
using
rep_type
=
typename
Duration
::
rep
;
...
...
libcaf_core/caf/json_writer.hpp
View file @
07e53a43
...
...
@@ -24,7 +24,8 @@ public:
enum
class
type
:
uint8_t
{
element
,
/// Can morph into any other type except `member`.
object
,
/// Contains any number of members.
member
,
/// A single object member (key/value pair).
member
,
/// A single key-value pair.
key
,
/// The key of a field.
array
,
/// Contains any number of elements.
string
,
/// A character sequence (terminal type).
number
,
/// An integer or floating point (terminal type).
...
...
@@ -161,9 +162,6 @@ private:
// Enters a new level of nesting.
void
push
(
type
=
type
::
element
);
// Enters a new level of nesting if the current top is `expected`.
bool
push_if
(
type
expected
,
type
=
type
::
element
);
// Backs up one level of nesting.
bool
pop
();
...
...
libcaf_core/src/detail/json.cpp
View file @
07e53a43
...
...
@@ -95,10 +95,18 @@ obj_consumer val_consumer::begin_object() {
void
read_value
(
string_parser_state
&
ps
,
val_consumer
consumer
);
void
read_json_null
(
string_parser_state
&
ps
,
val_consumer
consumer
)
{
template
<
class
Consumer
>
void
read_json_null_or_nan
(
string_parser_state
&
ps
,
Consumer
consumer
)
{
enum
{
nil
,
is_null
,
is_nan
};
auto
res_type
=
nil
;
auto
g
=
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
if
(
ps
.
code
<=
pec
::
trailing_character
)
{
CAF_ASSERT
(
res_type
!=
nil
);
if
(
res_type
==
is_null
)
consumer
.
value
(
json
::
null_t
{});
else
consumer
.
value
(
std
::
numeric_limits
<
double
>::
quiet_NaN
());
}
});
// clang-format off
start
();
...
...
@@ -108,12 +116,16 @@ void read_json_null(string_parser_state& ps, val_consumer consumer) {
}
state
(
has_n
)
{
transition
(
has_nu
,
'u'
)
transition
(
has_na
,
'a'
)
}
state
(
has_nu
)
{
transition
(
has_nul
,
'l'
)
}
state
(
has_nul
)
{
transition
(
done
,
'l'
)
transition
(
done
,
'l'
,
res_type
=
is_null
)
}
state
(
has_na
)
{
transition
(
done
,
'n'
,
res_type
=
is_nan
)
}
term_state
(
done
)
{
transition
(
init
,
"
\t\n
"
)
...
...
@@ -228,7 +240,7 @@ void read_value(string_parser_state& ps, val_consumer consumer) {
transition
(
init
,
"
\t\n
"
)
fsm_epsilon
(
read_json_string
(
ps
,
consumer
),
done
,
'"'
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_json_null
(
ps
,
consumer
),
done
,
"n"
)
fsm_epsilon
(
read_json_null
_or_nan
(
ps
,
consumer
),
done
,
"n"
)
fsm_epsilon
(
read_number
(
ps
,
consumer
),
done
,
"+-.0123456789"
)
fsm_epsilon
(
read_json_object
(
ps
,
consumer
.
begin_object
()),
done
,
'{'
)
fsm_epsilon
(
read_json_array
(
ps
,
consumer
.
begin_array
()),
done
,
'['
)
...
...
libcaf_core/src/json_reader.cpp
View file @
07e53a43
...
...
@@ -125,7 +125,7 @@ bool json_reader::load(string_view json_text) {
string_parser_state
ps
{
json_text
.
begin
(),
json_text
.
end
()};
root_
=
detail
::
json
::
parse
(
ps
,
&
buf_
);
if
(
ps
.
code
!=
pec
::
success
)
{
err_
=
make_error
(
ps
);
set_error
(
make_error
(
ps
)
);
st_
=
nullptr
;
return
false
;
}
else
{
...
...
@@ -500,7 +500,7 @@ bool json_reader::value(uint64_t& x) {
bool
json_reader
::
value
(
float
&
x
)
{
auto
tmp
=
0.0
;
if
(
value
(
tmp
))
{
x
=
static_cast
<
float
>
(
x
);
x
=
static_cast
<
float
>
(
tmp
);
return
true
;
}
else
{
return
false
;
...
...
@@ -524,7 +524,7 @@ bool json_reader::value(double& x) {
bool
json_reader
::
value
(
long
double
&
x
)
{
auto
tmp
=
0.0
;
if
(
value
(
tmp
))
{
x
=
static_cast
<
long
double
>
(
x
);
x
=
static_cast
<
long
double
>
(
tmp
);
return
true
;
}
else
{
return
false
;
...
...
libcaf_core/src/json_writer.cpp
View file @
07e53a43
...
...
@@ -11,6 +11,8 @@ namespace caf {
namespace
{
static
constexpr
const
char
class_name
[]
=
"caf::json_writer"
;
constexpr
bool
can_morph
(
json_writer
::
type
from
,
json_writer
::
type
to
)
{
return
from
==
json_writer
::
type
::
element
&&
to
!=
json_writer
::
type
::
member
;
}
...
...
@@ -31,11 +33,10 @@ template <class T>
bool
json_writer
::
number
(
T
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
unsafe_morph
(
type
::
number
);
detail
::
print
(
buf_
,
x
);
pop
();
return
true
;
case
type
:
:
member
:
unsafe_morph
(
type
::
element
);
case
type
:
:
key
:
add
(
'"'
);
detail
::
print
(
buf_
,
x
);
add
(
"
\"
: "
);
...
...
@@ -80,8 +81,12 @@ void json_writer::reset() {
bool
json_writer
::
begin_object
(
type_id_t
,
string_view
name
)
{
auto
add_type_annotation
=
[
this
,
name
]
{
CAF_ASSERT
(
top
()
==
type
::
key
);
add
(
R"_("@type": )_"
);
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
detail
::
print_escaped
(
buf_
,
name
);
pop
();
return
true
;
};
return
begin_associative_array
(
0
)
// Put opening paren, ...
...
...
@@ -96,9 +101,11 @@ bool json_writer::end_object() {
bool
json_writer
::
begin_field
(
string_view
name
)
{
if
(
begin_key_value_pair
())
{
CAF_ASSERT
(
top
()
==
type
::
key
);
detail
::
print_escaped
(
buf_
,
name
);
add
(
": "
);
unsafe_morph
(
type
::
element
);
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
return
true
;
}
else
{
return
false
;
...
...
@@ -107,12 +114,15 @@ bool json_writer::begin_field(string_view name) {
bool
json_writer
::
begin_field
(
string_view
name
,
bool
is_present
)
{
if
(
begin_key_value_pair
())
{
CAF_ASSERT
(
top
()
==
type
::
key
);
detail
::
print_escaped
(
buf_
,
name
);
add
(
": "
);
if
(
is_present
)
unsafe_morph
(
type
::
element
);
else
pop
();
CAF_ASSERT
(
top
()
==
type
::
element
);
if
(
!
is_present
)
{
add
(
"null"
);
pop
();
}
return
true
;
}
else
{
return
false
;
...
...
@@ -129,7 +139,7 @@ bool json_writer::begin_field(string_view name, bool is_present,
}
bool
json_writer
::
end_field
()
{
return
pop_if_next
(
type
::
object
);
return
end_key_value_pair
(
);
}
bool
json_writer
::
begin_tuple
(
size_t
size
)
{
...
...
@@ -142,14 +152,24 @@ bool json_writer::end_tuple() {
bool
json_writer
::
begin_key_value_pair
()
{
sep
();
return
push_if
(
type
::
object
,
type
::
member
);
auto
t
=
top
();
switch
(
t
)
{
case
type
:
:
object
:
push
(
type
::
member
);
push
(
type
::
element
);
push
(
type
::
key
);
return
true
;
default:
{
std
::
string
str
=
"expected object, found "
;
str
+=
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
std
::
move
(
str
));
return
false
;
}
}
}
bool
json_writer
::
end_key_value_pair
()
{
// Note: can't check for `type::member` here, because it has been morphed to
// whatever the value type has been. But after popping the current type, there
// still has to be the `object` entry below.
return
pop_if_next
(
type
::
object
);
return
pop_if
(
type
::
member
);
}
bool
json_writer
::
begin_sequence
(
size_t
)
{
...
...
@@ -184,18 +204,14 @@ bool json_writer::end_sequence() {
bool
json_writer
::
begin_associative_array
(
size_t
)
{
switch
(
top
())
{
default:
emplace_error
(
sec
::
runtime_error
,
emplace_error
(
sec
::
runtime_error
,
class_name
,
__func__
,
"unexpected begin_object or begin_associative_array"
);
return
false
;
case
type
:
:
member
:
emplace_error
(
sec
::
runtime_error
,
"unimplemented: json_writer currently does not support "
"complex types as dictionary keys"
);
return
false
;
case
type
:
:
element
:
unsafe_morph
(
type
::
object
);
break
;
case
type
:
:
array
:
sep
();
push
(
type
::
object
);
break
;
}
...
...
@@ -210,8 +226,8 @@ bool json_writer::end_associative_array() {
--
indentation_level_
;
nl
();
add
(
'}'
);
if
(
top
()
==
type
::
array
)
s
ep
()
;
if
(
!
stack_
.
empty
()
)
s
tack_
.
back
().
filled
=
true
;
return
true
;
}
else
{
return
false
;
...
...
@@ -223,7 +239,30 @@ bool json_writer::value(byte x) {
}
bool
json_writer
::
value
(
bool
x
)
{
return
number
(
x
);
auto
add_str
=
[
this
,
x
]
{
if
(
x
)
add
(
"true"
);
else
add
(
"false"
);
};
switch
(
top
())
{
case
type
:
:
element
:
add_str
();
pop
();
return
true
;
case
type
:
:
key
:
add
(
'"'
);
add_str
();
add
(
"
\"
: "
);
return
true
;
case
type
:
:
array
:
sep
();
add_str
();
return
true
;
default:
fail
(
type
::
boolean
);
return
false
;
}
}
bool
json_writer
::
value
(
int8_t
x
)
{
...
...
@@ -273,13 +312,13 @@ bool json_writer::value(long double x) {
bool
json_writer
::
value
(
string_view
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
unsafe_morph
(
type
::
string
);
detail
::
print_escaped
(
buf_
,
x
);
pop
();
return
true
;
case
type
:
:
member
:
unsafe_morph
(
type
::
element
);
case
type
:
:
key
:
detail
::
print_escaped
(
buf_
,
x
);
add
(
": "
);
pop
();
return
true
;
case
type
:
:
array
:
sep
();
...
...
@@ -306,18 +345,19 @@ bool json_writer::value(const std::u32string&) {
bool
json_writer
::
value
(
span
<
const
byte
>
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
unsafe_morph
(
type
::
string
);
add
(
'"'
);
detail
::
append_hex
(
buf_
,
reinterpret_cast
<
const
void
*>
(
x
.
data
()),
x
.
size
());
add
(
'"'
);
pop
();
return
true
;
case
type
:
:
member
:
case
type
:
:
key
:
unsafe_morph
(
type
::
element
);
add
(
'"'
);
detail
::
append_hex
(
buf_
,
reinterpret_cast
<
const
void
*>
(
x
.
data
()),
x
.
size
());
add
(
"
\"
: "
);
pop
();
return
true
;
case
type
:
:
array
:
sep
();
...
...
@@ -342,7 +382,7 @@ void json_writer::init() {
buf_
.
reserve
(
1024
);
// Even heavily nested objects should fit into 32 levels of nesting.
stack_
.
reserve
(
32
);
//
The concrete type of the output is yet unknown
.
//
Placeholder for what is to come
.
push
();
}
...
...
@@ -358,20 +398,6 @@ void json_writer::push(type t) {
stack_
.
push_back
({
t
,
false
});
}
bool
json_writer
::
push_if
(
type
expected
,
type
t
)
{
if
(
!
stack_
.
empty
()
&&
stack_
.
back
()
==
expected
)
{
stack_
.
push_back
({
t
,
false
});
return
true
;
}
else
{
std
::
string
str
=
"push_if failed: expected = "
;
str
+=
json_type_name
(
expected
);
str
+=
", found = "
;
str
+=
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
// Backs up one level of nesting.
bool
json_writer
::
pop
()
{
if
(
!
stack_
.
empty
())
{
...
...
@@ -389,12 +415,12 @@ bool json_writer::pop_if(type t) {
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop_if failed: expected
=
"
;
std
::
string
str
=
"pop_if failed: expected "
;
str
+=
json_type_name
(
t
);
if
(
stack_
.
empty
())
{
str
+=
", found an empty stack"
;
}
else
{
str
+=
", found
=
"
;
str
+=
", found "
;
str
+=
json_type_name
(
stack_
.
back
().
t
);
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
...
...
@@ -403,17 +429,19 @@ bool json_writer::pop_if(type t) {
}
bool
json_writer
::
pop_if_next
(
type
t
)
{
if
(
stack_
.
size
()
>
1
&&
stack_
[
stack_
.
size
()
-
2
]
==
t
)
{
if
(
stack_
.
size
()
>
1
&&
(
stack_
[
stack_
.
size
()
-
2
]
==
t
||
can_morph
(
stack_
[
stack_
.
size
()
-
2
].
t
,
t
)))
{
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop_if_next failed: expected
=
"
;
std
::
string
str
=
"pop_if_next failed: expected "
;
str
+=
json_type_name
(
t
);
if
(
stack_
.
size
()
<
2
)
{
str
+=
", found a stack of size "
;
detail
::
print
(
str
,
stack_
.
size
());
}
else
{
str
+=
", found
=
"
;
str
+=
", found "
;
str
+=
json_type_name
(
stack_
[
stack_
.
size
()
-
2
].
t
);
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
...
...
@@ -469,7 +497,8 @@ void json_writer::nl() {
}
void
json_writer
::
sep
()
{
CAF_ASSERT
(
top
()
==
type
::
object
||
top
()
==
type
::
array
);
CAF_ASSERT
(
top
()
==
type
::
element
||
top
()
==
type
::
object
||
top
()
==
type
::
array
);
if
(
stack_
.
back
().
filled
)
{
if
(
indentation_factor_
>
0
)
{
add
(
",
\n
"
);
...
...
libcaf_core/test/core-test.hpp
View file @
07e53a43
...
...
@@ -297,6 +297,45 @@ bool inspect(Inspector& f, dummy_enum& x) {
return
f
.
apply
(
get
,
set
);
}
struct
point
{
int
x
;
int
y
;
};
[[
maybe_unused
]]
constexpr
bool
operator
==
(
point
a
,
point
b
)
noexcept
{
return
a
.
x
==
b
.
x
&&
a
.
y
==
b
.
y
;
}
[[
maybe_unused
]]
constexpr
bool
operator
!=
(
point
a
,
point
b
)
noexcept
{
return
!
(
a
==
b
);
}
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
point
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"x"
,
x
.
x
),
f
.
field
(
"y"
,
x
.
y
));
}
struct
rectangle
{
point
top_left
;
point
bottom_right
;
};
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
rectangle
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"top-left"
,
x
.
top_left
),
f
.
field
(
"bottom-right"
,
x
.
bottom_right
));
}
[[
maybe_unused
]]
constexpr
bool
operator
==
(
const
rectangle
&
x
,
const
rectangle
&
y
)
noexcept
{
return
x
.
top_left
==
y
.
top_left
&&
x
.
bottom_right
==
y
.
bottom_right
;
}
[[
maybe_unused
]]
constexpr
bool
operator
!=
(
const
rectangle
&
x
,
const
rectangle
&
y
)
noexcept
{
return
!
(
x
==
y
);
}
// -- type IDs for for all unit test suites ------------------------------------
#define ADD_TYPE_ID(type) CAF_ADD_TYPE_ID(core_test, type)
...
...
@@ -319,7 +358,9 @@ CAF_BEGIN_TYPE_ID_BLOCK(core_test, caf::first_custom_type_id)
ADD_TYPE_ID
((
int_actor
))
ADD_TYPE_ID
((
level
))
ADD_TYPE_ID
((
my_request
))
ADD_TYPE_ID
((
point
))
ADD_TYPE_ID
((
raw_struct
))
ADD_TYPE_ID
((
rectangle
))
ADD_TYPE_ID
((
s1
))
ADD_TYPE_ID
((
s2
))
ADD_TYPE_ID
((
s3
))
...
...
libcaf_core/test/json_reader.cpp
View file @
07e53a43
...
...
@@ -58,36 +58,6 @@ struct fixture {
std
::
vector
<
std
::
function
<
bool
()
>>
test_cases
;
};
struct
point
{
int
x
;
int
y
;
};
bool
operator
==
(
point
a
,
point
b
)
noexcept
{
return
std
::
tie
(
a
.
x
,
a
.
y
)
==
std
::
tie
(
b
.
x
,
b
.
y
);
}
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
point
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"x"
,
x
.
x
),
f
.
field
(
"y"
,
x
.
y
));
}
struct
rectangle
{
point
top_left
;
point
bottom_right
;
};
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
rectangle
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"top-left"
,
x
.
top_left
),
f
.
field
(
"bottom-right"
,
x
.
bottom_right
));
}
bool
operator
==
(
const
rectangle
&
x
,
const
rectangle
&
y
)
noexcept
{
return
std
::
tie
(
x
.
top_left
,
x
.
bottom_right
)
==
std
::
tie
(
y
.
top_left
,
y
.
bottom_right
);
}
fixture
::
fixture
()
{
using
i32_list
=
std
::
vector
<
int32_t
>
;
using
str_list
=
std
::
vector
<
std
::
string
>
;
...
...
libcaf_core/test/json_writer.cpp
View file @
07e53a43
...
...
@@ -138,4 +138,37 @@ SCENARIO("the JSON writer converts simple structs to strings") {
}
}
SCENARIO
(
"the JSON writer converts nested structs to strings"
)
{
GIVEN
(
"a rectangle object"
)
{
auto
x
=
rectangle
{{
100
,
200
},
{
10
,
20
}};
WHEN
(
"converting it to JSON with indentation factor 0"
)
{
THEN
(
"the JSON output is a single line"
)
{
std
::
string
out
=
R"({"@type": "rectangle", )"
R"("top-left": {"@type": "point", "x": 100, "y": 200}, )"
R"("bottom-right": {"@type": "point", "x": 10, "y": 20}})"
;
CHECK_EQ
(
to_json_string
(
x
,
0
),
out
);
}
}
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON output uses multiple lines"
)
{
std
::
string
out
=
R"_({
"@type": "rectangle",
"top-left": {
"@type": "point",
"x": 100,
"y": 200
},
"bottom-right": {
"@type": "point",
"x": 10,
"y": 20
}
})_"
;
CHECK_EQ
(
to_json_string
(
x
,
2
),
out
);
}
}
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/serialization.cpp
View file @
07e53a43
...
...
@@ -42,6 +42,8 @@
#include "caf/detail/stringification_inspector.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/json_reader.hpp"
#include "caf/json_writer.hpp"
#include "caf/message.hpp"
#include "caf/message_handler.hpp"
#include "caf/proxy_registry.hpp"
...
...
@@ -143,6 +145,10 @@ struct fixture : test_coordinator_fixture<> {
message
msg
;
message
recursive
;
json_writer
jwriter
;
json_reader
jreader
;
template
<
class
...
Ts
>
byte_buffer
serialize
(
const
Ts
&
...
xs
)
{
byte_buffer
buf
;
...
...
@@ -161,12 +167,39 @@ struct fixture : test_coordinator_fixture<> {
CAF_FAIL
(
"deserialization failed: "
<<
source
.
get_error
());
}
template
<
class
...
Ts
>
std
::
string
serialize_json
(
const
Ts
&
...
xs
)
{
jwriter
.
reset
();
if
(
!
(
jwriter
.
apply
(
xs
)
&&
...))
CAF_FAIL
(
"JSON serialization failed: "
<<
jwriter
.
get_error
()
<<
", data: "
<<
deep_to_string
(
std
::
forward_as_tuple
(
xs
...)));
auto
str
=
jwriter
.
str
();
return
std
::
string
{
str
.
begin
(),
str
.
end
()};
}
template
<
class
...
Ts
>
void
deserialize_json
(
const
std
::
string
&
str
,
Ts
&
...
xs
)
{
if
(
!
jreader
.
load
(
str
))
CAF_FAIL
(
"JSON loading failed: "
<<
jreader
.
get_error
()
<<
"
\n
input: "
<<
str
);
if
(
!
(
jreader
.
apply
(
xs
)
&&
...))
CAF_FAIL
(
"JSON deserialization failed: "
<<
jreader
.
get_error
()
<<
"
\n
input: "
<<
str
);
}
// serializes `x` and then deserializes and returns the serialized value
template
<
class
T
>
T
roundtrip
(
T
x
)
{
T
result
;
deserialize
(
serialize
(
x
),
result
);
return
result
;
T
roundtrip
(
T
x
,
bool
enable_json
=
true
)
{
auto
r1
=
T
{};
deserialize
(
serialize
(
x
),
r1
);
if
(
enable_json
)
{
auto
r2
=
T
{};
deserialize_json
(
serialize_json
(
x
),
r2
);
if
(
!
CAF_CHECK_EQUAL
(
r1
,
r2
))
CAF_MESSAGE
(
"generated JSON: "
<<
serialize_json
(
x
));
}
return
r1
;
}
// converts `x` to a message, serialize it, then deserializes it, and
...
...
@@ -184,7 +217,7 @@ struct fixture : test_coordinator_fixture<> {
return
result
.
get_as
<
T
>
(
0
);
}
fixture
()
{
fixture
()
:
jwriter
(
sys
),
jreader
(
sys
)
{
rs
.
str
.
assign
(
std
::
string
(
str
.
rbegin
(),
str
.
rend
()));
msg
=
make_message
(
i32
,
i64
,
ts
,
te
,
str
,
rs
);
config_value
::
dictionary
dict
;
...
...
@@ -217,12 +250,20 @@ struct is_message {
}
// namespace
#define CHECK_RT(val) CAF_CHECK_EQUAL(val, roundtrip(val))
#define CHECK_RT(val) \
do { \
CAF_MESSAGE(#val); \
CAF_CHECK_EQUAL(val, roundtrip(val)); \
} while (false)
#define CHECK_PRED_RT(pred, value) CAF_CHECK(pred(roundtrip(value)))
#define CHECK_PRED_RT(pred, value) \
do { \
CAF_MESSAGE(#pred "(" #value ")"); \
CAF_CHECK(pred(roundtrip(value, false))); \
} while (false)
#define CHECK_SIGN_RT(value) \
CAF_CHECK_EQUAL(std::signbit(roundtrip(value)), std::signbit(value))
CAF_CHECK_EQUAL(std::signbit(roundtrip(value
, false
)), std::signbit(value))
#define CHECK_MSG_RT(val) CAF_CHECK_EQUAL(val, msg_roundtrip(val))
...
...
@@ -328,7 +369,7 @@ CAF_TEST(messages) {
deserialize
(
buf2
,
y
);
CAF_CHECK_EQUAL
(
to_string
(
msg
),
to_string
(
y
));
CAF_CHECK
(
is_message
(
y
).
equal
(
i32
,
i64
,
ts
,
te
,
str
,
rs
));
CAF_CHECK_EQUAL
(
to_string
(
recursive
),
to_string
(
roundtrip
(
recursive
)));
CAF_CHECK_EQUAL
(
to_string
(
recursive
),
to_string
(
roundtrip
(
recursive
,
false
)));
}
CAF_TEST
(
multiple_messages
)
{
...
...
@@ -370,11 +411,11 @@ CAF_TEST(variant_with_tree_types) {
CAF_MESSAGE
(
"deserializing into a non-empty vector overrides any content"
);
using
test_variant
=
variant
<
int
,
double
,
std
::
string
>
;
test_variant
x
{
42
};
CAF_CHECK_EQUAL
(
x
,
roundtrip
(
x
));
CAF_CHECK_EQUAL
(
x
,
roundtrip
(
x
,
false
));
x
=
12.34
;
CAF_CHECK_EQUAL
(
x
,
roundtrip
(
x
));
CAF_CHECK_EQUAL
(
x
,
roundtrip
(
x
,
false
));
x
=
std
::
string
{
"foobar"
};
CAF_CHECK_EQUAL
(
x
,
roundtrip
(
x
));
CAF_CHECK_EQUAL
(
x
,
roundtrip
(
x
,
false
));
}
// -- our vector<bool> serialization packs into an uint64_t. Hence, the
...
...
@@ -456,10 +497,10 @@ CAF_TEST(serializers handle actor handles) {
[](
int
i
)
{
return
i
;
},
};
});
CAF_CHECK_EQUAL
(
dummy
,
roundtrip
(
dummy
));
CAF_CHECK_EQUAL
(
dummy
,
roundtrip
(
dummy
,
false
));
CAF_CHECK_EQUAL
(
dummy
,
msg_roundtrip
(
dummy
));
std
::
vector
<
strong_actor_ptr
>
wrapped
{
actor_cast
<
strong_actor_ptr
>
(
dummy
)};
CAF_CHECK_EQUAL
(
wrapped
,
roundtrip
(
wrapped
));
CAF_CHECK_EQUAL
(
wrapped
,
roundtrip
(
wrapped
,
false
));
CAF_CHECK_EQUAL
(
wrapped
,
msg_roundtrip
(
wrapped
));
anon_send_exit
(
dummy
,
exit_reason
::
user_shutdown
);
}
...
...
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