summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--test/unit_SUITE.erl965
-rw-r--r--test/unit_amqp091_content_framing_SUITE.erl240
-rw-r--r--test/unit_collections_SUITE.erl60
-rw-r--r--test/unit_config_value_encryption_SUITE.erl242
-rw-r--r--test/unit_config_value_encryption_SUITE_data/lib/rabbit_shovel_test/ebin/rabbit_shovel_test.app (renamed from test/unit_SUITE_data/lib/rabbit_shovel_test/ebin/rabbit_shovel_test.app)0
-rw-r--r--test/unit_config_value_encryption_SUITE_data/rabbit_shovel_test.passphrase (renamed from test/unit_SUITE_data/rabbit_shovel_test.passphrase)0
-rw-r--r--test/unit_disk_monitor_SUITE.erl61
-rw-r--r--test/unit_disk_monitor_mocks_SUITE.erl121
-rw-r--r--test/unit_gen_server2_SUITE.erl79
-rw-r--r--test/unit_inbroker_parallel_SUITE.erl13
-rw-r--r--test/unit_operator_policy_SUITE.erl122
-rw-r--r--test/unit_plugin_directories_SUITE.erl85
-rw-r--r--test/unit_supervisor2_SUITE.erl78
13 files changed, 1032 insertions, 1034 deletions
diff --git a/test/unit_SUITE.erl b/test/unit_SUITE.erl
deleted file mode 100644
index 2d697c6fd6..0000000000
--- a/test/unit_SUITE.erl
+++ /dev/null
@@ -1,965 +0,0 @@
-%% The contents of this file are subject to the Mozilla Public License
-%% Version 1.1 (the "License"); you may not use this file except in
-%% compliance with the License. You may obtain a copy of the License at
-%% https://www.mozilla.org/MPL/
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
-%% License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Original Code is RabbitMQ.
-%%
-%% The Initial Developer of the Original Code is GoPivotal, Inc.
-%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
-%%
-
--module(unit_SUITE).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("eunit/include/eunit.hrl").
--include_lib("rabbit_common/include/rabbit.hrl").
--include_lib("rabbit_common/include/rabbit_framing.hrl").
-
--compile(export_all).
-
-all() ->
- [
- {group, parallel_tests},
- {group, sequential_tests}
- ].
-
-groups() ->
- [
- {parallel_tests, [parallel], [
- {access_control, [parallel], [
- auth_backend_internal_expand_topic_permission
- ]},
- {basic_header_handling, [parallel], [
- write_table_with_invalid_existing_type,
- invalid_existing_headers,
- disparate_invalid_header_entries_accumulate_separately,
- corrupt_or_invalid_headers_are_overwritten,
- invalid_same_header_entry_accumulation
- ]},
- content_framing,
- content_transcoding,
- rabbitmqctl_encode,
- pmerge,
- plmerge,
- merge_operator_policy_definitions,
- rabbit_direct_extract_extra_auth_props,
- {supervisor2, [], [
- check_shutdown_stop,
- check_shutdown_ignored
- ]},
- table_codec,
- unfold
- ]},
- {sequential_tests, [], [
- decrypt_start_app,
- decrypt_start_app_file,
- decrypt_start_app_undefined,
- decrypt_start_app_wrong_passphrase,
- decrypt_config,
- listing_plugins_from_multiple_directories
- ]}
- ].
-
-init_per_group(_, Config) -> Config.
-end_per_group(_, Config) -> Config.
-
-init_per_testcase(TC, Config) when TC =:= decrypt_start_app;
- TC =:= decrypt_start_app_file;
- TC =:= decrypt_start_app_undefined;
- TC =:= decrypt_start_app_wrong_passphrase ->
- application:set_env(rabbit, feature_flags_file, "", [{persistent, true}]),
- Config;
-init_per_testcase(_Testcase, Config) ->
- Config.
-
-end_per_testcase(_TC, _Config) ->
- ok.
-
-%% -------------------------------------------------------------------
-%% basic_header_handling.
-%% -------------------------------------------------------------------
--define(XDEATH_TABLE,
- [{<<"reason">>, longstr, <<"blah">>},
- {<<"queue">>, longstr, <<"foo.bar.baz">>},
- {<<"exchange">>, longstr, <<"my-exchange">>},
- {<<"routing-keys">>, array, []}]).
-
--define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]).
-
--define(BAD_HEADER(K), {<<K>>, longstr, <<"bad ", K>>}).
--define(BAD_HEADER2(K, Suf), {<<K>>, longstr, <<"bad ", K, Suf>>}).
--define(FOUND_BAD_HEADER(K), {<<K>>, array, [{longstr, <<"bad ", K>>}]}).
-
-write_table_with_invalid_existing_type(_Config) ->
- prepend_check(<<"header1">>, ?XDEATH_TABLE, [?BAD_HEADER("header1")]).
-
-invalid_existing_headers(_Config) ->
- Headers =
- prepend_check(<<"header2">>, ?ROUTE_TABLE, [?BAD_HEADER("header2")]),
- {array, [{table, ?ROUTE_TABLE}]} =
- rabbit_misc:table_lookup(Headers, <<"header2">>),
- passed.
-
-disparate_invalid_header_entries_accumulate_separately(_Config) ->
- BadHeaders = [?BAD_HEADER("header2")],
- Headers = prepend_check(<<"header2">>, ?ROUTE_TABLE, BadHeaders),
- Headers2 = prepend_check(<<"header1">>, ?XDEATH_TABLE,
- [?BAD_HEADER("header1") | Headers]),
- {table, [?FOUND_BAD_HEADER("header1"),
- ?FOUND_BAD_HEADER("header2")]} =
- rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY),
- passed.
-
-corrupt_or_invalid_headers_are_overwritten(_Config) ->
- Headers0 = [?BAD_HEADER("header1"),
- ?BAD_HEADER("x-invalid-headers")],
- Headers1 = prepend_check(<<"header1">>, ?XDEATH_TABLE, Headers0),
- {table,[?FOUND_BAD_HEADER("header1"),
- ?FOUND_BAD_HEADER("x-invalid-headers")]} =
- rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY),
- passed.
-
-invalid_same_header_entry_accumulation(_Config) ->
- BadHeader1 = ?BAD_HEADER2("header1", "a"),
- Headers = prepend_check(<<"header1">>, ?ROUTE_TABLE, [BadHeader1]),
- Headers2 = prepend_check(<<"header1">>, ?ROUTE_TABLE,
- [?BAD_HEADER2("header1", "b") | Headers]),
- {table, InvalidHeaders} =
- rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY),
- {array, [{longstr,<<"bad header1b">>},
- {longstr,<<"bad header1a">>}]} =
- rabbit_misc:table_lookup(InvalidHeaders, <<"header1">>),
- passed.
-
-prepend_check(HeaderKey, HeaderTable, Headers) ->
- Headers1 = rabbit_basic:prepend_table_header(
- HeaderKey, HeaderTable, Headers),
- {table, Invalid} =
- rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY),
- {Type, Value} = rabbit_misc:table_lookup(Headers, HeaderKey),
- {array, [{Type, Value} | _]} =
- rabbit_misc:table_lookup(Invalid, HeaderKey),
- Headers1.
-
-decrypt_config(_Config) ->
- %% Take all available block ciphers.
- Hashes = rabbit_pbe:supported_hashes(),
- Ciphers = rabbit_pbe:supported_ciphers(),
- Iterations = [1, 10, 100, 1000],
- %% Loop through all hashes, ciphers and iterations.
- _ = [begin
- PassPhrase = crypto:strong_rand_bytes(16),
- do_decrypt_config({C, H, I, PassPhrase})
- end || H <- Hashes, C <- Ciphers, I <- Iterations],
- ok.
-
-do_decrypt_config(Algo = {C, H, I, P}) ->
- ok = application:load(rabbit),
- RabbitConfig = application:get_all_env(rabbit),
- %% Encrypt a few values in configuration.
- %% Common cases.
- _ = [encrypt_value(Key, Algo) || Key <- [
- tcp_listeners,
- num_tcp_acceptors,
- ssl_options,
- vm_memory_high_watermark,
- default_pass,
- default_permissions,
- cluster_nodes,
- auth_mechanisms,
- msg_store_credit_disc_bound]],
- %% Special case: encrypt a value in a list.
- {ok, [LoopbackUser]} = application:get_env(rabbit, loopback_users),
- EncLoopbackUser = rabbit_pbe:encrypt_term(C, H, I, P, LoopbackUser),
- application:set_env(rabbit, loopback_users, [{encrypted, EncLoopbackUser}]),
- %% Special case: encrypt a value in a key/value list.
- {ok, TCPOpts} = application:get_env(rabbit, tcp_listen_options),
- {_, Backlog} = lists:keyfind(backlog, 1, TCPOpts),
- {_, Linger} = lists:keyfind(linger, 1, TCPOpts),
- EncBacklog = rabbit_pbe:encrypt_term(C, H, I, P, Backlog),
- EncLinger = rabbit_pbe:encrypt_term(C, H, I, P, Linger),
- TCPOpts1 = lists:keyreplace(backlog, 1, TCPOpts, {backlog, {encrypted, EncBacklog}}),
- TCPOpts2 = lists:keyreplace(linger, 1, TCPOpts1, {linger, {encrypted, EncLinger}}),
- application:set_env(rabbit, tcp_listen_options, TCPOpts2),
- %% Decrypt configuration.
- rabbit_prelaunch_conf:decrypt_config([rabbit], Algo),
- %% Check that configuration was decrypted properly.
- RabbitConfig = application:get_all_env(rabbit),
- ok = application:unload(rabbit),
- ok.
-
-encrypt_value(Key, {C, H, I, P}) ->
- {ok, Value} = application:get_env(rabbit, Key),
- EncValue = rabbit_pbe:encrypt_term(C, H, I, P, Value),
- application:set_env(rabbit, Key, {encrypted, EncValue}).
-
-decrypt_start_app(Config) ->
- do_decrypt_start_app(Config, "hello").
-
-decrypt_start_app_file(Config) ->
- do_decrypt_start_app(Config, {file, ?config(data_dir, Config) ++ "/rabbit_shovel_test.passphrase"}).
-
-do_decrypt_start_app(Config, Passphrase) ->
- %% Configure rabbit for decrypting configuration.
- application:set_env(rabbit, config_entry_decoder, [
- {cipher, aes_cbc256},
- {hash, sha512},
- {iterations, 1000},
- {passphrase, Passphrase}
- ], [{persistent, true}]),
- %% Add the path to our test application.
- code:add_path(?config(data_dir, Config) ++ "/lib/rabbit_shovel_test/ebin"),
- %% Attempt to start our test application.
- %%
- %% We expect a failure *after* the decrypting has been done.
- try
- rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
- catch _:_ ->
- ok
- end,
- %% Check if the values have been decrypted.
- {ok, Shovels} = application:get_env(rabbit_shovel_test, shovels),
- {_, FirstShovel} = lists:keyfind(my_first_shovel, 1, Shovels),
- {_, Sources} = lists:keyfind(sources, 1, FirstShovel),
- {_, Brokers} = lists:keyfind(brokers, 1, Sources),
- ["amqp://fred:secret@host1.domain/my_vhost",
- "amqp://john:secret@host2.domain/my_vhost"] = Brokers,
- ok.
-
-decrypt_start_app_undefined(Config) ->
- %% Configure rabbit for decrypting configuration.
- application:set_env(rabbit, config_entry_decoder, [
- {cipher, aes_cbc256},
- {hash, sha512},
- {iterations, 1000}
- %% No passphrase option!
- ], [{persistent, true}]),
- %% Add the path to our test application.
- code:add_path(?config(data_dir, Config) ++ "/lib/rabbit_shovel_test/ebin"),
- %% Attempt to start our test application.
- %%
- %% We expect a failure during decryption because the passphrase is missing.
- try
- rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
- catch
- throw:{bad_config_entry_decoder, missing_passphrase} -> ok;
- _:Exception -> exit({unexpected_exception, Exception})
- end.
-
-decrypt_start_app_wrong_passphrase(Config) ->
- %% Configure rabbit for decrypting configuration.
- application:set_env(rabbit, config_entry_decoder, [
- {cipher, aes_cbc256},
- {hash, sha512},
- {iterations, 1000},
- {passphrase, "wrong passphrase"}
- ], [{persistent, true}]),
- %% Add the path to our test application.
- code:add_path(?config(data_dir, Config) ++ "/lib/rabbit_shovel_test/ebin"),
- %% Attempt to start our test application.
- %%
- %% We expect a failure during decryption because the passphrase is wrong.
- try
- rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
- catch
- throw:{config_decryption_error, _, _} -> ok;
- _:Exception -> exit({unexpected_exception, Exception})
- end.
-
-rabbitmqctl_encode(_Config) ->
- % list ciphers and hashes
- {ok, _} = rabbit_control_pbe:list_ciphers(),
- {ok, _} = rabbit_control_pbe:list_hashes(),
- % incorrect ciphers, hashes and iteration number
- {error, _} = rabbit_control_pbe:encode(funny_cipher, undefined, undefined, undefined),
- {error, _} = rabbit_control_pbe:encode(undefined, funny_hash, undefined, undefined),
- {error, _} = rabbit_control_pbe:encode(undefined, undefined, -1, undefined),
- {error, _} = rabbit_control_pbe:encode(undefined, undefined, 0, undefined),
- % incorrect number of arguments
- {error, _} = rabbit_control_pbe:encode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- []
- ),
- {error, _} = rabbit_control_pbe:encode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [undefined]
- ),
- {error, _} = rabbit_control_pbe:encode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [undefined, undefined, undefined]
- ),
-
- % encrypt/decrypt
- % string
- rabbitmqctl_encode_encrypt_decrypt("foobar"),
- % binary
- rabbitmqctl_encode_encrypt_decrypt("<<\"foobar\">>"),
- % tuple
- rabbitmqctl_encode_encrypt_decrypt("{password,<<\"secret\">>}"),
-
- ok.
-
-rabbitmqctl_encode_encrypt_decrypt(Secret) ->
- PassPhrase = "passphrase",
- {ok, Output} = rabbit_control_pbe:encode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [Secret, PassPhrase]
- ),
- {encrypted, Encrypted} = rabbit_control_pbe:evaluate_input_as_term(lists:flatten(Output)),
-
- {ok, Result} = rabbit_control_pbe:decode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [lists:flatten(io_lib:format("~p", [Encrypted])), PassPhrase]
- ),
- Secret = lists:flatten(Result),
- % decrypt with {encrypted, ...} form as input
- {ok, Result} = rabbit_control_pbe:decode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [lists:flatten(io_lib:format("~p", [{encrypted, Encrypted}])), PassPhrase]
- ),
-
- % wrong passphrase
- {error, _} = rabbit_control_pbe:decode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [lists:flatten(io_lib:format("~p", [Encrypted])), PassPhrase ++ " "]
- ),
- {error, _} = rabbit_control_pbe:decode(
- rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
- [lists:flatten(io_lib:format("~p", [{encrypted, Encrypted}])), PassPhrase ++ " "]
- )
- .
-
-rabbit_direct_extract_extra_auth_props(_Config) ->
- {ok, CSC} = code_server_cache:start_link(),
- % no protocol to extract
- [] = rabbit_direct:extract_extra_auth_props(
- {<<"guest">>, <<"guest">>}, <<"/">>, 1,
- [{name,<<"127.0.0.1:52366 -> 127.0.0.1:1883">>}]),
- % protocol to extract, but no module to call
- [] = rabbit_direct:extract_extra_auth_props(
- {<<"guest">>, <<"guest">>}, <<"/">>, 1,
- [{protocol, {'PROTOCOL_WITHOUT_MODULE', "1.0"}}]),
- % see rabbit_dummy_protocol_connection_info module
- % protocol to extract, module that returns a client ID
- [{client_id, <<"DummyClientId">>}] = rabbit_direct:extract_extra_auth_props(
- {<<"guest">>, <<"guest">>}, <<"/">>, 1,
- [{protocol, {'DUMMY_PROTOCOL', "1.0"}}]),
- % protocol to extract, but error thrown in module
- [] = rabbit_direct:extract_extra_auth_props(
- {<<"guest">>, <<"guest">>}, <<"/">>, -1,
- [{protocol, {'DUMMY_PROTOCOL', "1.0"}}]),
- gen_server:stop(CSC),
- ok.
-
-%% -------------------------------------------------------------------
-%% pg_local.
-%% -------------------------------------------------------------------
-
-pg_local(_Config) ->
- [P, Q] = [spawn(fun () -> receive X -> X end end) || _ <- lists:seq(0, 1)],
- check_pg_local(ok, [], []),
- %% P joins group a, then b, then a again
- check_pg_local(pg_local:join(a, P), [P], []),
- check_pg_local(pg_local:join(b, P), [P], [P]),
- check_pg_local(pg_local:join(a, P), [P, P], [P]),
- %% Q joins group a, then b, then b again
- check_pg_local(pg_local:join(a, Q), [P, P, Q], [P]),
- check_pg_local(pg_local:join(b, Q), [P, P, Q], [P, Q]),
- check_pg_local(pg_local:join(b, Q), [P, P, Q], [P, Q, Q]),
- %% P leaves groups a and a
- check_pg_local(pg_local:leave(a, P), [P, Q], [P, Q, Q]),
- check_pg_local(pg_local:leave(b, P), [P, Q], [Q, Q]),
- %% leave/2 is idempotent
- check_pg_local(pg_local:leave(a, P), [Q], [Q, Q]),
- check_pg_local(pg_local:leave(a, P), [Q], [Q, Q]),
- %% clean up all processes
- [begin X ! done,
- Ref = erlang:monitor(process, X),
- receive {'DOWN', Ref, process, X, _Info} -> ok end
- end || X <- [P, Q]],
- %% ensure the groups are empty
- check_pg_local(ok, [], []),
- passed.
-
-pg_local_with_unexpected_deaths1(_Config) ->
- [A, B] = [spawn(fun () -> receive X -> X end end) || _ <- lists:seq(0, 1)],
- check_pg_local(ok, [], []),
- %% A joins groups a and b
- check_pg_local(pg_local:join(a, A), [A], []),
- check_pg_local(pg_local:join(b, A), [A], [A]),
- %% B joins group b
- check_pg_local(pg_local:join(b, B), [A], [A, B]),
-
- [begin erlang:exit(X, sleep_now_in_a_fire),
- Ref = erlang:monitor(process, X),
- receive {'DOWN', Ref, process, X, _Info} -> ok end
- end || X <- [A, B]],
- %% ensure the groups are empty
- check_pg_local(ok, [], []),
- ?assertNot(erlang:is_process_alive(A)),
- ?assertNot(erlang:is_process_alive(B)),
-
- passed.
-
-pg_local_with_unexpected_deaths2(_Config) ->
- [A, B] = [spawn(fun () -> receive X -> X end end) || _ <- lists:seq(0, 1)],
- check_pg_local(ok, [], []),
- %% A joins groups a and b
- check_pg_local(pg_local:join(a, A), [A], []),
- check_pg_local(pg_local:join(b, A), [A], [A]),
- %% B joins group b
- check_pg_local(pg_local:join(b, B), [A], [A, B]),
-
- %% something else yanks a record (or all of them) from the pg_local
- %% bookkeeping table
- ok = pg_local:clear(),
-
- [begin erlang:exit(X, sleep_now_in_a_fire),
- Ref = erlang:monitor(process, X),
- receive {'DOWN', Ref, process, X, _Info} -> ok end
- end || X <- [A, B]],
- %% ensure the groups are empty
- check_pg_local(ok, [], []),
- ?assertNot(erlang:is_process_alive(A)),
- ?assertNot(erlang:is_process_alive(B)),
-
- passed.
-
-check_pg_local(ok, APids, BPids) ->
- ok = pg_local:sync(),
- ?assertEqual([true, true], [lists:sort(Pids) == lists:sort(pg_local:get_members(Key)) ||
- {Key, Pids} <- [{a, APids}, {b, BPids}]]).
-
-%% -------------------------------------------------------------------
-%% priority_queue.
-%% -------------------------------------------------------------------
-
-priority_queue(_Config) ->
-
- false = priority_queue:is_queue(not_a_queue),
-
- %% empty Q
- Q = priority_queue:new(),
- {true, true, 0, [], []} = test_priority_queue(Q),
-
- %% 1-4 element no-priority Q
- true = lists:all(fun (X) -> X =:= passed end,
- lists:map(fun test_simple_n_element_queue/1,
- lists:seq(1, 4))),
-
- %% 1-element priority Q
- Q1 = priority_queue:in(foo, 1, priority_queue:new()),
- {true, false, 1, [{1, foo}], [foo]} =
- test_priority_queue(Q1),
-
- %% 2-element same-priority Q
- Q2 = priority_queue:in(bar, 1, Q1),
- {true, false, 2, [{1, foo}, {1, bar}], [foo, bar]} =
- test_priority_queue(Q2),
-
- %% 2-element different-priority Q
- Q3 = priority_queue:in(bar, 2, Q1),
- {true, false, 2, [{2, bar}, {1, foo}], [bar, foo]} =
- test_priority_queue(Q3),
-
- %% 1-element negative priority Q
- Q4 = priority_queue:in(foo, -1, priority_queue:new()),
- {true, false, 1, [{-1, foo}], [foo]} = test_priority_queue(Q4),
-
- %% merge 2 * 1-element no-priority Qs
- Q5 = priority_queue:join(priority_queue:in(foo, Q),
- priority_queue:in(bar, Q)),
- {true, false, 2, [{0, foo}, {0, bar}], [foo, bar]} =
- test_priority_queue(Q5),
-
- %% merge 1-element no-priority Q with 1-element priority Q
- Q6 = priority_queue:join(priority_queue:in(foo, Q),
- priority_queue:in(bar, 1, Q)),
- {true, false, 2, [{1, bar}, {0, foo}], [bar, foo]} =
- test_priority_queue(Q6),
-
- %% merge 1-element priority Q with 1-element no-priority Q
- Q7 = priority_queue:join(priority_queue:in(foo, 1, Q),
- priority_queue:in(bar, Q)),
- {true, false, 2, [{1, foo}, {0, bar}], [foo, bar]} =
- test_priority_queue(Q7),
-
- %% merge 2 * 1-element same-priority Qs
- Q8 = priority_queue:join(priority_queue:in(foo, 1, Q),
- priority_queue:in(bar, 1, Q)),
- {true, false, 2, [{1, foo}, {1, bar}], [foo, bar]} =
- test_priority_queue(Q8),
-
- %% merge 2 * 1-element different-priority Qs
- Q9 = priority_queue:join(priority_queue:in(foo, 1, Q),
- priority_queue:in(bar, 2, Q)),
- {true, false, 2, [{2, bar}, {1, foo}], [bar, foo]} =
- test_priority_queue(Q9),
-
- %% merge 2 * 1-element different-priority Qs (other way around)
- Q10 = priority_queue:join(priority_queue:in(bar, 2, Q),
- priority_queue:in(foo, 1, Q)),
- {true, false, 2, [{2, bar}, {1, foo}], [bar, foo]} =
- test_priority_queue(Q10),
-
- %% merge 2 * 2-element multi-different-priority Qs
- Q11 = priority_queue:join(Q6, Q5),
- {true, false, 4, [{1, bar}, {0, foo}, {0, foo}, {0, bar}],
- [bar, foo, foo, bar]} = test_priority_queue(Q11),
-
- %% and the other way around
- Q12 = priority_queue:join(Q5, Q6),
- {true, false, 4, [{1, bar}, {0, foo}, {0, bar}, {0, foo}],
- [bar, foo, bar, foo]} = test_priority_queue(Q12),
-
- %% merge with negative priorities
- Q13 = priority_queue:join(Q4, Q5),
- {true, false, 3, [{0, foo}, {0, bar}, {-1, foo}], [foo, bar, foo]} =
- test_priority_queue(Q13),
-
- %% and the other way around
- Q14 = priority_queue:join(Q5, Q4),
- {true, false, 3, [{0, foo}, {0, bar}, {-1, foo}], [foo, bar, foo]} =
- test_priority_queue(Q14),
-
- %% joins with empty queues:
- Q1 = priority_queue:join(Q, Q1),
- Q1 = priority_queue:join(Q1, Q),
-
- %% insert with priority into non-empty zero-priority queue
- Q15 = priority_queue:in(baz, 1, Q5),
- {true, false, 3, [{1, baz}, {0, foo}, {0, bar}], [baz, foo, bar]} =
- test_priority_queue(Q15),
-
- %% 1-element infinity priority Q
- Q16 = priority_queue:in(foo, infinity, Q),
- {true, false, 1, [{infinity, foo}], [foo]} = test_priority_queue(Q16),
-
- %% add infinity to 0-priority Q
- Q17 = priority_queue:in(foo, infinity, priority_queue:in(bar, Q)),
- {true, false, 2, [{infinity, foo}, {0, bar}], [foo, bar]} =
- test_priority_queue(Q17),
-
- %% and the other way around
- Q18 = priority_queue:in(bar, priority_queue:in(foo, infinity, Q)),
- {true, false, 2, [{infinity, foo}, {0, bar}], [foo, bar]} =
- test_priority_queue(Q18),
-
- %% add infinity to mixed-priority Q
- Q19 = priority_queue:in(qux, infinity, Q3),
- {true, false, 3, [{infinity, qux}, {2, bar}, {1, foo}], [qux, bar, foo]} =
- test_priority_queue(Q19),
-
- %% merge the above with a negative priority Q
- Q20 = priority_queue:join(Q19, Q4),
- {true, false, 4, [{infinity, qux}, {2, bar}, {1, foo}, {-1, foo}],
- [qux, bar, foo, foo]} = test_priority_queue(Q20),
-
- %% merge two infinity priority queues
- Q21 = priority_queue:join(priority_queue:in(foo, infinity, Q),
- priority_queue:in(bar, infinity, Q)),
- {true, false, 2, [{infinity, foo}, {infinity, bar}], [foo, bar]} =
- test_priority_queue(Q21),
-
- %% merge two mixed priority with infinity queues
- Q22 = priority_queue:join(Q18, Q20),
- {true, false, 6, [{infinity, foo}, {infinity, qux}, {2, bar}, {1, foo},
- {0, bar}, {-1, foo}], [foo, qux, bar, foo, bar, foo]} =
- test_priority_queue(Q22),
-
- passed.
-
-priority_queue_in_all(Q, L) ->
- lists:foldl(fun (X, Acc) -> priority_queue:in(X, Acc) end, Q, L).
-
-priority_queue_out_all(Q) ->
- case priority_queue:out(Q) of
- {empty, _} -> [];
- {{value, V}, Q1} -> [V | priority_queue_out_all(Q1)]
- end.
-
-test_priority_queue(Q) ->
- {priority_queue:is_queue(Q),
- priority_queue:is_empty(Q),
- priority_queue:len(Q),
- priority_queue:to_list(Q),
- priority_queue_out_all(Q)}.
-
-test_simple_n_element_queue(N) ->
- Items = lists:seq(1, N),
- Q = priority_queue_in_all(priority_queue:new(), Items),
- ToListRes = [{0, X} || X <- Items],
- {true, false, N, ToListRes, Items} = test_priority_queue(Q),
- passed.
-
-
-%% ---------------------------------------------------------------------------
-%% supervisor2.
-%% ---------------------------------------------------------------------------
-
-check_shutdown_stop(_Config) ->
- ok = check_shutdown(stop, 200, 200, 2000).
-
-check_shutdown_ignored(_Config) ->
- ok = check_shutdown(ignored, 1, 2, 2000).
-
-check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) ->
- {ok, Sup} = supervisor2:start_link(dummy_supervisor2, [SupTimeout]),
- Res = lists:foldl(
- fun (I, ok) ->
- TestSupPid = erlang:whereis(dummy_supervisor2),
- ChildPids =
- [begin
- {ok, ChildPid} =
- supervisor2:start_child(TestSupPid, []),
- ChildPid
- end || _ <- lists:seq(1, ChildCount)],
- MRef = erlang:monitor(process, TestSupPid),
- [P ! SigStop || P <- ChildPids],
- ok = supervisor2:terminate_child(Sup, test_sup),
- {ok, _} = supervisor2:restart_child(Sup, test_sup),
- receive
- {'DOWN', MRef, process, TestSupPid, shutdown} ->
- ok;
- {'DOWN', MRef, process, TestSupPid, Reason} ->
- {error, {I, Reason}}
- end;
- (_, R) ->
- R
- end, ok, lists:seq(1, Iterations)),
- unlink(Sup),
- MSupRef = erlang:monitor(process, Sup),
- exit(Sup, shutdown),
- receive
- {'DOWN', MSupRef, process, Sup, _Reason} ->
- ok
- end,
- Res.
-
-%% ---------------------------------------------------------------------------
-%% vm_memory_monitor.
-%% ---------------------------------------------------------------------------
-
-parse_line_linux(_Config) ->
- lists:foreach(fun ({S, {K, V}}) ->
- {K, V} = vm_memory_monitor:parse_line_linux(S)
- end,
- [{"MemTotal: 0 kB", {'MemTotal', 0}},
- {"MemTotal: 502968 kB ", {'MemTotal', 515039232}},
- {"MemFree: 178232 kB", {'MemFree', 182509568}},
- {"MemTotal: 50296888", {'MemTotal', 50296888}},
- {"MemTotal 502968 kB", {'MemTotal', 515039232}},
- {"MemTotal 50296866 ", {'MemTotal', 50296866}}]),
- ok.
-
-%% ---------------------------------------------------------------------------
-%% Unordered tests (originally from rabbit_tests.erl).
-%% ---------------------------------------------------------------------------
-
-%% Test that content frames don't exceed frame-max
-content_framing(_Config) ->
- %% no content
- passed = test_content_framing(4096, <<>>),
- %% easily fit in one frame
- passed = test_content_framing(4096, <<"Easy">>),
- %% exactly one frame (empty frame = 8 bytes)
- passed = test_content_framing(11, <<"One">>),
- %% more than one frame
- passed = test_content_framing(11, <<"More than one frame">>),
- passed.
-
-test_content_framing(FrameMax, BodyBin) ->
- [Header | Frames] =
- rabbit_binary_generator:build_simple_content_frames(
- 1,
- rabbit_binary_generator:ensure_content_encoded(
- rabbit_basic:build_content(#'P_basic'{}, BodyBin),
- rabbit_framing_amqp_0_9_1),
- FrameMax,
- rabbit_framing_amqp_0_9_1),
- %% header is formatted correctly and the size is the total of the
- %% fragments
- <<_FrameHeader:7/binary, _ClassAndWeight:4/binary,
- BodySize:64/unsigned, _Rest/binary>> = list_to_binary(Header),
- BodySize = size(BodyBin),
- true = lists:all(
- fun (ContentFrame) ->
- FrameBinary = list_to_binary(ContentFrame),
- %% assert
- <<_TypeAndChannel:3/binary,
- Size:32/unsigned, _Payload:Size/binary, 16#CE>> =
- FrameBinary,
- size(FrameBinary) =< FrameMax
- end, Frames),
- passed.
-
-content_transcoding(_Config) ->
- %% there are no guarantees provided by 'clear' - it's just a hint
- ClearDecoded = fun rabbit_binary_parser:clear_decoded_content/1,
- ClearEncoded = fun rabbit_binary_generator:clear_encoded_content/1,
- EnsureDecoded =
- fun (C0) ->
- C1 = rabbit_binary_parser:ensure_content_decoded(C0),
- true = C1#content.properties =/= none,
- C1
- end,
- EnsureEncoded =
- fun (Protocol) ->
- fun (C0) ->
- C1 = rabbit_binary_generator:ensure_content_encoded(
- C0, Protocol),
- true = C1#content.properties_bin =/= none,
- C1
- end
- end,
- %% Beyond the assertions in Ensure*, the only testable guarantee
- %% is that the operations should never fail.
- %%
- %% If we were using quickcheck we'd simply stuff all the above
- %% into a generator for sequences of operations. In the absence of
- %% quickcheck we pick particularly interesting sequences that:
- %%
- %% - execute every op twice since they are idempotent
- %% - invoke clear_decoded, clear_encoded, decode and transcode
- %% with one or both of decoded and encoded content present
- [begin
- sequence_with_content([Op]),
- sequence_with_content([ClearEncoded, Op]),
- sequence_with_content([ClearDecoded, Op])
- end || Op <- [ClearDecoded, ClearEncoded, EnsureDecoded,
- EnsureEncoded(rabbit_framing_amqp_0_9_1),
- EnsureEncoded(rabbit_framing_amqp_0_8)]],
- passed.
-
-sequence_with_content(Sequence) ->
- lists:foldl(fun (F, V) -> F(F(V)) end,
- rabbit_binary_generator:ensure_content_encoded(
- rabbit_basic:build_content(#'P_basic'{}, <<>>),
- rabbit_framing_amqp_0_9_1),
- Sequence).
-
-pmerge(_Config) ->
- P = [{a, 1}, {b, 2}],
- P = rabbit_misc:pmerge(a, 3, P),
- [{c, 3} | P] = rabbit_misc:pmerge(c, 3, P),
- passed.
-
-plmerge(_Config) ->
- P1 = [{a, 1}, {b, 2}, {c, 3}],
- P2 = [{a, 2}, {d, 4}],
- [{a, 1}, {b, 2}, {c, 3}, {d, 4}] = rabbit_misc:plmerge(P1, P2),
- passed.
-
-merge_operator_policy_definitions(_Config) ->
- P1 = undefined,
- P2 = [{definition, [{<<"message-ttl">>, 3000}]}],
- ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(P1, P2)),
- ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(P2, P1)),
-
- ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(P1, rabbit_data_coercion:to_map(P2))),
- ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(rabbit_data_coercion:to_map(P2), P1)),
-
- ?assertEqual(undefined, rabbit_policy:merge_operator_definitions(undefined, undefined)),
-
- ?assertEqual([], rabbit_policy:merge_operator_definitions([], [])),
- ?assertEqual([], rabbit_policy:merge_operator_definitions(#{}, [])),
- ?assertEqual([], rabbit_policy:merge_operator_definitions(#{}, #{})),
- ?assertEqual([], rabbit_policy:merge_operator_definitions([], #{})),
-
- %% operator policy takes precedence
- ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(
- [{definition, [
- {<<"message-ttl">>, 5000}
- ]}],
- [{definition, [
- {<<"message-ttl">>, 3000}
- ]}]
- )),
-
- ?assertEqual([{<<"delivery-limit">>, 20},
- {<<"message-ttl">>, 3000}],
- rabbit_policy:merge_operator_definitions(
- [{definition, [
- {<<"message-ttl">>, 5000},
- {<<"delivery-limit">>, 20}
- ]}],
- [{definition, [
- {<<"message-ttl">>, 3000}
- ]}])
- ),
-
- ?assertEqual(
- [{<<"delivery-limit">>, 20},
- {<<"message-ttl">>, 3000},
- {<<"unknown">>, <<"value">>}],
-
- rabbit_policy:merge_operator_definitions(
- #{definition => #{
- <<"message-ttl">> => 5000,
- <<"delivery-limit">> => 20
- }},
- #{definition => #{
- <<"message-ttl">> => 3000,
- <<"unknown">> => <<"value">>
- }})
- ),
-
- ?assertEqual(
- [{<<"delivery-limit">>, 20},
- {<<"message-ttl">>, 3000}],
-
- rabbit_policy:merge_operator_definitions(
- #{definition => #{
- <<"message-ttl">> => 5000,
- <<"delivery-limit">> => 20
- }},
- [{definition, [
- {<<"message-ttl">>, 3000}
- ]}])
- ),
-
- passed.
-
-table_codec(_Config) ->
- %% FIXME this does not test inexact numbers (double and float) yet,
- %% because they won't pass the equality assertions
- Table = [{<<"longstr">>, longstr, <<"Here is a long string">>},
- {<<"signedint">>, signedint, 12345},
- {<<"decimal">>, decimal, {3, 123456}},
- {<<"timestamp">>, timestamp, 109876543209876},
- {<<"table">>, table, [{<<"one">>, signedint, 54321},
- {<<"two">>, longstr,
- <<"A long string">>}]},
- {<<"byte">>, byte, -128},
- {<<"long">>, long, 1234567890},
- {<<"short">>, short, 655},
- {<<"bool">>, bool, true},
- {<<"binary">>, binary, <<"a binary string">>},
- {<<"unsignedbyte">>, unsignedbyte, 250},
- {<<"unsignedshort">>, unsignedshort, 65530},
- {<<"unsignedint">>, unsignedint, 4294967290},
- {<<"void">>, void, undefined},
- {<<"array">>, array, [{signedint, 54321},
- {longstr, <<"A long string">>}]}
- ],
- Binary = <<
- 7,"longstr", "S", 21:32, "Here is a long string",
- 9,"signedint", "I", 12345:32/signed,
- 7,"decimal", "D", 3, 123456:32,
- 9,"timestamp", "T", 109876543209876:64,
- 5,"table", "F", 31:32, % length of table
- 3,"one", "I", 54321:32,
- 3,"two", "S", 13:32, "A long string",
- 4,"byte", "b", -128:8/signed,
- 4,"long", "l", 1234567890:64,
- 5,"short", "s", 655:16,
- 4,"bool", "t", 1,
- 6,"binary", "x", 15:32, "a binary string",
- 12,"unsignedbyte", "B", 250:8/unsigned,
- 13,"unsignedshort", "u", 65530:16/unsigned,
- 11,"unsignedint", "i", 4294967290:32/unsigned,
- 4,"void", "V",
- 5,"array", "A", 23:32,
- "I", 54321:32,
- "S", 13:32, "A long string"
- >>,
- Binary = rabbit_binary_generator:generate_table(Table),
- Table = rabbit_binary_parser:parse_table(Binary),
- passed.
-
-unfold(_Config) ->
- {[], test} = rabbit_misc:unfold(fun (_V) -> false end, test),
- List = lists:seq(2,20,2),
- {List, 0} = rabbit_misc:unfold(fun (0) -> false;
- (N) -> {true, N*2, N-1}
- end, 10),
- passed.
-
-listing_plugins_from_multiple_directories(Config) ->
- %% Generate some fake plugins in .ez files
- FirstDir = filename:join([?config(priv_dir, Config), "listing_plugins_from_multiple_directories-1"]),
- SecondDir = filename:join([?config(priv_dir, Config), "listing_plugins_from_multiple_directories-2"]),
- ok = file:make_dir(FirstDir),
- ok = file:make_dir(SecondDir),
- lists:foreach(fun({Dir, AppName, Vsn}) ->
- EzName = filename:join([Dir, io_lib:format("~s-~s.ez", [AppName, Vsn])]),
- AppFileName = lists:flatten(io_lib:format("~s-~s/ebin/~s.app", [AppName, Vsn, AppName])),
- AppFileContents = list_to_binary(
- io_lib:format(
- "~p.",
- [{application, AppName,
- [{vsn, Vsn},
- {applications, [kernel, stdlib, rabbit]}]}])),
- {ok, {_, EzData}} = zip:zip(EzName, [{AppFileName, AppFileContents}], [memory]),
- ok = file:write_file(EzName, EzData)
- end,
- [{FirstDir, plugin_first_dir, "3"},
- {SecondDir, plugin_second_dir, "4"},
- {FirstDir, plugin_both, "1"},
- {SecondDir, plugin_both, "2"}]),
-
- %% Everything was collected from both directories, plugin with higher
- %% version should take precedence
- PathSep = case os:type() of
- {win32, _} -> ";";
- _ -> ":"
- end,
- Path = FirstDir ++ PathSep ++ SecondDir,
- Got = lists:sort([{Name, Vsn} || #plugin{name = Name, version = Vsn} <- rabbit_plugins:list(Path)]),
- %% `rabbit` was loaded automatically by `rabbit_plugins:list/1`.
- %% We want to unload it now so it does not interfere with other
- %% testcases.
- application:unload(rabbit),
- Expected = [{plugin_both, "2"}, {plugin_first_dir, "3"}, {plugin_second_dir, "4"}],
- case Got of
- Expected ->
- ok;
- _ ->
- ct:pal("Got ~p~nExpected: ~p", [Got, Expected]),
- exit({wrong_plugins_list, Got})
- end,
- ok.
-
-%%
-%% Access Control
-%%
-
-auth_backend_internal_expand_topic_permission(_Config) ->
- ExpandMap = #{<<"username">> => <<"guest">>, <<"vhost">> => <<"default">>},
- %% simple case
- <<"services/default/accounts/guest/notifications">> =
- rabbit_auth_backend_internal:expand_topic_permission(
- <<"services/{vhost}/accounts/{username}/notifications">>,
- ExpandMap
- ),
- %% replace variable twice
- <<"services/default/accounts/default/guest/notifications">> =
- rabbit_auth_backend_internal:expand_topic_permission(
- <<"services/{vhost}/accounts/{vhost}/{username}/notifications">>,
- ExpandMap
- ),
- %% nothing to replace
- <<"services/accounts/notifications">> =
- rabbit_auth_backend_internal:expand_topic_permission(
- <<"services/accounts/notifications">>,
- ExpandMap
- ),
- %% the expand map isn't defined
- <<"services/{vhost}/accounts/{username}/notifications">> =
- rabbit_auth_backend_internal:expand_topic_permission(
- <<"services/{vhost}/accounts/{username}/notifications">>,
- undefined
- ),
- %% the expand map is empty
- <<"services/{vhost}/accounts/{username}/notifications">> =
- rabbit_auth_backend_internal:expand_topic_permission(
- <<"services/{vhost}/accounts/{username}/notifications">>,
- #{}
- ),
- ok.
diff --git a/test/unit_amqp091_content_framing_SUITE.erl b/test/unit_amqp091_content_framing_SUITE.erl
new file mode 100644
index 0000000000..9400df672a
--- /dev/null
+++ b/test/unit_amqp091_content_framing_SUITE.erl
@@ -0,0 +1,240 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_amqp091_content_framing_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("rabbit_common/include/rabbit.hrl").
+-include_lib("rabbit_common/include/rabbit_framing.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, parallel_tests}
+ ].
+
+groups() ->
+ [
+ {parallel_tests, [parallel], [
+ write_table_with_invalid_existing_type,
+ invalid_existing_headers,
+ disparate_invalid_header_entries_accumulate_separately,
+ corrupt_or_invalid_headers_are_overwritten,
+ invalid_same_header_entry_accumulation,
+ content_framing,
+ content_transcoding,
+ table_codec
+ ]}
+ ].
+
+%% -------------------------------------------------------------------
+%% Test Cases
+%% -------------------------------------------------------------------
+
+-define(XDEATH_TABLE,
+ [{<<"reason">>, longstr, <<"blah">>},
+ {<<"queue">>, longstr, <<"foo.bar.baz">>},
+ {<<"exchange">>, longstr, <<"my-exchange">>},
+ {<<"routing-keys">>, array, []}]).
+
+-define(ROUTE_TABLE, [{<<"redelivered">>, bool, <<"true">>}]).
+
+-define(BAD_HEADER(K), {<<K>>, longstr, <<"bad ", K>>}).
+-define(BAD_HEADER2(K, Suf), {<<K>>, longstr, <<"bad ", K, Suf>>}).
+-define(FOUND_BAD_HEADER(K), {<<K>>, array, [{longstr, <<"bad ", K>>}]}).
+
+write_table_with_invalid_existing_type(_Config) ->
+ prepend_check(<<"header1">>, ?XDEATH_TABLE, [?BAD_HEADER("header1")]).
+
+invalid_existing_headers(_Config) ->
+ Headers =
+ prepend_check(<<"header2">>, ?ROUTE_TABLE, [?BAD_HEADER("header2")]),
+ {array, [{table, ?ROUTE_TABLE}]} =
+ rabbit_misc:table_lookup(Headers, <<"header2">>),
+ passed.
+
+disparate_invalid_header_entries_accumulate_separately(_Config) ->
+ BadHeaders = [?BAD_HEADER("header2")],
+ Headers = prepend_check(<<"header2">>, ?ROUTE_TABLE, BadHeaders),
+ Headers2 = prepend_check(<<"header1">>, ?XDEATH_TABLE,
+ [?BAD_HEADER("header1") | Headers]),
+ {table, [?FOUND_BAD_HEADER("header1"),
+ ?FOUND_BAD_HEADER("header2")]} =
+ rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY),
+ passed.
+
+corrupt_or_invalid_headers_are_overwritten(_Config) ->
+ Headers0 = [?BAD_HEADER("header1"),
+ ?BAD_HEADER("x-invalid-headers")],
+ Headers1 = prepend_check(<<"header1">>, ?XDEATH_TABLE, Headers0),
+ {table,[?FOUND_BAD_HEADER("header1"),
+ ?FOUND_BAD_HEADER("x-invalid-headers")]} =
+ rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY),
+ passed.
+
+invalid_same_header_entry_accumulation(_Config) ->
+ BadHeader1 = ?BAD_HEADER2("header1", "a"),
+ Headers = prepend_check(<<"header1">>, ?ROUTE_TABLE, [BadHeader1]),
+ Headers2 = prepend_check(<<"header1">>, ?ROUTE_TABLE,
+ [?BAD_HEADER2("header1", "b") | Headers]),
+ {table, InvalidHeaders} =
+ rabbit_misc:table_lookup(Headers2, ?INVALID_HEADERS_KEY),
+ {array, [{longstr,<<"bad header1b">>},
+ {longstr,<<"bad header1a">>}]} =
+ rabbit_misc:table_lookup(InvalidHeaders, <<"header1">>),
+ passed.
+
+prepend_check(HeaderKey, HeaderTable, Headers) ->
+ Headers1 = rabbit_basic:prepend_table_header(
+ HeaderKey, HeaderTable, Headers),
+ {table, Invalid} =
+ rabbit_misc:table_lookup(Headers1, ?INVALID_HEADERS_KEY),
+ {Type, Value} = rabbit_misc:table_lookup(Headers, HeaderKey),
+ {array, [{Type, Value} | _]} =
+ rabbit_misc:table_lookup(Invalid, HeaderKey),
+ Headers1.
+
+
+%% Test that content frames don't exceed frame-max
+content_framing(_Config) ->
+ %% no content
+ passed = test_content_framing(4096, <<>>),
+ %% easily fit in one frame
+ passed = test_content_framing(4096, <<"Easy">>),
+ %% exactly one frame (empty frame = 8 bytes)
+ passed = test_content_framing(11, <<"One">>),
+ %% more than one frame
+ passed = test_content_framing(11, <<"More than one frame">>),
+ passed.
+
+test_content_framing(FrameMax, BodyBin) ->
+ [Header | Frames] =
+ rabbit_binary_generator:build_simple_content_frames(
+ 1,
+ rabbit_binary_generator:ensure_content_encoded(
+ rabbit_basic:build_content(#'P_basic'{}, BodyBin),
+ rabbit_framing_amqp_0_9_1),
+ FrameMax,
+ rabbit_framing_amqp_0_9_1),
+ %% header is formatted correctly and the size is the total of the
+ %% fragments
+ <<_FrameHeader:7/binary, _ClassAndWeight:4/binary,
+ BodySize:64/unsigned, _Rest/binary>> = list_to_binary(Header),
+ BodySize = size(BodyBin),
+ true = lists:all(
+ fun (ContentFrame) ->
+ FrameBinary = list_to_binary(ContentFrame),
+ %% assert
+ <<_TypeAndChannel:3/binary,
+ Size:32/unsigned, _Payload:Size/binary, 16#CE>> =
+ FrameBinary,
+ size(FrameBinary) =< FrameMax
+ end, Frames),
+ passed.
+
+content_transcoding(_Config) ->
+ %% there are no guarantees provided by 'clear' - it's just a hint
+ ClearDecoded = fun rabbit_binary_parser:clear_decoded_content/1,
+ ClearEncoded = fun rabbit_binary_generator:clear_encoded_content/1,
+ EnsureDecoded =
+ fun (C0) ->
+ C1 = rabbit_binary_parser:ensure_content_decoded(C0),
+ true = C1#content.properties =/= none,
+ C1
+ end,
+ EnsureEncoded =
+ fun (Protocol) ->
+ fun (C0) ->
+ C1 = rabbit_binary_generator:ensure_content_encoded(
+ C0, Protocol),
+ true = C1#content.properties_bin =/= none,
+ C1
+ end
+ end,
+ %% Beyond the assertions in Ensure*, the only testable guarantee
+ %% is that the operations should never fail.
+ %%
+ %% If we were using quickcheck we'd simply stuff all the above
+ %% into a generator for sequences of operations. In the absence of
+ %% quickcheck we pick particularly interesting sequences that:
+ %%
+ %% - execute every op twice since they are idempotent
+ %% - invoke clear_decoded, clear_encoded, decode and transcode
+ %% with one or both of decoded and encoded content present
+ [begin
+ sequence_with_content([Op]),
+ sequence_with_content([ClearEncoded, Op]),
+ sequence_with_content([ClearDecoded, Op])
+ end || Op <- [ClearDecoded, ClearEncoded, EnsureDecoded,
+ EnsureEncoded(rabbit_framing_amqp_0_9_1),
+ EnsureEncoded(rabbit_framing_amqp_0_8)]],
+ passed.
+
+sequence_with_content(Sequence) ->
+ lists:foldl(fun (F, V) -> F(F(V)) end,
+ rabbit_binary_generator:ensure_content_encoded(
+ rabbit_basic:build_content(#'P_basic'{}, <<>>),
+ rabbit_framing_amqp_0_9_1),
+ Sequence).
+
+table_codec(_Config) ->
+ %% Note: this does not test inexact numbers (double and float) at the moment.
+ %% They won't pass the equality assertions.
+ Table = [{<<"longstr">>, longstr, <<"Here is a long string">>},
+ {<<"signedint">>, signedint, 12345},
+ {<<"decimal">>, decimal, {3, 123456}},
+ {<<"timestamp">>, timestamp, 109876543209876},
+ {<<"table">>, table, [{<<"one">>, signedint, 54321},
+ {<<"two">>, longstr,
+ <<"A long string">>}]},
+ {<<"byte">>, byte, -128},
+ {<<"long">>, long, 1234567890},
+ {<<"short">>, short, 655},
+ {<<"bool">>, bool, true},
+ {<<"binary">>, binary, <<"a binary string">>},
+ {<<"unsignedbyte">>, unsignedbyte, 250},
+ {<<"unsignedshort">>, unsignedshort, 65530},
+ {<<"unsignedint">>, unsignedint, 4294967290},
+ {<<"void">>, void, undefined},
+ {<<"array">>, array, [{signedint, 54321},
+ {longstr, <<"A long string">>}]}
+ ],
+ Binary = <<
+ 7,"longstr", "S", 21:32, "Here is a long string",
+ 9,"signedint", "I", 12345:32/signed,
+ 7,"decimal", "D", 3, 123456:32,
+ 9,"timestamp", "T", 109876543209876:64,
+ 5,"table", "F", 31:32, % length of table
+ 3,"one", "I", 54321:32,
+ 3,"two", "S", 13:32, "A long string",
+ 4,"byte", "b", -128:8/signed,
+ 4,"long", "l", 1234567890:64,
+ 5,"short", "s", 655:16,
+ 4,"bool", "t", 1,
+ 6,"binary", "x", 15:32, "a binary string",
+ 12,"unsignedbyte", "B", 250:8/unsigned,
+ 13,"unsignedshort", "u", 65530:16/unsigned,
+ 11,"unsignedint", "i", 4294967290:32/unsigned,
+ 4,"void", "V",
+ 5,"array", "A", 23:32,
+ "I", 54321:32,
+ "S", 13:32, "A long string"
+ >>,
+ Binary = rabbit_binary_generator:generate_table(Table),
+ Table = rabbit_binary_parser:parse_table(Binary),
+ passed.
diff --git a/test/unit_collections_SUITE.erl b/test/unit_collections_SUITE.erl
new file mode 100644
index 0000000000..d07e3469d1
--- /dev/null
+++ b/test/unit_collections_SUITE.erl
@@ -0,0 +1,60 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_collections_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, parallel_tests}
+ ].
+
+groups() ->
+ [
+ {parallel_tests, [parallel], [
+ pmerge,
+ plmerge,
+ unfold
+ ]}
+ ].
+
+%% -------------------------------------------------------------------
+%% Test Cases
+%% -------------------------------------------------------------------
+
+pmerge(_Config) ->
+ P = [{a, 1}, {b, 2}],
+ P = rabbit_misc:pmerge(a, 3, P),
+ [{c, 3} | P] = rabbit_misc:pmerge(c, 3, P),
+ passed.
+
+plmerge(_Config) ->
+ P1 = [{a, 1}, {b, 2}, {c, 3}],
+ P2 = [{a, 2}, {d, 4}],
+ [{a, 1}, {b, 2}, {c, 3}, {d, 4}] = rabbit_misc:plmerge(P1, P2),
+ passed.
+
+unfold(_Config) ->
+ {[], test} = rabbit_misc:unfold(fun (_V) -> false end, test),
+ List = lists:seq(2,20,2),
+ {List, 0} = rabbit_misc:unfold(fun (0) -> false;
+ (N) -> {true, N*2, N-1}
+ end, 10),
+ passed.
diff --git a/test/unit_config_value_encryption_SUITE.erl b/test/unit_config_value_encryption_SUITE.erl
new file mode 100644
index 0000000000..1d808c4993
--- /dev/null
+++ b/test/unit_config_value_encryption_SUITE.erl
@@ -0,0 +1,242 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_config_value_encryption_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, sequential_tests}
+ ].
+
+groups() ->
+ [
+ {sequential_tests, [], [
+ decrypt_start_app,
+ decrypt_start_app_file,
+ decrypt_start_app_undefined,
+ decrypt_start_app_wrong_passphrase,
+ decrypt_config,
+ rabbitmqctl_encode
+ ]}
+ ].
+
+init_per_testcase(TC, Config) when TC =:= decrypt_start_app;
+ TC =:= decrypt_start_app_file;
+ TC =:= decrypt_start_app_undefined;
+ TC =:= decrypt_start_app_wrong_passphrase ->
+ application:set_env(rabbit, feature_flags_file, "", [{persistent, true}]),
+ Config;
+init_per_testcase(_Testcase, Config) ->
+ Config.
+
+end_per_testcase(_TC, _Config) ->
+ ok.
+
+%% -------------------------------------------------------------------
+%% Test Cases
+%% -------------------------------------------------------------------
+
+decrypt_config(_Config) ->
+ %% Take all available block ciphers.
+ Hashes = rabbit_pbe:supported_hashes(),
+ Ciphers = rabbit_pbe:supported_ciphers(),
+ Iterations = [1, 10, 100, 1000],
+ %% Loop through all hashes, ciphers and iterations.
+ _ = [begin
+ PassPhrase = crypto:strong_rand_bytes(16),
+ do_decrypt_config({C, H, I, PassPhrase})
+ end || H <- Hashes, C <- Ciphers, I <- Iterations],
+ ok.
+
+do_decrypt_config(Algo = {C, H, I, P}) ->
+ ok = application:load(rabbit),
+ RabbitConfig = application:get_all_env(rabbit),
+ %% Encrypt a few values in configuration.
+ %% Common cases.
+ _ = [encrypt_value(Key, Algo) || Key <- [
+ tcp_listeners,
+ num_tcp_acceptors,
+ ssl_options,
+ vm_memory_high_watermark,
+ default_pass,
+ default_permissions,
+ cluster_nodes,
+ auth_mechanisms,
+ msg_store_credit_disc_bound]],
+ %% Special case: encrypt a value in a list.
+ {ok, [LoopbackUser]} = application:get_env(rabbit, loopback_users),
+ EncLoopbackUser = rabbit_pbe:encrypt_term(C, H, I, P, LoopbackUser),
+ application:set_env(rabbit, loopback_users, [{encrypted, EncLoopbackUser}]),
+ %% Special case: encrypt a value in a key/value list.
+ {ok, TCPOpts} = application:get_env(rabbit, tcp_listen_options),
+ {_, Backlog} = lists:keyfind(backlog, 1, TCPOpts),
+ {_, Linger} = lists:keyfind(linger, 1, TCPOpts),
+ EncBacklog = rabbit_pbe:encrypt_term(C, H, I, P, Backlog),
+ EncLinger = rabbit_pbe:encrypt_term(C, H, I, P, Linger),
+ TCPOpts1 = lists:keyreplace(backlog, 1, TCPOpts, {backlog, {encrypted, EncBacklog}}),
+ TCPOpts2 = lists:keyreplace(linger, 1, TCPOpts1, {linger, {encrypted, EncLinger}}),
+ application:set_env(rabbit, tcp_listen_options, TCPOpts2),
+ %% Decrypt configuration.
+ rabbit_prelaunch_conf:decrypt_config([rabbit], Algo),
+ %% Check that configuration was decrypted properly.
+ RabbitConfig = application:get_all_env(rabbit),
+ ok = application:unload(rabbit),
+ ok.
+
+encrypt_value(Key, {C, H, I, P}) ->
+ {ok, Value} = application:get_env(rabbit, Key),
+ EncValue = rabbit_pbe:encrypt_term(C, H, I, P, Value),
+ application:set_env(rabbit, Key, {encrypted, EncValue}).
+
+decrypt_start_app(Config) ->
+ do_decrypt_start_app(Config, "hello").
+
+decrypt_start_app_file(Config) ->
+ do_decrypt_start_app(Config, {file, ?config(data_dir, Config) ++ "/rabbit_shovel_test.passphrase"}).
+
+do_decrypt_start_app(Config, Passphrase) ->
+ %% Configure rabbit for decrypting configuration.
+ application:set_env(rabbit, config_entry_decoder, [
+ {cipher, aes_cbc256},
+ {hash, sha512},
+ {iterations, 1000},
+ {passphrase, Passphrase}
+ ], [{persistent, true}]),
+ %% Add the path to our test application.
+ code:add_path(?config(data_dir, Config) ++ "/lib/rabbit_shovel_test/ebin"),
+ %% Attempt to start our test application.
+ %%
+ %% We expect a failure *after* the decrypting has been done.
+ try
+ rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
+ catch _:_ ->
+ ok
+ end,
+ %% Check if the values have been decrypted.
+ {ok, Shovels} = application:get_env(rabbit_shovel_test, shovels),
+ {_, FirstShovel} = lists:keyfind(my_first_shovel, 1, Shovels),
+ {_, Sources} = lists:keyfind(sources, 1, FirstShovel),
+ {_, Brokers} = lists:keyfind(brokers, 1, Sources),
+ ["amqp://fred:secret@host1.domain/my_vhost",
+ "amqp://john:secret@host2.domain/my_vhost"] = Brokers,
+ ok.
+
+decrypt_start_app_undefined(Config) ->
+ %% Configure rabbit for decrypting configuration.
+ application:set_env(rabbit, config_entry_decoder, [
+ {cipher, aes_cbc256},
+ {hash, sha512},
+ {iterations, 1000}
+ %% No passphrase option!
+ ], [{persistent, true}]),
+ %% Add the path to our test application.
+ code:add_path(?config(data_dir, Config) ++ "/lib/rabbit_shovel_test/ebin"),
+ %% Attempt to start our test application.
+ %%
+ %% We expect a failure during decryption because the passphrase is missing.
+ try
+ rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
+ catch
+ throw:{bad_config_entry_decoder, missing_passphrase} -> ok;
+ _:Exception -> exit({unexpected_exception, Exception})
+ end.
+
+decrypt_start_app_wrong_passphrase(Config) ->
+ %% Configure rabbit for decrypting configuration.
+ application:set_env(rabbit, config_entry_decoder, [
+ {cipher, aes_cbc256},
+ {hash, sha512},
+ {iterations, 1000},
+ {passphrase, "wrong passphrase"}
+ ], [{persistent, true}]),
+ %% Add the path to our test application.
+ code:add_path(?config(data_dir, Config) ++ "/lib/rabbit_shovel_test/ebin"),
+ %% Attempt to start our test application.
+ %%
+ %% We expect a failure during decryption because the passphrase is wrong.
+ try
+ rabbit:start_apps([rabbit_shovel_test], #{rabbit => temporary})
+ catch
+ throw:{config_decryption_error, _, _} -> ok;
+ _:Exception -> exit({unexpected_exception, Exception})
+ end.
+
+rabbitmqctl_encode(_Config) ->
+ % list ciphers and hashes
+ {ok, _} = rabbit_control_pbe:list_ciphers(),
+ {ok, _} = rabbit_control_pbe:list_hashes(),
+ % incorrect ciphers, hashes and iteration number
+ {error, _} = rabbit_control_pbe:encode(funny_cipher, undefined, undefined, undefined),
+ {error, _} = rabbit_control_pbe:encode(undefined, funny_hash, undefined, undefined),
+ {error, _} = rabbit_control_pbe:encode(undefined, undefined, -1, undefined),
+ {error, _} = rabbit_control_pbe:encode(undefined, undefined, 0, undefined),
+ % incorrect number of arguments
+ {error, _} = rabbit_control_pbe:encode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ []
+ ),
+ {error, _} = rabbit_control_pbe:encode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [undefined]
+ ),
+ {error, _} = rabbit_control_pbe:encode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [undefined, undefined, undefined]
+ ),
+
+ % encrypt/decrypt
+ % string
+ rabbitmqctl_encode_encrypt_decrypt("foobar"),
+ % binary
+ rabbitmqctl_encode_encrypt_decrypt("<<\"foobar\">>"),
+ % tuple
+ rabbitmqctl_encode_encrypt_decrypt("{password,<<\"secret\">>}"),
+
+ ok.
+
+rabbitmqctl_encode_encrypt_decrypt(Secret) ->
+ PassPhrase = "passphrase",
+ {ok, Output} = rabbit_control_pbe:encode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [Secret, PassPhrase]
+ ),
+ {encrypted, Encrypted} = rabbit_control_pbe:evaluate_input_as_term(lists:flatten(Output)),
+
+ {ok, Result} = rabbit_control_pbe:decode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [lists:flatten(io_lib:format("~p", [Encrypted])), PassPhrase]
+ ),
+ Secret = lists:flatten(Result),
+ % decrypt with {encrypted, ...} form as input
+ {ok, Result} = rabbit_control_pbe:decode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [lists:flatten(io_lib:format("~p", [{encrypted, Encrypted}])), PassPhrase]
+ ),
+
+ % wrong passphrase
+ {error, _} = rabbit_control_pbe:decode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [lists:flatten(io_lib:format("~p", [Encrypted])), PassPhrase ++ " "]
+ ),
+ {error, _} = rabbit_control_pbe:decode(
+ rabbit_pbe:default_cipher(), rabbit_pbe:default_hash(), rabbit_pbe:default_iterations(),
+ [lists:flatten(io_lib:format("~p", [{encrypted, Encrypted}])), PassPhrase ++ " "]
+ ).
diff --git a/test/unit_SUITE_data/lib/rabbit_shovel_test/ebin/rabbit_shovel_test.app b/test/unit_config_value_encryption_SUITE_data/lib/rabbit_shovel_test/ebin/rabbit_shovel_test.app
index a8481c9aa4..a8481c9aa4 100644
--- a/test/unit_SUITE_data/lib/rabbit_shovel_test/ebin/rabbit_shovel_test.app
+++ b/test/unit_config_value_encryption_SUITE_data/lib/rabbit_shovel_test/ebin/rabbit_shovel_test.app
diff --git a/test/unit_SUITE_data/rabbit_shovel_test.passphrase b/test/unit_config_value_encryption_SUITE_data/rabbit_shovel_test.passphrase
index ce01362503..ce01362503 100644
--- a/test/unit_SUITE_data/rabbit_shovel_test.passphrase
+++ b/test/unit_config_value_encryption_SUITE_data/rabbit_shovel_test.passphrase
diff --git a/test/unit_disk_monitor_SUITE.erl b/test/unit_disk_monitor_SUITE.erl
index 87ce6f8d2d..2decb5a07a 100644
--- a/test/unit_disk_monitor_SUITE.erl
+++ b/test/unit_disk_monitor_SUITE.erl
@@ -31,8 +31,6 @@ all() ->
groups() ->
[
{sequential_tests, [], [
- disk_monitor,
- disk_monitor_enable,
set_disk_free_limit_command
]}
].
@@ -78,10 +76,6 @@ set_disk_free_limit_command(Config) ->
?MODULE, set_disk_free_limit_command1, [Config]).
set_disk_free_limit_command1(_Config) ->
- %% use an absolute value
- rabbit_disk_monitor:set_disk_free_limit("2000kiB"),
- ?assertEqual(2048000, rabbit_disk_monitor:get_disk_free_limit()),
-
%% Use an integer
rabbit_disk_monitor:set_disk_free_limit({mem_relative, 1}),
disk_free_limit_to_total_memory_ratio_is(1),
@@ -90,7 +84,12 @@ set_disk_free_limit_command1(_Config) ->
rabbit_disk_monitor:set_disk_free_limit({mem_relative, 1.5}),
disk_free_limit_to_total_memory_ratio_is(1.5),
+ %% use an absolute value
+ rabbit_disk_monitor:set_disk_free_limit("70MiB"),
+ ?assertEqual(73400320, rabbit_disk_monitor:get_disk_free_limit()),
+
rabbit_disk_monitor:set_disk_free_limit("50MB"),
+ ?assertEqual(50 * 1000 * 1000, rabbit_disk_monitor:get_disk_free_limit()),
passed.
disk_free_limit_to_total_memory_ratio_is(MemRatio) ->
@@ -98,53 +97,3 @@ disk_free_limit_to_total_memory_ratio_is(MemRatio) ->
% Total memory is unstable, so checking order
true = ExpectedLimit/rabbit_disk_monitor:get_disk_free_limit() < 1.2,
true = ExpectedLimit/rabbit_disk_monitor:get_disk_free_limit() > 0.98.
-
-
-disk_monitor(Config) ->
- passed = rabbit_ct_broker_helpers:rpc(Config, 0,
- ?MODULE, disk_monitor1, [Config]).
-
-disk_monitor1(_Config) ->
- %% Issue: rabbitmq-server #91
- %% os module could be mocked using 'unstick', however it may have undesired
- %% side effects in following tests. Thus, we mock at rabbit_misc level
- ok = meck:new(rabbit_misc, [passthrough]),
- ok = meck:expect(rabbit_misc, os_cmd, fun(_) -> "\n" end),
- ok = rabbit_sup:stop_child(rabbit_disk_monitor_sup),
- ok = rabbit_sup:start_delayed_restartable_child(rabbit_disk_monitor, [1000]),
- meck:unload(rabbit_misc),
- passed.
-
-disk_monitor_enable(Config) ->
- passed = rabbit_ct_broker_helpers:rpc(Config, 0,
- ?MODULE, disk_monitor_enable1, [Config]).
-
-disk_monitor_enable1(_Config) ->
- ok = meck:new(rabbit_misc, [passthrough]),
- ok = meck:expect(rabbit_misc, os_cmd, fun(_) -> "\n" end),
- application:set_env(rabbit, disk_monitor_failure_retries, 20000),
- application:set_env(rabbit, disk_monitor_failure_retry_interval, 100),
- ok = rabbit_sup:stop_child(rabbit_disk_monitor_sup),
- ok = rabbit_sup:start_delayed_restartable_child(rabbit_disk_monitor, [1000]),
- undefined = rabbit_disk_monitor:get_disk_free(),
- Cmd = case os:type() of
- {win32, _} -> " Le volume dans le lecteur C n’a pas de nom.\n"
- " Le numéro de série du volume est 707D-5BDC\n"
- "\n"
- " Répertoire de C:\Users\n"
- "\n"
- "10/12/2015 11:01 <DIR> .\n"
- "10/12/2015 11:01 <DIR> ..\n"
- " 0 fichier(s) 0 octets\n"
- " 2 Rép(s) 758537121792 octets libres\n";
- _ -> "Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on\n"
- "/dev/disk1 975798272 234783364 740758908 25% 58759839 185189727 24% /\n"
- end,
- ok = meck:expect(rabbit_misc, os_cmd, fun(_) -> Cmd end),
- timer:sleep(1000),
- Bytes = 740758908 * 1024,
- Bytes = rabbit_disk_monitor:get_disk_free(),
- meck:unload(rabbit_misc),
- application:set_env(rabbit, disk_monitor_failure_retries, 10),
- application:set_env(rabbit, disk_monitor_failure_retry_interval, 120000),
- passed.
diff --git a/test/unit_disk_monitor_mocks_SUITE.erl b/test/unit_disk_monitor_mocks_SUITE.erl
new file mode 100644
index 0000000000..f9deb1de1f
--- /dev/null
+++ b/test/unit_disk_monitor_mocks_SUITE.erl
@@ -0,0 +1,121 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_disk_monitor_mocks_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+-define(TIMEOUT, 30000).
+
+all() ->
+ [
+ {group, sequential_tests}
+ ].
+
+groups() ->
+ [
+ {sequential_tests, [], [
+ disk_monitor,
+ disk_monitor_enable
+ ]}
+ ].
+
+%% -------------------------------------------------------------------
+%% Testsuite setup/teardown
+%% -------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ rabbit_ct_helpers:log_environment(),
+ rabbit_ct_helpers:run_setup_steps(Config).
+
+end_per_suite(Config) ->
+ rabbit_ct_helpers:run_teardown_steps(Config).
+
+init_per_group(Group, Config) ->
+ Config1 = rabbit_ct_helpers:set_config(Config, [
+ {rmq_nodename_suffix, Group},
+ {rmq_nodes_count, 1}
+ ]),
+ rabbit_ct_helpers:run_steps(Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()).
+
+end_per_group(_Group, Config) ->
+ rabbit_ct_helpers:run_steps(Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()).
+
+init_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_started(Config, Testcase).
+
+end_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_finished(Config, Testcase).
+
+%% -------------------------------------------------------------------
+%% Test cases
+%% -------------------------------------------------------------------
+
+disk_monitor(Config) ->
+ passed = rabbit_ct_broker_helpers:rpc(Config, 0,
+ ?MODULE, disk_monitor1, [Config]).
+
+disk_monitor1(_Config) ->
+ %% Issue: rabbitmq-server #91
+ %% os module could be mocked using 'unstick', however it may have undesired
+ %% side effects in following tests. Thus, we mock at rabbit_misc level
+ ok = meck:new(rabbit_misc, [passthrough]),
+ ok = meck:expect(rabbit_misc, os_cmd, fun(_) -> "\n" end),
+ ok = rabbit_sup:stop_child(rabbit_disk_monitor_sup),
+ ok = rabbit_sup:start_delayed_restartable_child(rabbit_disk_monitor, [1000]),
+ meck:unload(rabbit_misc),
+ passed.
+
+disk_monitor_enable(Config) ->
+ passed = rabbit_ct_broker_helpers:rpc(Config, 0,
+ ?MODULE, disk_monitor_enable1, [Config]).
+
+disk_monitor_enable1(_Config) ->
+ ok = meck:new(rabbit_misc, [passthrough]),
+ ok = meck:expect(rabbit_misc, os_cmd, fun(_) -> "\n" end),
+ application:set_env(rabbit, disk_monitor_failure_retries, 20000),
+ application:set_env(rabbit, disk_monitor_failure_retry_interval, 100),
+ ok = rabbit_sup:stop_child(rabbit_disk_monitor_sup),
+ ok = rabbit_sup:start_delayed_restartable_child(rabbit_disk_monitor, [1000]),
+ undefined = rabbit_disk_monitor:get_disk_free(),
+ Cmd = case os:type() of
+ {win32, _} -> " Le volume dans le lecteur C n’a pas de nom.\n"
+ " Le numéro de série du volume est 707D-5BDC\n"
+ "\n"
+ " Répertoire de C:\Users\n"
+ "\n"
+ "10/12/2015 11:01 <DIR> .\n"
+ "10/12/2015 11:01 <DIR> ..\n"
+ " 0 fichier(s) 0 octets\n"
+ " 2 Rép(s) 758537121792 octets libres\n";
+ _ -> "Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on\n"
+ "/dev/disk1 975798272 234783364 740758908 25% 58759839 185189727 24% /\n"
+ end,
+ ok = meck:expect(rabbit_misc, os_cmd, fun(_) -> Cmd end),
+ timer:sleep(1000),
+ Bytes = 740758908 * 1024,
+ Bytes = rabbit_disk_monitor:get_disk_free(),
+ meck:unload(rabbit_misc),
+ application:set_env(rabbit, disk_monitor_failure_retries, 10),
+ application:set_env(rabbit, disk_monitor_failure_retry_interval, 120000),
+ passed.
diff --git a/test/unit_gen_server2_SUITE.erl b/test/unit_gen_server2_SUITE.erl
new file mode 100644
index 0000000000..0135193ff1
--- /dev/null
+++ b/test/unit_gen_server2_SUITE.erl
@@ -0,0 +1,79 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_gen_server2_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, sequential_tests}
+ ].
+
+groups() ->
+ [
+ {sequential_tests, [], [
+ gen_server2_with_state
+ ]}
+ ].
+
+%% -------------------------------------------------------------------
+%% Testsuite setup/teardown
+%% -------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ rabbit_ct_helpers:log_environment(),
+ rabbit_ct_helpers:run_setup_steps(Config).
+
+end_per_suite(Config) ->
+ rabbit_ct_helpers:run_teardown_steps(Config).
+
+init_per_group(Group, Config) ->
+ Config1 = rabbit_ct_helpers:set_config(Config, [
+ {rmq_nodename_suffix, Group},
+ {rmq_nodes_count, 1}
+ ]),
+ rabbit_ct_helpers:run_steps(Config1,
+ rabbit_ct_broker_helpers:setup_steps() ++
+ rabbit_ct_client_helpers:setup_steps()).
+
+end_per_group(_Group, Config) ->
+ rabbit_ct_helpers:run_steps(Config,
+ rabbit_ct_client_helpers:teardown_steps() ++
+ rabbit_ct_broker_helpers:teardown_steps()).
+
+init_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_started(Config, Testcase).
+
+end_per_testcase(Testcase, Config) ->
+ rabbit_ct_helpers:testcase_finished(Config, Testcase).
+
+
+%% -------------------------------------------------------------------
+%% Test cases
+%% -------------------------------------------------------------------
+
+gen_server2_with_state(Config) ->
+ passed = rabbit_ct_broker_helpers:rpc(Config, 0,
+ ?MODULE, gen_server2_with_state1, [Config]).
+
+gen_server2_with_state1(_Config) ->
+ fhc_state = gen_server2:with_state(file_handle_cache,
+ fun (S) -> element(1, S) end),
+ passed.
diff --git a/test/unit_inbroker_parallel_SUITE.erl b/test/unit_inbroker_parallel_SUITE.erl
index f0d4b7b1c6..3933c37748 100644
--- a/test/unit_inbroker_parallel_SUITE.erl
+++ b/test/unit_inbroker_parallel_SUITE.erl
@@ -47,9 +47,7 @@ groups() ->
[
{parallel_tests, [parallel], [
configurable_server_properties,
- credit_flow_settings,
dynamic_mirroring,
- gen_server2_with_state,
mcall,
max_message_size,
@@ -464,17 +462,6 @@ test_spawn_remote() ->
after ?TIMEOUT -> throw(failed_to_receive_result)
end.
-%% ---------------------------------------------------------------------------
-%% Unordered tests (originally from rabbit_tests.erl).
-%% ---------------------------------------------------------------------------
-gen_server2_with_state(Config) ->
- passed = rabbit_ct_broker_helpers:rpc(Config, 0,
- ?MODULE, gen_server2_with_state1, [Config]).
-
-gen_server2_with_state1(_Config) ->
- fhc_state = gen_server2:with_state(file_handle_cache,
- fun (S) -> element(1, S) end),
- passed.
mcall(Config) ->
passed = rabbit_ct_broker_helpers:rpc(Config, 0,
diff --git a/test/unit_operator_policy_SUITE.erl b/test/unit_operator_policy_SUITE.erl
new file mode 100644
index 0000000000..36620aa66c
--- /dev/null
+++ b/test/unit_operator_policy_SUITE.erl
@@ -0,0 +1,122 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_operator_policy_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("rabbit_common/include/rabbit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, parallel_tests}
+ ].
+
+groups() ->
+ [
+ {parallel_tests, [parallel], [
+ merge_operator_policy_definitions
+ ]}
+ ].
+
+init_per_testcase(TC, Config) when TC =:= decrypt_start_app;
+ TC =:= decrypt_start_app_file;
+ TC =:= decrypt_start_app_undefined;
+ TC =:= decrypt_start_app_wrong_passphrase ->
+ application:set_env(rabbit, feature_flags_file, "", [{persistent, true}]),
+ Config;
+init_per_testcase(_Testcase, Config) ->
+ Config.
+
+end_per_testcase(_TC, _Config) ->
+ ok.
+
+
+%% -------------------------------------------------------------------
+%% Test Cases
+%% -------------------------------------------------------------------
+
+merge_operator_policy_definitions(_Config) ->
+ P1 = undefined,
+ P2 = [{definition, [{<<"message-ttl">>, 3000}]}],
+ ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(P1, P2)),
+ ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(P2, P1)),
+
+ ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(P1, rabbit_data_coercion:to_map(P2))),
+ ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(rabbit_data_coercion:to_map(P2), P1)),
+
+ ?assertEqual(undefined, rabbit_policy:merge_operator_definitions(undefined, undefined)),
+
+ ?assertEqual([], rabbit_policy:merge_operator_definitions([], [])),
+ ?assertEqual([], rabbit_policy:merge_operator_definitions(#{}, [])),
+ ?assertEqual([], rabbit_policy:merge_operator_definitions(#{}, #{})),
+ ?assertEqual([], rabbit_policy:merge_operator_definitions([], #{})),
+
+ %% operator policy takes precedence
+ ?assertEqual([{<<"message-ttl">>, 3000}], rabbit_policy:merge_operator_definitions(
+ [{definition, [
+ {<<"message-ttl">>, 5000}
+ ]}],
+ [{definition, [
+ {<<"message-ttl">>, 3000}
+ ]}]
+ )),
+
+ ?assertEqual([{<<"delivery-limit">>, 20},
+ {<<"message-ttl">>, 3000}],
+ rabbit_policy:merge_operator_definitions(
+ [{definition, [
+ {<<"message-ttl">>, 5000},
+ {<<"delivery-limit">>, 20}
+ ]}],
+ [{definition, [
+ {<<"message-ttl">>, 3000}
+ ]}])
+ ),
+
+ ?assertEqual(
+ [{<<"delivery-limit">>, 20},
+ {<<"message-ttl">>, 3000},
+ {<<"unknown">>, <<"value">>}],
+
+ rabbit_policy:merge_operator_definitions(
+ #{definition => #{
+ <<"message-ttl">> => 5000,
+ <<"delivery-limit">> => 20
+ }},
+ #{definition => #{
+ <<"message-ttl">> => 3000,
+ <<"unknown">> => <<"value">>
+ }})
+ ),
+
+ ?assertEqual(
+ [{<<"delivery-limit">>, 20},
+ {<<"message-ttl">>, 3000}],
+
+ rabbit_policy:merge_operator_definitions(
+ #{definition => #{
+ <<"message-ttl">> => 5000,
+ <<"delivery-limit">> => 20
+ }},
+ [{definition, [
+ {<<"message-ttl">>, 3000}
+ ]}])
+ ),
+
+ passed.
diff --git a/test/unit_plugin_directories_SUITE.erl b/test/unit_plugin_directories_SUITE.erl
new file mode 100644
index 0000000000..3a4ae44581
--- /dev/null
+++ b/test/unit_plugin_directories_SUITE.erl
@@ -0,0 +1,85 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_plugin_directories_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("rabbit_common/include/rabbit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, parallel_tests}
+ ].
+
+groups() ->
+ [
+ {parallel_tests, [parallel], [
+ listing_plugins_from_multiple_directories
+ ]}
+ ].
+
+
+%% -------------------------------------------------------------------
+%% Test Cases
+%% -------------------------------------------------------------------
+
+listing_plugins_from_multiple_directories(Config) ->
+ %% Generate some fake plugins in .ez files
+ FirstDir = filename:join([?config(priv_dir, Config), "listing_plugins_from_multiple_directories-1"]),
+ SecondDir = filename:join([?config(priv_dir, Config), "listing_plugins_from_multiple_directories-2"]),
+ ok = file:make_dir(FirstDir),
+ ok = file:make_dir(SecondDir),
+ lists:foreach(fun({Dir, AppName, Vsn}) ->
+ EzName = filename:join([Dir, io_lib:format("~s-~s.ez", [AppName, Vsn])]),
+ AppFileName = lists:flatten(io_lib:format("~s-~s/ebin/~s.app", [AppName, Vsn, AppName])),
+ AppFileContents = list_to_binary(
+ io_lib:format(
+ "~p.",
+ [{application, AppName,
+ [{vsn, Vsn},
+ {applications, [kernel, stdlib, rabbit]}]}])),
+ {ok, {_, EzData}} = zip:zip(EzName, [{AppFileName, AppFileContents}], [memory]),
+ ok = file:write_file(EzName, EzData)
+ end,
+ [{FirstDir, plugin_first_dir, "3"},
+ {SecondDir, plugin_second_dir, "4"},
+ {FirstDir, plugin_both, "1"},
+ {SecondDir, plugin_both, "2"}]),
+
+ %% Everything was collected from both directories, plugin with higher
+ %% version should take precedence
+ PathSep = case os:type() of
+ {win32, _} -> ";";
+ _ -> ":"
+ end,
+ Path = FirstDir ++ PathSep ++ SecondDir,
+ Got = lists:sort([{Name, Vsn} || #plugin{name = Name, version = Vsn} <- rabbit_plugins:list(Path)]),
+ %% `rabbit` was loaded automatically by `rabbit_plugins:list/1`.
+ %% We want to unload it now so it does not interfere with other
+ %% testcases.
+ application:unload(rabbit),
+ Expected = [{plugin_both, "2"}, {plugin_first_dir, "3"}, {plugin_second_dir, "4"}],
+ case Got of
+ Expected ->
+ ok;
+ _ ->
+ ct:pal("Got ~p~nExpected: ~p", [Got, Expected]),
+ exit({wrong_plugins_list, Got})
+ end,
+ ok.
diff --git a/test/unit_supervisor2_SUITE.erl b/test/unit_supervisor2_SUITE.erl
new file mode 100644
index 0000000000..c1dffe575e
--- /dev/null
+++ b/test/unit_supervisor2_SUITE.erl
@@ -0,0 +1,78 @@
+%% The contents of this file are subject to the Mozilla Public License
+%% Version 1.1 (the "License"); you may not use this file except in
+%% compliance with the License. You may obtain a copy of the License at
+%% https://www.mozilla.org/MPL/
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+%% License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is RabbitMQ.
+%%
+%% The Initial Developer of the Original Code is GoPivotal, Inc.
+%% Copyright (c) 2011-2020 VMware, Inc. or its affiliates. All rights reserved.
+%%
+
+-module(unit_supervisor2_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-compile(export_all).
+
+all() ->
+ [
+ {group, sequential_tests}
+ ].
+
+groups() ->
+ [
+ {sequential_tests, [], [
+ check_shutdown_stop,
+ check_shutdown_ignored
+ ]}
+ ].
+
+%% -------------------------------------------------------------------
+%% Test Cases
+%% -------------------------------------------------------------------
+
+check_shutdown_stop(_Config) ->
+ ok = check_shutdown(stop, 200, 200, 2000).
+
+check_shutdown_ignored(_Config) ->
+ ok = check_shutdown(ignored, 1, 2, 2000).
+
+check_shutdown(SigStop, Iterations, ChildCount, SupTimeout) ->
+ {ok, Sup} = supervisor2:start_link(dummy_supervisor2, [SupTimeout]),
+ Res = lists:foldl(
+ fun (I, ok) ->
+ TestSupPid = erlang:whereis(dummy_supervisor2),
+ ChildPids =
+ [begin
+ {ok, ChildPid} =
+ supervisor2:start_child(TestSupPid, []),
+ ChildPid
+ end || _ <- lists:seq(1, ChildCount)],
+ MRef = erlang:monitor(process, TestSupPid),
+ [P ! SigStop || P <- ChildPids],
+ ok = supervisor2:terminate_child(Sup, test_sup),
+ {ok, _} = supervisor2:restart_child(Sup, test_sup),
+ receive
+ {'DOWN', MRef, process, TestSupPid, shutdown} ->
+ ok;
+ {'DOWN', MRef, process, TestSupPid, Reason} ->
+ {error, {I, Reason}}
+ end;
+ (_, R) ->
+ R
+ end, ok, lists:seq(1, Iterations)),
+ unlink(Sup),
+ MSupRef = erlang:monitor(process, Sup),
+ exit(Sup, shutdown),
+ receive
+ {'DOWN', MSupRef, process, Sup, _Reason} ->
+ ok
+ end,
+ Res.