-
Notifications
You must be signed in to change notification settings - Fork 73
/
prop_transport.erl
118 lines (104 loc) · 3.71 KB
/
prop_transport.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
-module(prop_transport).
-compile(export_all).
-include("../include/socketio.hrl").
-include_lib("proper/include/proper.hrl").
%% Sanity checks that make sure the encoder and decoders understand
%% each other.
%% These tests DO NOT prove that we're actually decoding or encoding
%% anything. Other properties need to account for that.
prop_sanity_msg() ->
?FORALL(Str, gen_string(),
begin
S = eval(Str),
String = #msg{content=S},
Encoded = socketio_data:encode(String),
[#msg{content=Parsed}] = socketio_data:decode(#msg{content=Encoded}),
?WHENFAIL(
io:format("~p =/= ~p~n", [S,Parsed]),
S =:= Parsed
)
end).
prop_sanity_heartbeat() ->
?FORALL(N, heartbeat(),
begin
Hb = #heartbeat{index=N},
Encoded = socketio_data:encode(Hb),
[#heartbeat{index=Parsed}] = socketio_data:decode(#msg{content=Encoded}),
?WHENFAIL(
io:format("~p =/= ~p~n", [N, Parsed]),
N =:= Parsed
)
end).
prop_sanity_json() ->
?FORALL(Term, json(),
begin
J = #msg{content=Term, json=true},
Encoded = socketio_data:encode(J),
[#msg{content=Parsed, json=true}] = socketio_data:decode(#msg{content=Encoded}),
?WHENFAIL(
io:format("~p =/= ~p~n", [Term, Parsed]),
Term =:= Parsed
)
end).
%% Given the nature of the socket.io protocol and the environment it is part of,
%% it will happen that messages will be appended one to each other. The parser
%% Should thus return a list of messages to be considered, in the order they
%% were received (we assume first received is first, here)
prop_client_buffered_messages_count() ->
?FORALL({MsgNum, MsgString}, gen_encoded_many(),
begin
Decoded = socketio_data:decode(#msg{content=eval(MsgString)}),
equals(length(Decoded), MsgNum)
end).
prop_client_buffered_messages_sanity() ->
?FORALL({_, MsgString}, gen_encoded_many(),
begin
Decoded = socketio_data:decode(#msg{content=eval(MsgString)}),
Encoded = lists:flatten([socketio_data:encode(M) || M <- Decoded]),
?WHENFAIL(io:format("~p =/= ~p~n",[MsgString, Encoded]),
equals(MsgString, Encoded))
end).
%%% GENERATORS
%% generates strings with a more certain presence of escape characters
gen_encoded_many() ->
?SUCHTHAT(X, gen_encoded({0, ""}), X =/= {0, ""}).
gen_encoded({N, Encoded}) ->
?LET(E, Encoded,
?LAZY(union([
{N, Encoded},
gen_encoded({N+1, ?LET(X, heartbeat(), E++socketio_data:encode(#heartbeat{index=X}))}),
gen_encoded({N+1, ?LET(X, json(), E++socketio_data:encode(#msg{content=X, json=true}))}),
gen_encoded({N+1, ?LET(X, gen_string(), E++socketio_data:encode(#msg{content=X}))})
]))
).
gen_string() ->
?LAZY(weighted_union([
{1, []},
{1, [$~|string()]},
{10, [char()|gen_string()]}
])).
heartbeat() -> ?LET(N, int(), abs(N)).
json() ->
?LET(Compiled,
?LET(X, [json_data(),end_json], lists:flatten(X)),
jsx:json_to_term(jsx:format(jsx:eventify(Compiled)))).
json_data() ->
?LAZY(weighted_union([
{1,?LET(X, [start_array, json_data(), end_array], lists:flatten(X))},
{1,?LET(X, [start_object,json_object(),end_object], lists:flatten(X))}
])).
json_object() ->
?LAZY(union([[], [key(), val()]])).
key() -> {key, ascii()}.
val() ->
union([
{literal,true},
{literal,false},
{literal,null},
{integer,?LET(N, int(), integer_to_list(N))},
{string, ascii()},
{float,
?LET({A,B}, {int(),nat()}, integer_to_list(A)++[$.]++integer_to_list(B))}
]).
ascii() ->
list(choose($a,$z)).