diff options
author | Sverker Eriksson <[email protected]> | 2017-08-30 20:55:08 +0200 |
---|---|---|
committer | Sverker Eriksson <[email protected]> | 2017-08-30 20:55:08 +0200 |
commit | 7c67bbddb53c364086f66260701bc54a61c9659c (patch) | |
tree | 92ab0d4b91d5e2f6e7a3f9d61ea25089e8a71fe0 /lib/stdlib/test/dets_SUITE.erl | |
parent | 97dc5e7f396129222419811c173edc7fa767b0f8 (diff) | |
parent | 3b7a6ffddc819bf305353a593904cea9e932e7dc (diff) | |
download | otp-7c67bbddb53c364086f66260701bc54a61c9659c.tar.gz otp-7c67bbddb53c364086f66260701bc54a61c9659c.tar.bz2 otp-7c67bbddb53c364086f66260701bc54a61c9659c.zip |
Merge tag 'OTP-19.0' into sverker/19/binary_to_atom-utf8-crash/ERL-474/OTP-14590
Diffstat (limited to 'lib/stdlib/test/dets_SUITE.erl')
-rw-r--r-- | lib/stdlib/test/dets_SUITE.erl | 725 |
1 files changed, 362 insertions, 363 deletions
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 8ff7c3ccc9..8948f496c4 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -1,24 +1,25 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% 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. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. %% %% %CopyrightEnd% %% -module(dets_SUITE). -%-define(debug, true). +%%-define(debug, true). -ifdef(debug). -define(format(S, A), io:format(S, A)). @@ -27,10 +28,10 @@ -define(privdir(_), "./dets_SUITE_priv"). -define(datadir(_), "./dets_SUITE_data"). -else. --include_lib("test_server/include/test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(format(S, A), ok). --define(privdir(Conf), ?config(priv_dir, Conf)). --define(datadir(Conf), ?config(data_dir, Conf)). +-define(privdir(Conf), proplists:get_value(priv_dir, Conf)). +-define(datadir(Conf), proplists:get_value(data_dir, Conf)). -endif. -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -52,7 +53,8 @@ simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, - otp_8923/1, otp_9282/1]). + otp_8923/1, otp_9282/1, otp_11245/1, otp_11709/1, otp_13229/1, + otp_13260/1]). -export([dets_dirty_loop/0]). @@ -81,15 +83,14 @@ -define(CLOSED_PROPERLY,1). init_per_testcase(_Case, Config) -> - Dog=?t:timetrap(?t:minutes(15)), - [{watchdog, Dog}|Config]. + Config. end_per_testcase(_Case, _Config) -> - Dog=?config(watchdog, _Config), - test_server:timetrap_cancel(Dog), ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,15}}]. all() -> [ @@ -109,7 +110,8 @@ all() -> many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923, otp_9282 + otp_8899, otp_8903, otp_8923, otp_9282, otp_11245, otp_11709, + otp_13229, otp_13260 ]. groups() -> @@ -127,10 +129,7 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. -newly_started(doc) -> - ["OTP-3621"]; -newly_started(suite) -> - []; +%% OTP-3621 newly_started(Config) when is_list(Config) -> true = is_alive(), {ok, Node} = test_server:start_node(slave1, slave, []), @@ -138,17 +137,11 @@ newly_started(Config) when is_list(Config) -> test_server:stop_node(Node), ok. -basic_v8(doc) -> - ["Basic test case."]; -basic_v8(suite) -> - []; +%% Basic test case. basic_v8(Config) when is_list(Config) -> basic(Config, 8). -basic_v9(doc) -> - ["Basic test case."]; -basic_v9(suite) -> - []; +%% Basic test case. basic_v9(Config) when is_list(Config) -> basic(Config, 9). @@ -181,17 +174,9 @@ basic(Config, Version) -> ok. -open_v8(doc) -> - []; -open_v8(suite) -> - []; open_v8(Config) when is_list(Config) -> open(Config, 8). -open_v9(doc) -> - []; -open_v9(suite) -> - []; open_v9(Config) when is_list(Config) -> open(Config, 9). @@ -223,8 +208,7 @@ open(Config, Version) -> ?format("Crashing dets server \n", []), process_flag(trap_exit, true), - Procs = [whereis(?DETS_SERVER) | map(fun(Tab) -> dets:info(Tab, pid) end, - Tabs)], + Procs = [whereis(?DETS_SERVER) | [dets:info(Tab, pid) || Tab <- Tabs]], foreach(fun(Pid) -> exit(Pid, kill) end, Procs), timer:sleep(100), c:flush(), %% flush all the EXIT sigs @@ -235,18 +219,32 @@ open(Config, Version) -> open_files(1, All, Version), ?format("Checking contents of repaired files \n", []), check(Tabs, Data), - - close_all(Tabs), + close_all(Tabs), delete_files(All), - P1 = pps(), + {Ports0, Procs0} = P0, - {Ports1, Procs1} = P1, - true = Ports1 =:= Ports0, - %% The dets_server process has been restarted: - [_] = Procs0 -- Procs1, - [_] = Procs1 -- Procs0, - ok. + Test = fun() -> + P1 = pps(), + {Ports1, Procs1} = P1, + show("Old port", Ports0 -- Ports1), + show("New port", Ports1 -- Ports0), + show("Old procs", Procs0 -- Procs1), + show("New procs", Procs1 -- Procs0), + io:format("Remaining Dets-pids (should be nil): ~p~n", + [find_dets_pids()]), + true = Ports1 =:= Ports0, + %% The dets_server process has been restarted: + [_] = Procs0 -- Procs1, + [_] = Procs1 -- Procs0, + ok + end, + case catch Test() of + ok -> ok; + _ -> + timer:sleep(500), + ok = Test() + end. check(Tabs, Data) -> foreach(fun(Tab) -> @@ -267,17 +265,11 @@ bad(_Tab, _Item) -> ?format("Can't find item ~p in ~p ~n", [_Item, _Tab]), exit(badtab). -sets_v8(doc) -> - ["Performs traversal and match testing on set type dets tables."]; -sets_v8(suite) -> - []; +%% Perform traversal and match testing on set type dets tables. sets_v8(Config) when is_list(Config) -> sets(Config, 8). -sets_v9(doc) -> - ["Performs traversal and match testing on set type dets tables."]; -sets_v9(suite) -> - []; +%% Perform traversal and match testing on set type dets tables. sets_v9(Config) when is_list(Config) -> sets(Config, 9). @@ -309,17 +301,11 @@ sets(Config, Version) -> check_pps(P0), ok. -bags_v8(doc) -> - ["Performs traversal and match testing on bag type dets tables."]; -bags_v8(suite) -> - []; +%% Perform traversal and match testing on bag type dets tables. bags_v8(Config) when is_list(Config) -> bags(Config, 8). -bags_v9(doc) -> - ["Performs traversal and match testing on bag type dets tables."]; -bags_v9(suite) -> - []; +%% Perform traversal and match testing on bag type dets tables. bags_v9(Config) when is_list(Config) -> bags(Config, 9). @@ -349,17 +335,11 @@ bags(Config, Version) -> ok. -duplicate_bags_v8(doc) -> - ["Performs traversal and match testing on duplicate_bag type dets tables."]; -duplicate_bags_v8(suite) -> - []; +%% Perform traversal and match testing on duplicate_bag type dets tables. duplicate_bags_v8(Config) when is_list(Config) -> duplicate_bags(Config, 8). -duplicate_bags_v9(doc) -> - ["Performs traversal and match testing on duplicate_bag type dets tables."]; -duplicate_bags_v9(suite) -> - []; +%% Perform traversal and match testing on duplicate_bag type dets tables. duplicate_bags_v9(Config) when is_list(Config) -> duplicate_bags(Config, 9). @@ -389,17 +369,9 @@ duplicate_bags(Config, Version) when is_list(Config) -> ok. -access_v8(doc) -> - []; -access_v8(suite) -> - []; access_v8(Config) when is_list(Config) -> access(Config, 8). -access_v9(doc) -> - []; -access_v9(suite) -> - []; access_v9(Config) when is_list(Config) -> access(Config, 9). @@ -432,10 +404,7 @@ access(Config, Version) -> ok. -dirty_mark(doc) -> - ["Test that the table is not marked dirty if not written"]; -dirty_mark(suite) -> - []; +%% Test that the table is not marked dirty if not written. dirty_mark(Config) when is_list(Config) -> true = is_alive(), Tab = dets_dirty_mark_test, @@ -484,10 +453,7 @@ dirty_mark(Config) when is_list(Config) -> check_pps(P0), ok. -dirty_mark2(doc) -> - ["Test that the table is flushed when auto_save is in effect"]; -dirty_mark2(suite) -> - []; +%% Test that the table is flushed when auto_save is in effect. dirty_mark2(Config) when is_list(Config) -> true = is_alive(), Tab = dets_dirty_mark2_test, @@ -555,17 +521,11 @@ dets_dirty_loop() -> end. -bag_next_v8(suite) -> - []; -bag_next_v8(doc) -> - ["Check that bags and next work as expected."]; +%% Check that bags and next work as expected. bag_next_v8(Config) when is_list(Config) -> bag_next(Config, 8). -bag_next_v9(suite) -> - []; -bag_next_v9(doc) -> - ["Check that bags and next work as expected."]; +%% Check that bags and next work as expected. bag_next_v9(Config) when is_list(Config) -> Tab = dets_bag_next_test, FName = filename(Tab, Config), @@ -618,17 +578,9 @@ bag_next(Config, Version) -> check_pps(P0), ok. -oldbugs_v8(doc) -> - []; -oldbugs_v8(suite) -> - []; oldbugs_v8(Config) when is_list(Config) -> oldbugs(Config, 8). -oldbugs_v9(doc) -> - []; -oldbugs_v9(suite) -> - []; oldbugs_v9(Config) when is_list(Config) -> oldbugs(Config, 9). @@ -646,9 +598,7 @@ oldbugs(Config, Version) -> check_pps(P0), ok. -unsafe_assumptions(suite) -> []; -unsafe_assumptions(doc) -> - "Tests that shrinking an object and then expanding it works."; +%% Test that shrinking an object and then expanding it works. unsafe_assumptions(Config) when is_list(Config) -> FName = filename(dets_suite_unsafe_assumptions_test, Config), file:delete(FName), @@ -677,17 +627,13 @@ unsafe_assumptions(Config) when is_list(Config) -> check_pps(P0), ok. -truncated_segment_array_v8(suite) -> []; -truncated_segment_array_v8(doc) -> - "Tests that a file where the segment array has been truncated " - "is possible to repair."; +%% Test that a file where the segment array has been truncated +%% is possible to repair. truncated_segment_array_v8(Config) when is_list(Config) -> trunc_seg_array(Config, 8). -truncated_segment_array_v9(suite) -> []; -truncated_segment_array_v9(doc) -> - "Tests that a file where the segment array has been truncated " - "is possible to repair."; +%% Test that a file where the segment array has been truncated +%% is possible to repair. truncated_segment_array_v9(Config) when is_list(Config) -> trunc_seg_array(Config, 9). @@ -713,17 +659,11 @@ trunc_seg_array(Config, V) -> check_pps(P0), ok. -open_file_v8(doc) -> - ["open_file/1 test case."]; -open_file_v8(suite) -> - []; +%% Test open_file/1. open_file_v8(Config) when is_list(Config) -> open_1(Config, 8). -open_file_v9(doc) -> - ["open_file/1 test case."]; -open_file_v9(suite) -> - []; +%% Test open_file/1. open_file_v9(Config) when is_list(Config) -> T = open_v9, Fname = filename(T, Config), @@ -772,26 +712,20 @@ open_1(Config, V) -> crash(Fname, TypePos), {error, {invalid_type_code,Fname}} = dets:open_file(Fname), truncate(Fname, HeadSize - 10), - {error, {tooshort,Fname}} = dets:open_file(Fname), - {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]), - ok = dets:close(TabRef), + {error,{not_a_dets_file,Fname}} = dets:open_file(Fname), + {error,{not_a_dets_file,Fname}} = + dets:open_file(TabRef, [{file,Fname},{version,V}]), file:delete(Fname), {error,{file_error,{foo,bar},_}} = dets:is_dets_file({foo,bar}), check_pps(P0), ok. -init_table_v8(doc) -> - ["initialize_table/2 and from_ets/2 test case."]; -init_table_v8(suite) -> - []; +%% Test initialize_table/2 and from_ets/2. init_table_v8(Config) when is_list(Config) -> init_table(Config, 8). -init_table_v9(doc) -> - ["initialize_table/2 and from_ets/2 test case."]; -init_table_v9(suite) -> - []; +%% Test initialize_table/2 and from_ets/2. init_table_v9(Config) when is_list(Config) -> %% Objects are returned in "time order". T = init_table_v9, @@ -967,10 +901,12 @@ fast_init_table(Config) -> {'EXIT', _} = (catch dets:init_table(TabRef, fun(foo) -> bar end, {format,bchunk})), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), {'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end, {format,bchunk})), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), {'EXIT', {badarg, _}} = (catch dets:init_table(TabRef, nofun, {format,bchunk})), @@ -979,10 +915,12 @@ fast_init_table(Config) -> away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end, {format,bchunk})), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), {error, {init_fun, fopp}} = dets:init_table(TabRef, fun(read) -> fopp end, {format,bchunk}), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), dets:safe_fixtable(TabRef, true), {error, {fixed_table, TabRef}} = @@ -1250,17 +1188,11 @@ items(I, N, C, L) when I =:= N; C =:= 0 -> items(I, N, C, L) -> items(I+1, N, C-1, [{I, item(I)} | L]). -repair_v8(doc) -> - ["open_file and repair."]; -repair_v8(suite) -> - []; +%% Test open_file and repair. repair_v8(Config) when is_list(Config) -> repair(Config, 8). -repair_v9(doc) -> - ["open_file and repair."]; -repair_v9(suite) -> - []; +%% Test open_file and repair. repair_v9(Config) when is_list(Config) -> %% Convert from format 9 to format 8. T = convert_98, @@ -1389,23 +1321,6 @@ repair(Config, V) -> {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]), ok = ins(TabRef, 100), ok = dets:close(TabRef), - truncate(Fname, HeadSize - 10), - %% a new file is created ('tooshort') - {ok, TabRef} = dets:open_file(TabRef, - [{file,Fname},{version,V}, - {min_no_slots,1000}, - {max_no_slots,1000000}]), - case dets:info(TabRef, no_slots) of - undefined -> ok; - {Min1,Slot1,Max1} -> - true = Min1 =< Slot1, true = Slot1 =< Max1, - true = 1000 < Min1, true = 1000+256 > Min1, - true = 1000000 < Max1, true = (1 bsl 20)+256 > Max1 - end, - 0 = dets:info(TabRef, size), - no_keys_test(TabRef), - _ = histogram(TabRef, silent), - ok = dets:close(TabRef), file:delete(Fname), %% version bump (v8) @@ -1614,11 +1529,9 @@ repair(Config, V) -> check_pps(P0), ok. -hash_v8b_v8c(doc) -> - ["Test the use of different hashing algorithms in v8b and v8c of the " - "Dets file format."]; -hash_v8b_v8c(suite) -> - []; + +%% Test the use of different hashing algorithms in v8b and v8c of the +%% Dets file format. hash_v8b_v8c(Config) when is_list(Config) -> Source = filename:join(?datadir(Config), "dets_test_v8b.dets"), @@ -1693,10 +1606,7 @@ hash_v8b_v8c(Config) when is_list(Config) -> check_pps(P0), {comment, Mess}. -phash(doc) -> - ["Test version 9(b) with erlang:phash/2 as hash function."]; -phash(suite) -> - []; +%% Test version 9(b) with erlang:phash/2 as hash function. phash(Config) when is_list(Config) -> T = phash, Phash_v9bS = filename:join(?datadir(Config), "version_9b_phash.dat"), @@ -1754,17 +1664,11 @@ phash(Config) when is_list(Config) -> file:delete(Fname), ok. -fold_v8(doc) -> - ["foldl, foldr, to_ets"]; -fold_v8(suite) -> - []; +%% Test foldl, foldr, to_ets. fold_v8(Config) when is_list(Config) -> fold(Config, 8). -fold_v9(doc) -> - ["foldl, foldr, to_ets"]; -fold_v9(suite) -> - []; +%% Test foldl, foldr, to_ets. fold_v9(Config) when is_list(Config) -> fold(Config, 9). @@ -1833,17 +1737,11 @@ fold(Config, Version) -> check_pps(P0), ok. -fixtable_v8(doc) -> - ["Add objects to a fixed table."]; -fixtable_v8(suite) -> - []; +%% Add objects to a fixed table. fixtable_v8(Config) when is_list(Config) -> fixtable(Config, 8). -fixtable_v9(doc) -> - ["Add objects to a fixed table."]; -fixtable_v9(suite) -> - []; +%% Add objects to a fixed table. fixtable_v9(Config) when is_list(Config) -> fixtable(Config, 9). @@ -1875,9 +1773,33 @@ fixtable(Config, Version) when is_list(Config) -> {ok, _} = dets:open_file(T, [{type, duplicate_bag} | Args]), %% In a fixed table, delete and re-insert an object. ok = dets:insert(T, {1, a, b}), + SysBefore = erlang:timestamp(), + MonBefore = erlang:monotonic_time(), dets:safe_fixtable(T, true), + MonAfter = erlang:monotonic_time(), + SysAfter = erlang:timestamp(), + Self = self(), + {FixMonTime,[{Self,1}]} = dets:info(T,safe_fixed_monotonic_time), + {FixSysTime,[{Self,1}]} = dets:info(T,safe_fixed), + true = is_integer(FixMonTime), + true = MonBefore =< FixMonTime, + true = FixMonTime =< MonAfter, + {FstMs,FstS,FstUs} = FixSysTime, + true = is_integer(FstMs), + true = is_integer(FstS), + true = is_integer(FstUs), + case erlang:system_info(time_warp_mode) of + no_time_warp -> + true = timer:now_diff(FixSysTime, SysBefore) >= 0, + true = timer:now_diff(SysAfter, FixSysTime) >= 0; + _ -> + %% ets:info(Tab,safe_fixed) not timewarp safe... + ignore + end, ok = dets:match_delete(T, {1, a, b}), ok = dets:insert(T, {1, a, b}), + {FixMonTime,[{Self,1}]} = dets:info(T,safe_fixed_monotonic_time), + {FixSysTime,[{Self,1}]} = dets:info(T,safe_fixed), dets:safe_fixtable(T, false), 1 = length(dets:match_object(T, '_')), @@ -1909,17 +1831,11 @@ fixtable(Config, Version) when is_list(Config) -> check_pps(P0), ok. -match_v8(doc) -> - ["Matching objects of a fixed table."]; -match_v8(suite) -> - []; +%% Matching objects of a fixed table. match_v8(Config) when is_list(Config) -> match(Config, 8). -match_v9(doc) -> - ["Matching objects of a fixed table."]; -match_v9(suite) -> - []; +%% Matching objects of a fixed table. match_v9(Config) when is_list(Config) -> match(Config, 9). @@ -2045,6 +1961,12 @@ match(Config, Version) -> CrashPos = if Version =:= 8 -> 5; Version =:= 9 -> 1 end, crash(Fname, ObjPos2+CrashPos), {ok, _} = dets:open_file(T, Args), + case dets:insert_new(T, Obj) of % OTP-12024 + ok -> + bad_object(dets:sync(T), Fname); + Else3 -> + bad_object(Else3, Fname) + end, io:format("Expect corrupt table:~n"), case ins(T, N) of ok -> @@ -2085,17 +2007,11 @@ match(Config, Version) -> check_pps(P0), ok. -select_v8(doc) -> - ["Selecting objects of a fixed table."]; -select_v8(suite) -> - []; +%% Selecting objects of a fixed table. select_v8(Config) when is_list(Config) -> select(Config, 8). -select_v9(doc) -> - ["Selecting objects of a fixed table."]; -select_v9(suite) -> - []; +%% Selecting objects of a fixed table. select_v9(Config) when is_list(Config) -> select(Config, 9). @@ -2199,10 +2115,7 @@ select(Config, Version) -> check_pps(P0), ok. -update_counter(doc) -> - ["Test update_counter/1."]; -update_counter(suite) -> - []; +%% Test update_counter/1. update_counter(Config) when is_list(Config) -> T = update_counter, Fname = filename(select, Config), @@ -2236,10 +2149,7 @@ update_counter(Config) when is_list(Config) -> ok. -badarg(doc) -> - ["Call some functions with bad arguments."]; -badarg(suite) -> - []; +%% Call some functions with bad arguments. badarg(Config) when is_list(Config) -> T = badarg, Fname = filename(select, Config), @@ -2248,7 +2158,6 @@ badarg(Config) when is_list(Config) -> Args = [{file,Fname},{keypos,3}], {ok, _} = dets:open_file(T, [{type,set} | Args]), - % dets:verbose(), %% badargs are tested in match, select and fixtable too. @@ -2371,17 +2280,11 @@ badarg(Config) when is_list(Config) -> check_pps(P0), ok. -cache_sets_v8(doc) -> - ["Test the write cache for sets."]; -cache_sets_v8(suite) -> - []; +%% Test the write cache for sets. cache_sets_v8(Config) when is_list(Config) -> cache_sets(Config, 8). -cache_sets_v9(doc) -> - ["Test the write cache for sets."]; -cache_sets_v9(suite) -> - []; +%% Test the write cache for sets. cache_sets_v9(Config) when is_list(Config) -> cache_sets(Config, 9). @@ -2515,7 +2418,7 @@ cache_sets(Config, DelayedWrite, Extra, Sz, Version) -> {[],[]} -> ok; {X,Y} -> NoBad = length(X) + length(Y), - test_server:fail({sets,DelayedWrite,Extra,Sz,NoBad}) + ct:fail({sets,DelayedWrite,Extra,Sz,NoBad}) end; true -> ok @@ -2526,17 +2429,11 @@ cache_sets(Config, DelayedWrite, Extra, Sz, Version) -> check_pps(P0), ok. -cache_bags_v8(doc) -> - ["Test the write cache for bags."]; -cache_bags_v8(suite) -> - []; +%% Test the write cache for bags. cache_bags_v8(Config) when is_list(Config) -> cache_bags(Config, 8). -cache_bags_v9(doc) -> - ["Test the write cache for bags."]; -cache_bags_v9(suite) -> - []; +%% Test the write cache for bags. cache_bags_v9(Config) when is_list(Config) -> cache_bags(Config, 9). @@ -2679,7 +2576,7 @@ cache_bags(Config, DelayedWrite, Extra, Sz, Version) -> {[],[]} -> ok; {X,Y} -> NoBad = length(X) + length(Y), - test_server:fail({bags,DelayedWrite,Extra,Sz,NoBad}) + ct:fail({bags,DelayedWrite,Extra,Sz,NoBad}) end; true -> ok @@ -2708,17 +2605,11 @@ cache_bags(Config, DelayedWrite, Extra, Sz, Version) -> check_pps(P0), ok. -cache_duplicate_bags_v8(doc) -> - ["Test the write cache for duplicate bags."]; -cache_duplicate_bags_v8(suite) -> - []; +%% Test the write cache for duplicate bags. cache_duplicate_bags_v8(Config) when is_list(Config) -> cache_duplicate_bags(Config, 8). -cache_duplicate_bags_v9(doc) -> - ["Test the write cache for duplicate bags."]; -cache_duplicate_bags_v9(suite) -> - []; +%% Test the write cache for duplicate bags. cache_duplicate_bags_v9(Config) when is_list(Config) -> cache_duplicate_bags(Config, 9). @@ -2837,7 +2728,7 @@ cache_dup_bags(Config, DelayedWrite, Extra, Sz, Version) -> {[],[]} -> ok; {X,Y} -> NoBad = length(X) + length(Y), - test_server:fail({dup_bags,DelayedWrite,Extra,Sz,NoBad}) + ct:fail({dup_bags,DelayedWrite,Extra,Sz,NoBad}) end; true -> ok @@ -2903,10 +2794,7 @@ symdiff(L1, L2) -> sofs:symmetric_partition(sofs:set(L1), sofs:set(L2)), {sofs:to_external(X), sofs:to_external(Y)}. -otp_4208(doc) -> - ["Read only table and traversal caused crash."]; -otp_4208(suite) -> - []; +%% Test read-only tables and traversal caused crashes. otp_4208(Config) when is_list(Config) -> Tab = otp_4208, FName = filename(Tab, Config), @@ -2925,10 +2813,7 @@ otp_4208(Config) when is_list(Config) -> ok. -otp_4989(doc) -> - ["Read only table and growth."]; -otp_4989(suite) -> - []; +%% Test read-only tables and growth. otp_4989(Config) when is_list(Config) -> Tab = otp_4989, FName = filename(Tab, Config), @@ -2956,10 +2841,7 @@ ets_init(Tab, N) -> ets:insert(Tab, {N,N}), ets_init(Tab, N - 1). -otp_8898(doc) -> - ["OTP-8898. Truncated Dets file."]; -otp_8898(suite) -> - []; +%% OTP-8898. Truncated Dets file. otp_8898(Config) when is_list(Config) -> Tab = otp_8898, FName = filename(Tab, Config), @@ -2979,10 +2861,7 @@ otp_8898(Config) when is_list(Config) -> ok. -otp_8899(doc) -> - ["OTP-8899. Several clients. Updated Head was ignored."]; -otp_8899(suite) -> - []; +%% OTP-8899. Several clients. Updated Head was ignored. otp_8899(Config) when is_list(Config) -> Tab = many_clients, FName = filename(Tab, Config), @@ -3007,10 +2886,7 @@ otp_8899(Config) when is_list(Config) -> ok. -many_clients(doc) -> - ["Several clients accessing a table simultaneously."]; -many_clients(suite) -> - []; +%% Test several clients accessing a table simultaneously. many_clients(Config) when is_list(Config) -> Tab = many_clients, FName = filename(Tab, Config), @@ -3196,10 +3072,7 @@ eval([{info,Tag,Expected} | L], Tab) -> eval(Else, _Tab) -> {error, {bad_request,Else}}. -otp_4906(doc) -> - ["More than 128k keys caused crash."]; -otp_4906(suite) -> - []; +%% More than 128k keys caused crash. otp_4906(Config) when is_list(Config) -> N = 256*512 + 400, Tab = otp_4906, @@ -3243,10 +3116,7 @@ ins_small(T, I, N) -> ok = dets:insert(T, {I}), ins_small(T, I+1, N). -otp_5402(doc) -> - ["Unwritable ramfile caused krasch."]; -otp_5402(suite) -> - []; +%% Unwritable ramfile caused crash. otp_5402(Config) when is_list(Config) -> Tab = otp_5402, File = filename:join(["cannot", "write", "this", "file"]), @@ -3273,21 +3143,28 @@ otp_5402(Config) when is_list(Config) -> {error,{file_error,_,_}} = dets:close(T), ok. -simultaneous_open(doc) -> - ["Several clients open and close tables simultaneously."]; -simultaneous_open(suite) -> - []; +%% Several clients open and close tables simultaneously. simultaneous_open(Config) -> Tab = sim_open, File = filename(Tab, Config), ok = monit(Tab, File), - ok = kill_while_repairing(Tab, File), - ok = kill_while_init(Tab, File), - ok = open_ro(Tab, File), - ok = open_w(Tab, File, 0, Config), - ok = open_w(Tab, File, 100, Config), - ok. + case feasible() of + false -> {comment, "OK, but did not run all of the test"}; + true -> + ok = kill_while_repairing(Tab, File), + ok = kill_while_init(Tab, File), + ok = open_ro(Tab, File), + ok = open_w(Tab, File, 0, Config), + ok = open_w(Tab, File, 100, Config) + end. + +feasible() -> + LP = erlang:system_info(logical_processors), + (is_integer(LP) + andalso LP >= erlang:system_info(schedulers_online) + andalso not erlang:system_info(debug_compiled) + andalso not erlang:system_info(lock_checking)). %% One process logs and another process closes the log. Before %% monitors were used, this would make the client never return. @@ -3314,7 +3191,6 @@ kill_while_repairing(Tab, File) -> Delay = 1000, dets:start(), Parent = self(), - Ps = processes(), F = fun() -> R = (catch dets:open_file(Tab, [{file,File}])), timer:sleep(Delay), @@ -3325,7 +3201,7 @@ kill_while_repairing(Tab, File) -> P1 = spawn(F), P2 = spawn(F), P3 = spawn(F), - DetsPid = find_dets_pid([P1, P2, P3 | Ps]), + DetsPid = find_dets_pid(), exit(DetsPid, kill), receive {P1,R1} -> R1 end, @@ -3349,12 +3225,6 @@ kill_while_repairing(Tab, File) -> file:delete(File), ok. -find_dets_pid(P0) -> - case lists:sort(processes() -- P0) of - [P, _] -> P; - _ -> timer:sleep(100), find_dets_pid(P0) - end. - find_dets_pid() -> case find_dets_pids() of [] -> @@ -3428,6 +3298,13 @@ open_ro(Tab, File) -> open_w(Tab, File, Delay, Config) -> create_opened_log(File), + + Tab2 = t2, + File2 = filename(Tab2, Config), + file:delete(File2), + {ok,Tab2} = dets:open_file(Tab2, [{file,File2}]), + ok = dets:close(Tab2), + Parent = self(), F = fun() -> R = dets:open_file(Tab, [{file,File}]), @@ -3437,16 +3314,16 @@ open_w(Tab, File, Delay, Config) -> Pid1 = spawn(F), Pid2 = spawn(F), Pid3 = spawn(F), - undefined = dets:info(Tab), % is repairing now - 0 = qlen(), - Tab2 = t2, - File2 = filename(Tab2, Config), - file:delete(File2), + ok = wait_for_repair_to_start(Tab), + + %% It is assumed that it takes some time to repair the file. {ok,Tab2} = dets:open_file(Tab2, [{file,File2}]), + %% The Dets server managed to handle to open_file request. + 0 = qlen(), % still repairing + ok = dets:close(Tab2), file:delete(File2), - 0 = qlen(), % still repairing receive {Pid1,R1} -> {ok, Tab} = R1 end, receive {Pid2,R2} -> {ok, Tab} = R2 end, @@ -3463,6 +3340,15 @@ open_w(Tab, File, Delay, Config) -> file:delete(File), ok. +wait_for_repair_to_start(Tab) -> + case catch dets_server:get_pid(Tab) of + {'EXIT', _} -> + timer:sleep(1), + wait_for_repair_to_start(Tab); + Pid when is_pid(Pid) -> + ok + end. + qlen() -> {_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())), N. @@ -3476,10 +3362,7 @@ create_opened_log(File) -> crash(File, ?CLOSED_PROPERLY_POS+3, ?NOT_PROPERLY_CLOSED), ok. -insert_new(doc) -> - ["OTP-5075. insert_new/2"]; -insert_new(suite) -> - []; +%% OTP-5075. insert_new/2 insert_new(Config) -> Tab = insert_new, File = filename(Tab, Config), @@ -3507,10 +3390,7 @@ insert_new(Config) -> file:delete(File), ok. -repair_continuation(doc) -> - ["OTP-5126. repair_continuation/2"]; -repair_continuation(suite) -> - []; +%% OTP-5126. repair_continuation/2 repair_continuation(Config) -> Tab = repair_continuation_table, Fname = filename(repair_cont, Config), @@ -3533,10 +3413,7 @@ repair_continuation(Config) -> file:delete(Fname), ok. -otp_5487(doc) -> - ["OTP-5487. Growth of read-only table (again)."]; -otp_5487(suite) -> - []; +%% OTP-5487. Growth of read-only table (again). otp_5487(Config) -> otp_5487(Config, 9), otp_5487(Config, 8), @@ -3559,10 +3436,7 @@ otp_5487(Config, Version) -> ets:delete(Ets), file:delete(Fname). -otp_6206(doc) -> - ["OTP-6206. Badly formed free lists."]; -otp_6206(suite) -> - []; +%% OTP-6206. Badly formed free lists. otp_6206(Config) -> Tab = otp_6206, File = filename(Tab, Config), @@ -3581,10 +3455,7 @@ otp_6206(Config) -> file:delete(File), ok. -otp_6359(doc) -> - ["OTP-6359. select and match never return the empty list."]; -otp_6359(suite) -> - []; +%% OTP-6359. select and match never return the empty list. otp_6359(Config) -> Tab = otp_6359, File = filename(Tab, Config), @@ -3597,10 +3468,7 @@ otp_6359(Config) -> file:delete(File), ok. -otp_4738(doc) -> - ["OTP-4738. ==/2 and =:=/2."]; -otp_4738(suite) -> - []; +%% OTP-4738. ==/2 and =:=/2. otp_4738(Config) -> %% Version 8 has not been corrected. %% (The constant -12857447 is for version 9 only.) @@ -3752,10 +3620,7 @@ otp_4738_set(Version, Config) -> file:delete(File), ok. -otp_7146(doc) -> - ["OTP-7146. Bugfix: missing test when re-hashing."]; -otp_7146(suite) -> - []; +%% OTP-7146. Bugfix: missing test when re-hashing. otp_7146(Config) -> Tab = otp_7146, File = filename(Tab, Config), @@ -3778,10 +3643,7 @@ write_dets(Tab, N, Max) -> ok = dets:insert(Tab,{ N, {entry,N}}), write_dets(Tab, N+1, Max). -otp_8070(doc) -> - ["OTP-8070. Duplicated objects with insert_new() and duplicate_bag."]; -otp_8070(suite) -> - []; +%% OTP-8070. Duplicated objects with insert_new() and duplicate_bag. otp_8070(Config) when is_list(Config) -> Tab = otp_8070, File = filename(Tab, Config), @@ -3794,10 +3656,7 @@ otp_8070(Config) when is_list(Config) -> file:delete(File), ok. -otp_8856(doc) -> - ["OTP-8856. insert_new() bug."]; -otp_8856(suite) -> - []; +%% OTP-8856. insert_new() bug. otp_8856(Config) when is_list(Config) -> Tab = otp_8856, File = filename(Tab, Config), @@ -3819,10 +3678,7 @@ otp_8856(Config) when is_list(Config) -> file:delete(File), ok. -otp_8903(doc) -> - ["OTP-8903. bchunk/match/select bug."]; -otp_8903(suite) -> - []; +%% OTP-8903. bchunk/match/select bug. otp_8903(Config) when is_list(Config) -> Tab = otp_8903, File = filename(Tab, Config), @@ -3842,10 +3698,7 @@ otp_8903(Config) when is_list(Config) -> file:delete(File), ok. -otp_8923(doc) -> - ["OTP-8923. rehash due to lookup after initialization."]; -otp_8923(suite) -> - []; +%% OTP-8923. rehash due to lookup after initialization. otp_8923(Config) when is_list(Config) -> Tab = otp_8923, File = filename(Tab, Config), @@ -3875,10 +3728,7 @@ otp_8923(Config) when is_list(Config) -> file:delete(File), ok. -otp_9282(doc) -> - ["OTP-9282. The name of a table can be an arbitrary term"]; -otp_9282(suite) -> - []; +%% OTP-9282. The name of a table can be an arbitrary term. otp_9282(Config) when is_list(Config) -> some_calls(make_ref(), Config), some_calls({a,typical,name}, Config), @@ -3898,19 +3748,126 @@ some_calls(Tab, Config) -> file:delete(File). +%% OTP-11245. Tables remained fixed after traversal. +otp_11245(Config) when is_list(Config) -> + Tab = otp_11245, + File = filename(Tab, Config), + {ok, Tab} = dets:open_file(Tab, [{file,File}]), + N = 1024, + ins(Tab, N), + N = length(dets:match(Tab, '_')), + false = dets:info(Tab, safe_fixed), + dets:traverse(Tab, fun(_) -> continue end), + false = dets:info(Tab, safe_fixed), + N = dets:foldl(fun(_, N2) -> N2+1 end, 0, Tab), + false = dets:info(Tab, safe_fixed), + N = dets:foldr(fun(_, N2) -> N2+1 end, 0, Tab), + false = dets:info(Tab, safe_fixed), + ok = dets:close(Tab), + file:delete(File), + ok. + +%% OTP-11709. Bugfixes. +otp_11709(Config) when is_list(Config) -> + Short = <<"foo">>, + Long = <<"a sufficiently long text">>, + + %% Bug: leaking file descriptor + P0 = pps(), + File = filename(otp_11709, Config), + ok = file:write_file(File, Long), + false = dets:is_dets_file(File), + check_pps(P0), + + %% Bug: deleting file + Args = [[{access, A}, {repair, R}] || + A <- [read, read_write], + R <- [true, false, force]], + Fun1 = fun(S, As) -> + P1 = pps(), + ok = file:write_file(File, S), + {error,{not_a_dets_file,File}} = dets:open_file(File, As), + {ok, S} = file:read_file(File), + check_pps(P1) + end, + Fun2 = fun(S) -> + _ = [Fun1(S, As) || As <- Args], + ok + end, + ok = Fun2(Long), % no change here + ok = Fun2(Short), % mimic the behaviour for longer files + + %% open_file/1 + ok = file:write_file(File, Long), + {error,{not_a_dets_file,File}} = dets:open_file(File), % no change + ok = file:write_file(File, Short), + {error,{not_a_dets_file,File}} = dets:open_file(File), % mimic + + _ = file:delete(File), + ok. + +%% OTP-13229. open_file() exits with badarg when given binary file name. +otp_13229(_Config) -> + F = <<"binfile.tab">>, + try dets:open_file(name, [{file, F}]) of + R -> + exit({open_succeeded, R}) + catch + error:badarg -> + ok + end. + +%% OTP-13260. Race when opening a table. +otp_13260(Config) -> + [ok] = lists:usort([otp_13260_1(Config) || _ <- lists:seq(1, 3)]), + ok. + +otp_13260_1(Config) -> + Tab = otp_13260, + File = filename(Tab, Config), + N = 20, + P = self(), + Pids = [spawn_link(fun() -> counter(P, Tab, File) end) || + _ <- lists:seq(1, N)], + Rs = rec(Pids), + true = lists:all(fun(R) -> is_integer(R) end, Rs), + wait_for_close(Tab). + +rec([]) -> + []; +rec([Pid | Pids]) -> + receive {Pid, R} -> + [R | rec(Pids)] + end. + +%% One may have to run the test several times to trigger the bug. +counter(P, Tab, File) -> + Key = key, + N = case catch dets:update_counter(Tab, Key, 1) of + {'EXIT', _} -> + {ok, Tab} = dets:open_file(Tab, [{file, File}]), + ok = dets:insert(Tab, {Key, 1}), + dets:update_counter(Tab, Key, 1); + N1 when is_integer(N1) -> + N1; + DetsBug -> + DetsBug + end, + P ! {self(), N}. + +wait_for_close(Tab) -> + case dets:info(Tab, owner) of + undefined -> + ok; + _ -> + timer:sleep(100), + wait_for_close(Tab) + end. + %% %% Parts common to several test cases %% -start_node_rel(Name, Rel, How) -> - Release = [{release, atom_to_list(Rel)}], - Pa = filename:dirname(code:which(?MODULE)), - test_server:start_node(Name, How, - [{args, - " -kernel net_setuptime 100 " - " -pa " ++ Pa}, - {erl, Release}]). - crash(File, Where) -> crash(File, Where, 10). @@ -4301,7 +4258,8 @@ check_badarg({'EXIT', {badarg, [{M,F,Args,_} | _]}}, M, F, Args) -> check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args) -> true = test_server:is_native(M) andalso length(Args) =:= A. -check_pps(P0) -> +check_pps({Ports0,Procs0} = P0) -> + ok = check_dets_tables(), case pps() of P0 -> ok; @@ -4313,22 +4271,60 @@ check_pps(P0) -> case pps() of P0 -> ok; - P1 -> - io:format("failure, got ~p~n, expected ~p\n", [P1, P0]), - {Ports0,Procs0} = P0, - {Ports1,Procs1} = P1, - show("Old ports", Ports0 -- Ports1), - show("New ports", Ports1 -- Ports0), - show("Old procs", Procs0 -- Procs1), - show("New procs", Procs1 -- Procs0), - ?t:fail() - end + {Ports1,Procs1} = P1 -> + case {Ports1 -- Ports0, Procs1 -- Procs0} of + {[], []} -> ok; + {PortsDiff,ProcsDiff} -> + io:format("failure, got ~p~n, expected ~p\n", [P1, P0]), + show("Old port", Ports0 -- Ports1), + show("New port", PortsDiff), + show("Old proc", Procs0 -- Procs1), + show("New proc", ProcsDiff), + ct:fail(failed) + end + end + end. + +%% Copied from dets_server.erl: +-define(REGISTRY, dets_registry). +-define(OWNERS, dets_owners). +-define(STORE, dets). + +check_dets_tables() -> + Store = [T || + T <- ets:all(), + ets:info(T, name) =:= ?STORE, + owner(T) =:= dets], + S = case Store of + [Tab] -> ets:tab2list(Tab); + [] -> [] + end, + case {ets:tab2list(?REGISTRY), ets:tab2list(?OWNERS), S} of + {[], [], []} -> ok; + {R, O, _} -> + io:format("Registry: ~p~n", [R]), + io:format("Owners: ~p~n", [O]), + io:format("Store: ~p~n", [S]), + not_ok + end. + +owner(Tab) -> + Owner = ets:info(Tab, owner), + case process_info(Owner, registered_name) of + {registered_name, Name} -> Name; + _ -> Owner end. show(_S, []) -> ok; -show(S, L) -> - io:format("~s: ~p~n", [S, L]). +show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) -> + io:format("~s: ~w (~w), ~w: ~p~n", + [S, Pid, proc_reg_name(Name), InitCall, + erlang:process_info(Pid)]), + show(S, Pids); +show(S, [{Port, _}|Ports]) when is_port(Port)-> + io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]), + show(S, Ports). pps() -> dets:start(), @@ -4343,5 +4339,8 @@ process_list() -> safe_second_element(process_info(P, initial_call))} || P <- processes()]. +proc_reg_name({registered_name, Name}) -> Name; +proc_reg_name([]) -> no_reg_name. + safe_second_element({_,Info}) -> Info; safe_second_element(Other) -> Other. |