88
%% Command frequencies are tuned so that queues are normally reasonably
89
%% short, but they may sometimes exceed ?QUEUE_MAXLEN. Publish-multiple
90
%% and purging cause extreme queue lengths, so these have lower probabilities.
91
%% Fetches are sufficiently frequent so that commands that need acktags
92
%% get decent coverage.
88
%% Command frequencies are tuned so that queues are normally
89
%% reasonably short, but they may sometimes exceed
90
%% ?QUEUE_MAXLEN. Publish-multiple and purging cause extreme queue
91
%% lengths, so these have lower probabilities. Fetches/drops are
92
%% sufficiently frequent so that commands that need acktags get decent
95
96
frequency([{10, qc_publish(S)},
96
97
{1, qc_publish_delivered(S)},
97
98
{1, qc_publish_multiple(S)}, %% very slow
98
{15, qc_fetch(S)}, %% needed for ack and requeue
99
{9, qc_fetch(S)}, %% needed for ack and requeue
100
102
{15, qc_requeue(S)},
101
103
{3, qc_set_ram_duration_target(S)},
112
115
#message_properties{needs_confirming = frequency([{1, true},
114
117
expiry = oneof([undefined | lists:seq(1, 10)])},
117
120
qc_publish_multiple(#state{}) ->
118
121
{call, ?MODULE, publish_multiple, [resize(?QUEUE_MAXLEN, pos_integer())]}.
124
127
qc_fetch(#state{bqstate = BQ}) ->
125
128
{call, ?BQMOD, fetch, [boolean(), BQ]}.
130
qc_drop(#state{bqstate = BQ}) ->
131
{call, ?BQMOD, drop, [boolean(), BQ]}.
127
133
qc_ack(#state{bqstate = BQ, acks = Acks}) ->
128
134
{call, ?BQMOD, ack, [rand_choice(proplists:get_keys(Acks)), BQ]}.
141
147
{call, ?BQMOD, drain_confirmed, [BQ]}.
143
149
qc_dropwhile(#state{bqstate = BQ}) ->
144
{call, ?BQMOD, dropwhile, [fun dropfun/1, false, BQ]}.
150
{call, ?BQMOD, dropwhile, [fun dropfun/1, BQ]}.
146
152
qc_is_empty(#state{bqstate = BQ}) ->
147
153
{call, ?BQMOD, is_empty, [BQ]}.
152
158
qc_purge(#state{bqstate = BQ}) ->
153
159
{call, ?BQMOD, purge, [BQ]}.
161
qc_fold(#state{bqstate = BQ}) ->
162
{call, ?BQMOD, fold, [makefoldfun(pos_integer()), foldacc(), BQ]}.
157
166
%% Create long queues by only allowing publishing
176
next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Pid, _BQ]}) ->
185
next_state(S, BQ, {call, ?BQMOD, publish, [Msg, MsgProps, _Del, _Pid, _BQ]}) ->
177
186
#state{len = Len,
178
187
messages = Messages,
179
188
confirms = Confirms,
219
228
next_state(S, Res, {call, ?BQMOD, fetch, [AckReq, _BQ]}) ->
220
#state{len = Len, messages = Messages, acks = Acks} = S,
221
ResultInfo = {call, erlang, element, [1, Res]},
222
BQ1 = {call, erlang, element, [2, Res]},
223
AckTag = {call, erlang, element, [3, ResultInfo]},
224
S1 = S#state{bqstate = BQ1},
225
case gb_trees:is_empty(Messages) of
227
false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages),
228
S2 = S1#state{len = Len - 1, messages = M2},
231
S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]};
229
next_state_fetch_and_drop(S, Res, AckReq, 3);
231
next_state(S, Res, {call, ?BQMOD, drop, [AckReq, _BQ]}) ->
232
next_state_fetch_and_drop(S, Res, AckReq, 2);
237
234
next_state(S, Res, {call, ?BQMOD, ack, [AcksArg, _BQ]}) ->
238
235
#state{acks = AcksState} = S,
265
262
S#state{bqstate = BQ1};
267
264
next_state(S, Res, {call, ?BQMOD, dropwhile, _Args}) ->
268
BQ = {call, erlang, element, [3, Res]},
265
BQ = {call, erlang, element, [2, Res]},
269
266
#state{messages = Messages} = S,
270
267
Msgs1 = drop_messages(Messages),
271
268
S#state{bqstate = BQ, len = gb_trees:size(Msgs1), messages = Msgs1};
279
276
next_state(S, Res, {call, ?BQMOD, purge, _Args}) ->
280
277
BQ1 = {call, erlang, element, [2, Res]},
281
S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()}.
278
S#state{bqstate = BQ1, len = 0, messages = gb_trees:empty()};
280
next_state(S, Res, {call, ?BQMOD, fold, _Args}) ->
281
BQ1 = {call, erlang, element, [2, Res]},
282
S#state{bqstate = BQ1}.
283
284
%% Postconditions
285
286
postcondition(S, {call, ?BQMOD, fetch, _Args}, Res) ->
286
287
#state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S,
288
{{MsgFetched, _IsDelivered, AckTag, RemainingLen}, _BQ} ->
289
{{MsgFetched, _IsDelivered, AckTag}, _BQ} ->
289
290
{_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages),
290
291
MsgFetched =:= Msg andalso
291
292
not proplists:is_defined(AckTag, Acks) andalso
292
293
not gb_sets:is_element(AckTag, Confrms) andalso
293
RemainingLen =:= Len - 1;
299
postcondition(S, {call, ?BQMOD, drop, _Args}, Res) ->
300
#state{messages = Messages, len = Len, acks = Acks, confirms = Confrms} = S,
302
{{MsgIdFetched, AckTag}, _BQ} ->
303
{_SeqId, {_MsgProps, Msg}} = gb_trees:smallest(Messages),
304
MsgId = eval({call, erlang, element,
305
[?RECORD_INDEX(id, basic_message), Msg]}),
306
MsgIdFetched =:= MsgId andalso
307
not proplists:is_defined(AckTag, Acks) andalso
308
not gb_sets:is_element(AckTag, Confrms) andalso
313
329
lists:all(fun (M) -> gb_sets:is_element(M, Confirms) end,
314
330
ReportedConfirmed);
332
postcondition(S, {call, ?BQMOD, fold, [FoldFun, Acc0, _BQ0]}, {Res, _BQ1}) ->
333
#state{messages = Messages} = S,
334
{_, Model} = lists:foldl(fun ({_SeqId, {_MsgProps, _Msg}}, {stop, Acc}) ->
336
({_SeqId, {MsgProps, Msg}}, {cont, Acc}) ->
337
FoldFun(Msg, MsgProps, false, Acc)
338
end, {cont, Acc0}, gb_trees:to_list(Messages)),
339
true = Model =:= Res;
316
341
postcondition(#state{bqstate = BQ, len = Len}, {call, _M, _F, _A}, _Res) ->
317
342
?BQMOD:len(BQ) =:= Len.
371
396
rand_choice(List -- [Picked], [Picked | Selection],
400
fun (Msg, _MsgProps, Unacked, Acc) ->
401
case {length(Acc) > Size, Unacked} of
402
{false, false} -> {cont, [Msg | Acc]};
403
{false, true} -> {cont, Acc};
404
{true, _} -> {stop, Acc}
374
409
dropfun(Props) ->
375
410
Expiry = eval({call, erlang, element,
376
411
[?RECORD_INDEX(expiry, message_properties), Props]}),
426
next_state_fetch_and_drop(S, Res, AckReq, AckTagIdx) ->
427
#state{len = Len, messages = Messages, acks = Acks} = S,
428
ResultInfo = {call, erlang, element, [1, Res]},
429
BQ1 = {call, erlang, element, [2, Res]},
430
AckTag = {call, erlang, element, [AckTagIdx, ResultInfo]},
431
S1 = S#state{bqstate = BQ1},
432
case gb_trees:is_empty(Messages) of
434
false -> {SeqId, MsgProp_Msg, M2} = gb_trees:take_smallest(Messages),
435
S2 = S1#state{len = Len - 1, messages = M2},
438
S2#state{acks = [{AckTag, {SeqId, MsgProp_Msg}}|Acks]};
393
446
-export([prop_disabled/0]).