@@ -31,27 +31,12 @@ setup_max_size() ->
3131setup_max_items_and_size () ->
3232 setup ([{max_size , 160 }, {max_items , 3 }]).
3333
34- setup_multi_workers () ->
35- {Q , Producer , Consumer1 } = setup ([
36- {max_size , 160 },
37- {max_items , 3 },
38- {multi_workers , true }
39- ]),
40- Consumer2 = spawn_consumer (Q ),
41- Consumer3 = spawn_consumer (Q ),
42- {Q , Producer , [Consumer1 , Consumer2 , Consumer3 ]}.
43-
44- teardown ({Q , Producer , Consumers }) when is_list (Consumers ) ->
34+ teardown ({Q , Producer , Consumer }) ->
4535 % consume all to unblock and let producer/consumer stop without timeout
46- [consume (Consumer , all ) || Consumer <- Consumers ],
47-
36+ consume (Consumer , all ),
4837 ok = close_queue (Q ),
4938 ok = stop (Producer , " producer" ),
50- R = [stop (Consumer , " consumer" ) || Consumer <- Consumers ],
51- R = [ok || _ <- Consumers ],
52- ok ;
53- teardown ({Q , Producer , Consumer }) ->
54- teardown ({Q , Producer , [Consumer ]}).
39+ ok = stop (Consumer , " consumer" ).
5540
5641single_consumer_test_ () ->
5742 {
@@ -87,29 +72,16 @@ single_consumer_test_() ->
8772 ]
8873 }.
8974
90- multiple_consumers_test_ () ->
91- {
92- " Single producer and multiple consumers" ,
93- [
94- {
95- " Queue with max size of 160 bytes and 3 max items" ,
96- {
97- foreach ,
98- fun setup_multi_workers /0 ,
99- fun teardown /1 ,
100- common_cases () ++ multiple_consumers ()
101- }
102- }
103- ]
104- }.
105-
10675common_cases () ->
10776 [
10877 fun should_block_consumer_on_dequeue_from_empty_queue /1 ,
10978 fun should_consume_right_item /1 ,
11079 fun should_timeout_on_close_non_empty_queue /1 ,
11180 fun should_not_block_producer_for_non_empty_queue_after_close /1 ,
112- fun should_be_closed /1
81+ fun should_be_closed /1 ,
82+ fun should_produce_consume_any_term /1 ,
83+ fun should_crash_on_multiple_consumers /1 ,
84+ fun should_crash_on_random_messages /1
11385 ].
11486
11587single_consumer_max_item_count () ->
@@ -134,13 +106,6 @@ single_consumer_max_size() ->
134106single_consumer_max_items_and_size () ->
135107 single_consumer_max_item_count () ++ single_consumer_max_size ().
136108
137- multiple_consumers () ->
138- [
139- fun should_have_zero_size_for_new_queue /1 ,
140- fun should_have_no_items_for_new_queue /1 ,
141- fun should_increase_queue_size_on_produce /1
142- ].
143-
144109should_have_no_items_for_new_queue ({Q , _ , _ }) ->
145110 ? _assertEqual (0 , couch_work_queue :item_count (Q )).
146111
@@ -279,19 +244,6 @@ should_not_block_producer_for_non_empty_queue_after_close({Q, Producer, _}) ->
279244
280245 ? _assertEqual ({ok , 1 , 1 }, {Pong , Size , Count }).
281246
282- should_be_closed ({Q , _ , Consumers }) when is_list (Consumers ) ->
283- ok = close_queue (Q ),
284-
285- [consume (C , 1 ) || C <- Consumers ],
286-
287- LastConsumerItems = [last_consumer_items (C ) || C <- Consumers ],
288- ItemsCount = couch_work_queue :item_count (Q ),
289- Size = couch_work_queue :size (Q ),
290-
291- ? _assertEqual (
292- {[closed , closed , closed ], closed , closed },
293- {LastConsumerItems , ItemsCount , Size }
294- );
295247should_be_closed ({Q , _ , Consumer }) ->
296248 ok = close_queue (Q ),
297249
@@ -306,6 +258,46 @@ should_be_closed({Q, _, Consumer}) ->
306258 {LastConsumerItems , ItemsCount , Size }
307259 ).
308260
261+ should_produce_consume_any_term ({Q , Producer , Consumer }) ->
262+ Potato = produce_term (Q , Producer , potato , false ),
263+ Tomato = produce_term (Q , Producer , {tomato }, false ),
264+ Carrot = produce_term (Q , Producer , [carrot ], false ),
265+
266+ consume (Consumer , 2 ),
267+ {ok , Items1 } = last_consumer_items (Consumer ),
268+ ? _assertEqual ([Potato , Tomato ], Items1 ),
269+
270+ consume (Consumer , 1 ),
271+ {ok , Items2 } = last_consumer_items (Consumer ),
272+ ? _assertEqual ([Carrot ], Items2 ).
273+
274+ should_crash_on_multiple_consumers ({_Q , _Producer , _Consumer }) ->
275+ % Do not want to crash the test, so set up a new Q process
276+ {ok , Q } = couch_work_queue :new ([]),
277+ unlink (Q ),
278+ Ref = monitor (process , Q ),
279+ Pid1 = spawn (fun () -> couch_work_queue :dequeue (Q , 1 ) end ),
280+ Pid2 = spawn (fun () -> couch_work_queue :dequeue (Q , 1 ) end ),
281+ Reason =
282+ receive
283+ {'DOWN' , Ref , _ , _ , Res } -> Res
284+ end ,
285+ exit (Pid1 , kill ),
286+ exit (Pid2 , kill ),
287+ ? _assertEqual (multiple_workers_error , Reason ).
288+
289+ should_crash_on_random_messages ({_Q , _Producer , _Consumer }) ->
290+ % Do not want to crash the test, so set up a new Q process
291+ {ok , Q } = couch_work_queue :new ([]),
292+ unlink (Q ),
293+ Ref = monitor (process , Q ),
294+ Q ! eggplant ,
295+ Reason =
296+ receive
297+ {'DOWN' , Ref , _ , _ , Res } -> Res
298+ end ,
299+ ? _assertEqual (eggplant , Reason ).
300+
309301close_queue (Q ) ->
310302 test_util :stop_sync (
311303 Q ,
@@ -329,7 +321,10 @@ consumer_loop(Parent, Q, PrevItem) ->
329321 {last_item , Ref } ->
330322 Parent ! {item , Ref , PrevItem },
331323 consumer_loop (Parent , Q , PrevItem );
332- {consume , N } ->
324+ {consume , all } ->
325+ Result = couch_work_queue :dequeue (Q ),
326+ consumer_loop (Parent , Q , Result );
327+ {consume , N } when is_integer (N ) ->
333328 Result = couch_work_queue :dequeue (Q , N ),
334329 consumer_loop (Parent , Q , Result )
335330 end .
@@ -345,7 +340,11 @@ producer_loop(Parent, Q) ->
345340 {ping , Ref } ->
346341 Parent ! {pong , Ref },
347342 producer_loop (Parent , Q );
348- {produce , Ref , Size } ->
343+ {produce_term , Ref , Term } ->
344+ Parent ! {item , Ref , Term },
345+ ok = couch_work_queue :queue (Q , Term ),
346+ producer_loop (Parent , Q );
347+ {produce_binary , Ref , Size } ->
349348 Item = crypto :strong_rand_bytes (Size ),
350349 Parent ! {item , Ref , Item },
351350 ok = couch_work_queue :queue (Q , Item ),
@@ -365,10 +364,30 @@ last_consumer_items(Consumer) ->
365364 timeout
366365 end .
367366
367+ produce_term (Q , Producer , Term , Wait ) ->
368+ Ref = make_ref (),
369+ ItemsCount = couch_work_queue :item_count (Q ),
370+ Producer ! {produce_term , Ref , Term },
371+ receive
372+ {item , Ref , Item } when Wait ->
373+ ok = wait_increment (Q , ItemsCount ),
374+ Item ;
375+ {item , Ref , Item } ->
376+ Item
377+ after ? TIMEOUT ->
378+ erlang :error (
379+ {assertion_failed , [
380+ {module , ? MODULE },
381+ {line , ? LINE },
382+ {reason , " Timeout asking producer to produce a term" }
383+ ]}
384+ )
385+ end .
386+
368387produce (Q , Producer , Size , Wait ) ->
369388 Ref = make_ref (),
370389 ItemsCount = couch_work_queue :item_count (Q ),
371- Producer ! {produce , Ref , Size },
390+ Producer ! {produce_binary , Ref , Size },
372391 receive
373392 {item , Ref , Item } when Wait ->
374393 ok = wait_increment (Q , ItemsCount ),
0 commit comments