1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2001-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(cover_SUITE). 21 22-compile(export_all). 23 24-include_lib("common_test/include/ct.hrl"). 25 26suite() -> 27 [{ct_hooks,[ts_install_cth]}, 28 {timetrap,{minutes,5}}]. 29 30all() -> 31 NoStartStop = [eif,otp_5305,otp_5418,otp_7095,otp_8273, 32 otp_8340,otp_8188,compile_beam_opts,eep37, 33 analyse_no_beam, line_0, compile_beam_no_file, 34 compile_beam_missing_backend, 35 otp_13277, otp_13289], 36 StartStop = [start, compile, analyse, misc, stop, 37 distribution, reconnect, die_and_reconnect, 38 dont_reconnect_after_stop, stop_node_after_disconnect, 39 export_import, otp_5031, otp_6115, 40 otp_8270, otp_10979_hanging_node, otp_14817, 41 local_only, startup_race, otp_16476, cover_clauses], 42 case whereis(cover_server) of 43 undefined -> 44 [coverage,StartStop ++ NoStartStop]; 45 _pid -> 46 [coverage|NoStartStop++[coverage_analysis]] 47 end. 48 49init_per_suite(Config) -> 50 [{ct_is_running_cover,whereis(cover_server) =/= undefined}|Config]. 51 52end_per_suite(_Config) -> 53 ok. 54 55init_per_testcase(TC, Config) when TC =:= misc; 56 TC =:= compile; 57 TC =:= analyse; 58 TC =:= distribution; 59 TC =:= otp_5031; 60 TC =:= stop -> 61 case code:which(crypto) of 62 Path when is_list(Path) -> 63 init_per_testcase(dummy_tc, Config); 64 _Else -> 65 {skip, "No crypto file to test with"} 66 end; 67init_per_testcase(_TestCase, Config) -> 68 Config. 69 70end_per_testcase(TestCase, Config) -> 71 NoStop = [start,compile,analyse,misc], 72 DontStop = proplists:get_bool(ct_is_running_cover, Config) orelse 73 lists:member(TestCase, NoStop), 74 case DontStop of 75 true -> ok; 76 false -> cover:stop() 77 end, 78 ok. 79 80coverage(Config) when is_list(Config) -> 81 {ok,?MODULE} = cover:compile_beam(?MODULE), 82 ?MODULE:do_coverage(Config). 83 84do_coverage(Config) -> 85 Outdir = proplists:get_value(priv_dir, Config), 86 ExportFile = filename:join(Outdir, "export"), 87 ok = cover:export(ExportFile, ?MODULE), 88 {error,{already_started,_}} = cover:start(), 89 {error,_} = cover:compile_beam(non_existing_module), 90 _ = cover:which_nodes(), 91 _ = cover:modules(), 92 _ = cover:imported(), 93 {error,{not_cover_compiled,lists}} = cover:analyze(lists), 94 95 %% Cover escaping of '&' in HTML files. 96 97 case proplists:get_bool(ct_is_running_cover, Config) of 98 false -> 99 %% Cover server was implicitly started when this module 100 %% was cover-compiled. We must stop the cover server, but 101 %% we must ensure that this module is not on the call 102 %% stack when it is unloaded. Therefore, the call that 103 %% follows MUST be tail-recursive. 104 cover:stop(); 105 true -> 106 %% Cover server was started by common_test; don't stop it. 107 ok 108 end. 109 110%% This test case will only be run when common_test is running cover. 111coverage_analysis(Config) when is_list(Config) -> 112 {ok,Analysis1} = cover:analyze(?MODULE), 113 io:format("~p\n", [Analysis1]), 114 {ok,Analysis2} = cover:analyze(?MODULE, calls), 115 io:format("~p\n", [Analysis2]), 116 {ok,_Analysis3} = cover:analyze(?MODULE, calls, line), 117 118 Outdir = proplists:get_value(priv_dir, Config), 119 Outfile = filename:join(Outdir, ?MODULE), 120 121 {ok,Outfile} = cover:analyze_to_file(?MODULE, Outfile), 122 {ok,Contents} = file:read_file(Outfile), 123 ok = file:delete(Outfile), 124 ok = io:put_chars(Contents), 125 {ok,Outfile} = cover:analyze_to_file(?MODULE, Outfile, [html]), 126 ok. 127 128start(Config) when is_list(Config) -> 129 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 130 131 Files = lsfiles(), 132 remove(files(Files, ".out")), 133 134 {ok, Pid} = cover:start(), 135 {error, {already_started, Pid}} = cover:start(). 136 137compile(Config) when is_list(Config) -> 138 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 139 140 Result1 = cover:compile_directory(), 141 SortedResult = lists:sort(Result1), 142 {ok, CWD} = file:get_cwd(), 143 Result2 = cover:compile_directory(CWD), 144 SortedResult = lists:sort(Result2), 145 [{error,DFile},{ok,a},{ok,b},{ok,cc},{ok,f}] = SortedResult, 146 [{ok,e}] = cover:compile_directory("d1"), 147 {error,enoent} = cover:compile_directory("d2"), 148 149 [] = cover:compile([]), 150 Result21 = cover:compile([a,b,"cc.erl",d,"f"]), 151 SortedResult21 = lists:sort(Result21), 152 [{error,DFile},{ok,a},{ok,b},{ok,cc},{ok,f}] = SortedResult21, 153 154 {ok,a} = cover:compile(a), 155 {ok,b} = compile:file(b), 156 code:purge(b), 157 {module,b} = code:load_file(b), 158 {ok,d} = cover:compile("d.erl", [{d,'AGE',42}]), 159 {error,_BBFile} = cover:compile(bb), 160 161 StdlibDir = code:lib_dir(stdlib), 162 Lists = filename:join([StdlibDir, "src", "lists.erl"]), 163 {error, Lists} = cover:compile(Lists), 164 165 %% For compiling beam: using dummy files v,w,x,y and z 166 file:set_cwd("compile_beam"), 167 {ok,_} = compile:file(v,[debug_info,report]), 168 {ok,_} = compile:file(w,[debug_info,report]), 169 {ok,_} = compile:file(x), 170 {ok,_} = compile:file("d/y",[debug_info,{outdir,"d"},report]), 171 Key = "A Krypto Key", 172 CryptoWorks = crypto_works(), 173 case CryptoWorks of 174 false -> 175 {ok,_} = compile:file(crypt, [debug_info,report]), 176 {ok,crypt} = cover:compile_beam("crypt.beam"); 177 true -> 178 {ok,_} = compile:file(crypt, [{debug_info_key,Key},report]), 179 {error,{encrypted_abstract_code,_}} = 180 cover:compile_beam("crypt.beam"), 181 ok = beam_lib:crypto_key_fun(simple_crypto_fun(Key)), 182 {ok,crypt} = cover:compile_beam("crypt.beam") 183 end, 184 Path = filename:join([proplists:get_value(data_dir, Config), "compile_beam", "v.erl"]), 185 {ok,v} = cover:compile_beam(v), 186 {source,Path} = lists:keyfind(source, 1, v:module_info(compile)), 187 {ok,w} = cover:compile_beam("w.beam"), 188 {error,{no_abstract_code,"./x.beam"}} = cover:compile_beam(x), 189 {error,{already_cover_compiled,no_beam_found,a}}=cover:compile_beam(a), 190 {error,non_existing} = cover:compile_beam(z), 191 [{ok,y}] = cover:compile_beam_directory("d"), 192 Result3 = lists:sort(cover:compile_beam_directory()), 193 [{error,{no_abstract_code,XBeam}},{ok,crypt},{ok,v},{ok,w}] = Result3, 194 {error,enoent} = cover:compile_beam_directory("d2"), 195 196 [] = cover:compile_beam([]), 197 Result31 = cover:compile_beam([crypt,"v.beam",w,"x"]), 198 SortedResult31 = lists:sort(Result31), 199 [{error,{no_abstract_code,XBeam}},{ok,crypt},{ok,v},{ok,w}] = SortedResult31, 200 201 decompile([v,w,y]), 202 Files = lsfiles(), 203 remove(files(Files, ".beam")). 204 205crypto_works() -> 206 try crypto:start() of 207 {error,{already_started,crypto}} -> true; 208 ok -> true 209 catch 210 error:_ -> 211 false 212 end. 213 214simple_crypto_fun(Key) -> 215 fun(init) -> ok; 216 ({debug_info, des3_cbc, crypt, _}) -> Key 217 end. 218 219analyse(Config) when is_list(Config) -> 220 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 221 222 done = a:start(5), 223 224 {ok, {a,{17,2}}=ACovMod} = cover:analyse(a, coverage, module), 225 {ok, [{{a,exit_kalle,0},{1,0}}, 226 {{a,loop,3},{5,1}}, 227 {{a,pong,1},{1,0}}, 228 {{a,start,1},{6,0}}, 229 {{a,stop,1},{0,1}}, 230 {{a,trycatch,1},{4,0}}]=ACovFunc} = 231 cover:analyse(a, coverage, function), 232 {ok, [{{a,exit_kalle,0,1},{1,0}}, 233 {{a,loop,3,1},{3,1}}, 234 {{a,loop,3,2},{2,0}}, 235 {{a,pong,1,1},{1,0}}, 236 {{a,start,1,1},{6,0}}, 237 {{a,stop,1,1},{0,1}}, 238 {{a,trycatch,1,1},{4,0}}]=ACovClause} = 239 cover:analyse(a, coverage, clause), 240 {ok, [{{a,9},{1,0}}, 241 {{a,10},{1,0}}, 242 {{a,11},{1,0}}, 243 {{a,13},{1,0}}, 244 {{a,14},{1,0}}, 245 {{a,15},{1,0}}, 246 {{a,21},{0,1}}, 247 {{a,26},{1,0}}, 248 {{a,31},{1,0}}, 249 {{a,32},{1,0}}, 250 {{a,34},{1,0}}, 251 {{a,36},{0,1}}, 252 {{a,39},{1,0}}, 253 {{a,40},{1,0}}, 254 {{a,44},{1,0}}, 255 {{a,47},{1,0}}, 256 {{a,49},{1,0}}, 257 {{a,51},{1,0}}, 258 {{a,55},{1,0}}]=ACovLine} = cover:analyse(a, coverage, line), 259 260 {ok, {a,15}=ACallsMod} = cover:analyse(a, calls, module), 261 {ok, [{{a,exit_kalle,0},1}, 262 {{a,loop,3},6}, 263 {{a,pong,1},5}, 264 {{a,start,1},1}, 265 {{a,stop,1},0}, 266 {{a,trycatch,1},2}]=ACallsFunc} = cover:analyse(a, calls, function), 267 {ok, [{{a,exit_kalle,0,1},1}, 268 {{a,loop,3,1},5}, 269 {{a,loop,3,2},1}, 270 {{a,pong,1,1},5}, 271 {{a,start,1,1},1}, 272 {{a,stop,1,1},0}, 273 {{a,trycatch,1,1},2}]=ACallsClause} = cover:analyse(a, calls, clause), 274 {ok, [{{a,9},1}, 275 {{a,10},1}, 276 {{a,11},1}, 277 {{a,13},1}, 278 {{a,14},1}, 279 {{a,15},1}, 280 {{a,21},0}, 281 {{a,26},5}, 282 {{a,31},5}, 283 {{a,32},5}, 284 {{a,34},5}, 285 {{a,36},0}, 286 {{a,39},1}, 287 {{a,40},1}, 288 {{a,44},2}, 289 {{a,47},1}, 290 {{a,49},1}, 291 {{a,51},2}, 292 {{a,55},1}]=ACallsLine} = cover:analyse(a, calls, line), 293 294 {ok,ACovFunc} = cover:analyse(a), 295 {ok,ACovMod} = cover:analyse(a, module), 296 {ok,ACallsFunc} = cover:analyse(a, calls), 297 298 {ok, "a.COVER.out"} = cover:analyse_to_file(a), 299 {ok, "e.COVER.out"} = cover:analyse_to_file(e), 300 {ok, "a.COVER.html"} = cover:analyse_to_file(a,[html]), 301 {ok, "e.COVER.html"} = cover:analyse_to_file(e,[html]), 302 303 %% Analyse all modules 304 Modules = cover:modules(), 305 N = length(Modules), 306 307 {result,CovFunc,[]} = cover:analyse(), % default = coverage, function 308 ACovFunc = [A || {{a,_,_},_}=A<-CovFunc], 309 310 {result,CovMod,[]} = cover:analyse(coverage,module), 311 ACovMod = lists:keyfind(a,1,CovMod), 312 313 {result,CovClause,[]} = cover:analyse(coverage,clause), 314 ACovClause = [A || {{a,_,_,_},_}=A<-CovClause], 315 316 {result,CovLine,[]} = cover:analyse(coverage,line), 317 ACovLine = [A || {{a,_},_}=A<-CovLine], 318 319 {result,CallsFunc,[]} = cover:analyse(calls,function), 320 ACallsFunc = [A || {{a,_,_},_}=A<-CallsFunc], 321 322 {result,CallsMod,[]} = cover:analyse(calls,module), 323 ACallsMod = lists:keyfind(a,1,CallsMod), 324 325 {result,CallsClause,[]} = cover:analyse(calls,clause), 326 ACallsClause = [A || {{a,_,_,_},_}=A<-CallsClause], 327 328 {result,CallsLine,[]} = cover:analyse(calls,line), 329 ACallsLine = [A || {{a,_},_}=A<-CallsLine], 330 331 {result,AllToFile,[]} = cover:analyse_to_file(), 332 N = length(AllToFile), 333 true = lists:member("a.COVER.out",AllToFile), 334 {result,AllToFileHtml,[]} = cover:analyse_to_file([html]), 335 N = length(AllToFileHtml), 336 true = lists:member("a.COVER.html",AllToFileHtml), 337 338 %% Analyse list of modules 339 %% Listing all modules so we can compare result with above result 340 %% from analysing all. 341 342 {result,CovFunc1,[]} = cover:analyse(Modules), % default = coverage, function 343 true = lists:sort(CovFunc) == lists:sort(CovFunc1), 344 345 {result,CovMod1,[]} = cover:analyse(Modules,coverage,module), 346 true = lists:sort(CovMod) == lists:sort(CovMod1), 347 348 {result,CovClause1,[]} = cover:analyse(Modules,coverage,clause), 349 true = lists:sort(CovClause) == lists:sort(CovClause1), 350 351 {result,CovLine1,[]} = cover:analyse(Modules,coverage,line), 352 true = lists:sort(CovLine) == lists:sort(CovLine1), 353 354 {result,CallsFunc1,[]} = cover:analyse(Modules,calls,function), 355 true = lists:sort(CallsFunc1) == lists:sort(CallsFunc1), 356 357 {result,CallsMod1,[]} = cover:analyse(Modules,calls,module), 358 true = lists:sort(CallsMod) == lists:sort(CallsMod1), 359 360 {result,CallsClause1,[]} = cover:analyse(Modules,calls,clause), 361 true = lists:sort(CallsClause) == lists:sort(CallsClause1), 362 363 {result,CallsLine1,[]} = cover:analyse(Modules,calls,line), 364 true = lists:sort(CallsLine) == lists:sort(CallsLine1), 365 366 {result,AllToFile1,[]} = cover:analyse_to_file(Modules), 367 true = lists:sort(AllToFile) == lists:sort(AllToFile1), 368 {result,AllToFileHtml1,[]} = cover:analyse_to_file(Modules,[html]), 369 true = lists:sort(AllToFileHtml) == lists:sort(AllToFileHtml1), 370 371 %% analyse_to_file of file which is compiled from beam 372 {ok,f} = compile:file(f,[debug_info]), 373 code:purge(f), 374 {module,f} = code:load_file(f), 375 {ok,f} = cover:compile_beam(f), 376 f:f2(), 377 {ok, "f.COVER.out"} = cover:analyse_to_file(f), 378 379 %% Source code can be found via source 380 {ok,v} = compile:file("compile_beam/v",[debug_info]), 381 code:purge(v), 382 {module,v} = code:load_file(v), 383 {ok,v} = cover:compile_beam(v), 384 {ok,"v.COVER.out"} = cover:analyse_to_file(v), 385 386 %% Source code cannot be found 387 {ok,_} = file:copy("compile_beam/z.erl", "z.erl"), 388 {ok,z} = compile:file(z,[debug_info]), 389 code:purge(z), 390 {module,z} = code:load_file(z), 391 {ok,z} = cover:compile_beam(z), 392 ok = file:delete("z.erl"), 393 {error,{no_source_code_found,z}} = cover:analyse_to_file(z), 394 {result,[],[{no_source_code_found,z}]} = cover:analyse_to_file([z]), 395 code:purge(z), 396 code:delete(z), 397 398 {error,{not_cover_compiled,b}} = cover:analyse(b), 399 {error,{not_cover_compiled,g}} = cover:analyse(g), 400 {result,[],[{not_cover_compiled,b}]} = cover:analyse([b]), 401 {error,{not_cover_compiled,b}} = cover:analyse_to_file(b), 402 {error,{not_cover_compiled,g}} = cover:analyse_to_file(g), 403 {result,[],[{not_cover_compiled,g}]} = cover:analyse_to_file([g]). 404 405misc(Config) when is_list(Config) -> 406 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 407 408 [a,cc,crypt,d,e,f,v] = lists:sort(cover:modules()), 409 410 {ok,cc} = compile:file(cc), 411 code:purge(cc), 412 {module,cc} = code:load_file(cc), 413 [a,crypt,d,e,f,v] = lists:sort(cover:modules()), 414 415 {file, _File} = cover:is_compiled(a), 416 false = cover:is_compiled(b), 417 false = cover:is_compiled(g), 418 419 ok = cover:reset(a), 420 {ok, {a,{0,19}}} = cover:analyse(a, module), 421 ok = cover:reset(). 422 423stop(Config) when is_list(Config) -> 424 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 425 426 cover_compiled = code:which(a), 427 {ok,d} = compile:file(d, [{d,'AGE',42}]), 428 code:purge(d), 429 {module,d} = code:load_file(d), 430 ok = cover:stop(), 431 Beam = code:which(a), 432 true = is_unloaded(Beam), 433 434 Files = lsfiles(), 435 remove(files(Files, ".out")), 436 remove(files(Files, ".html")), 437 remove(files(Files, ".beam")). 438 439distribution(Config) when is_list(Config) -> 440 DataDir = proplists:get_value(data_dir, Config), 441 ok = file:set_cwd(DataDir), 442 443 {ok,N1} = test_server:start_node(cover_SUITE_distribution1,slave,[]), 444 {ok,N2} = test_server:start_node(cover_SUITE_distribution2,slave,[]), 445 {ok,N3} = test_server:start_node(cover_SUITE_distribution3,slave,[]), 446 {ok,N4} = test_server:start_node(cover_SUITE_distribution4,slave,[]), 447 448 %% Check that an already compiled module is loaded on new nodes 449 {ok,f} = cover:compile(f), 450 {ok,[_,_,_,_]} = cover:start(nodes()), 451 cover_compiled = code:which(f), 452 cover_compiled = rpc:call(N1,code,which,[f]), 453 cover_compiled = rpc:call(N2,code,which,[f]), 454 cover_compiled = rpc:call(N3,code,which,[f]), 455 cover_compiled = rpc:call(N4,code,which,[f]), 456 457 %% Check that a node cannot be started twice 458 {ok,[]} = cover:start(N2), 459 460 %% Check that the current node (i.e. the main node) is not started with 461 %% start/1 and not stopped with stop/1 462 {ok,[]} = cover:start(node()), 463 ok = cover:stop(node()), 464 true = is_pid(whereis(cover_server)), 465 466 %% Check that a new compiled module is loaded on all existing nodes 467 compile:file("compile_beam/v",[debug_info]), 468 {ok,v} = cover:compile_beam(v), 469 cover_compiled = code:which(v), 470 cover_compiled = rpc:call(N1,code,which,[v]), 471 cover_compiled = rpc:call(N2,code,which,[v]), 472 cover_compiled = rpc:call(N3,code,which,[v]), 473 cover_compiled = rpc:call(N4,code,which,[v]), 474 475 %% this is lost when the node is killed 476 rpc:call(N3,f,f2,[]), 477 rpc:call(N3,erlang,halt,[]), 478 479 %% this should be visible in analyse 480 rpc:call(N1,f,f1,[]), 481 482 %% Check that data is collected from remote node when stopped 483 ok = cover:stop(N1), 484 N1Beam = rpc:call(N1,code,which,[f]), 485 true = is_unloaded(N1Beam), 486 check_f_calls(1,0), 487 488 %% Call f:f1() again on another node and check that number of calls is 489 %% accumulated. 490 f:f1(), 491 check_f_calls(2,0), 492 493 %% Check that reset works on all nodes 494 f:f1(), 495 rpc:call(N2,f,f1,[]), 496 ok = cover:reset(f), 497 check_f_calls(0,0), 498 499 %% Check that data is collected from all nodes 500 rpc:call(N2,f,f1,[]), 501 f:f2(), 502 check_f_calls(1,1), 503 504 %% Check that same data is not fetched again (i.e. that analyse does 505 %% reset on the remote node(s)) 506 check_f_calls(1,1), 507 508 %% Another checn that data is not fetched twice, i.e. when flushed 509 %% then analyse should not add the same data again. 510 rpc:call(N4,f,f2,[]), 511 ok = cover:flush(N4), 512 check_f_calls(1,2), 513 514 %% Check that flush collects data so calls are not lost if node is killed 515 rpc:call(N4,f,f2,[]), 516 ok = cover:flush(N4), 517 rpc:call(N4,erlang,halt,[]), 518 check_f_calls(1,3), 519 520 %% Check that stop() unloads on all nodes 521 ok = cover:stop(), 522 timer:sleep(100), %% Give nodes time to unload on slow machines. 523 LocalBeam = code:which(f), 524 N2Beam = rpc:call(N2,code,which,[f]), 525 true = is_unloaded(LocalBeam), 526 true = is_unloaded(N2Beam), 527 528 %% Check that cover_server on remote node does not die if main node dies 529 {ok,[N1]} = cover:start(N1), 530 true = is_pid(N1Server = rpc:call(N1,erlang,whereis,[cover_server])), 531 exit(whereis(cover_server),kill), 532 timer:sleep(100), 533 N1Server = rpc:call(N1,erlang,whereis,[cover_server]), 534 535 %% Cleanup 536 Files = lsfiles(), 537 remove(files(Files, ".beam")), 538 test_server:stop_node(N1), 539 test_server:stop_node(N2). 540 541%% Test that a lost node is reconnected 542reconnect(Config) -> 543 DataDir = proplists:get_value(data_dir, Config), 544 ok = file:set_cwd(DataDir), 545 546 {ok,a} = compile:file(a), 547 {ok,b} = compile:file(b), 548 {ok,f} = compile:file(f), 549 550 {ok,N1} = test_server:start_node(cover_SUITE_reconnect,peer, 551 [{args," -pa " ++ DataDir}, 552 {start_cover,false}]), 553 {ok,a} = cover:compile(a), 554 {ok,f} = cover:compile(f), 555 {ok,[N1]} = cover:start(nodes()), 556 557 %% Some calls to check later 558 rpc:call(N1,f,f1,[]), 559 cover:flush(N1), 560 rpc:call(N1,f,f1,[]), 561 562 %% This will cause first casue the N1 node to initiate a 563 %% disconnect and then call f:f2() when nodes() =:= [] on N1. 564 rpc:cast(N1,f,call_f2_when_isolated,[]), 565 timer:sleep(500), % allow some to detect disconnect and for f:f2() call 566 cover_which_nodes([]), 567 568 %% Do some add one module (b) and remove one module (a) 569 code:purge(a), 570 {module,a} = code:load_file(a), 571 {ok,b} = cover:compile(b), 572 cover_compiled = code:which(b), 573 574 cover_which_nodes([]), 575 check_f_calls(1,0), % only the first call - before the flush 576 577 %% Reconnect the node and check that b and f are cover compiled but not a 578 net_kernel:connect_node(N1), 579 timer:sleep(100), 580 [N1] = cover:which_nodes(), % we are reconnected 581 cover_compiled = rpc:call(N1,code,which,[b]), 582 cover_compiled = rpc:call(N1,code,which,[f]), 583 ABeam = rpc:call(N1,code,which,[a]), 584 false = (cover_compiled==ABeam), 585 586 %% Ensure that we have: 587 %% * one f1 call from before the flush, 588 %% * one f1 call from after the flush but before disconnect 589 %% * one f2 call when disconnected 590 check_f_calls(2,1), 591 592 cover:stop(), 593 test_server:stop_node(N1), 594 ok. 595 596%% Test that a lost node is reconnected - also if it has been dead 597die_and_reconnect(Config) -> 598 DataDir = proplists:get_value(data_dir, Config), 599 ok = file:set_cwd(DataDir), 600 601 {ok,f} = compile:file(f), 602 603 NodeName = cover_SUITE_die_and_reconnect, 604 {ok,N1} = test_server:start_node(NodeName,peer, 605 [{args," -pa " ++ DataDir}, 606 {start_cover,false}]), 607 %% {ok,a} = cover:compile(a), 608 {ok,f} = cover:compile(f), 609 {ok,[N1]} = cover:start(nodes()), 610 611 %% Some calls to check later 612 rpc:call(N1,f,f1,[]), 613 cover:flush(N1), 614 rpc:call(N1,f,f1,[]), 615 616 %% Kill the node 617 rpc:call(N1,erlang,halt,[]), 618 cover_which_nodes([]), 619 620 check_f_calls(1,0), % only the first call - before the flush 621 622 %% Restart the node and check that cover reconnects 623 {ok,N1} = test_server:start_node(NodeName,peer, 624 [{args," -pa " ++ DataDir}, 625 {start_cover,false}]), 626 timer:sleep(100), 627 [N1] = cover:which_nodes(), % we are reconnected 628 cover_compiled = rpc:call(N1,code,which,[f]), 629 630 %% One more call... 631 rpc:call(N1,f,f1,[]), 632 633 %% Ensure that no more calls are counted 634 check_f_calls(2,0), 635 636 cover:stop(), 637 test_server:stop_node(N1), 638 ok. 639 640%% Test that a stopped node is not marked as lost, i.e. that it is not 641%% reconnected if it is restarted (OTP-10638) 642dont_reconnect_after_stop(Config) -> 643 DataDir = proplists:get_value(data_dir, Config), 644 ok = file:set_cwd(DataDir), 645 646 {ok,f} = compile:file(f), 647 648 NodeName = cover_SUITE_dont_reconnect_after_stop, 649 {ok,N1} = test_server:start_node(NodeName,peer, 650 [{args," -pa " ++ DataDir}, 651 {start_cover,false}]), 652 {ok,f} = cover:compile(f), 653 {ok,[N1]} = cover:start(nodes()), 654 655 %% A call to check later 656 rpc:call(N1,f,f1,[]), 657 658 %% Stop cover on the node, then terminate the node 659 cover:stop(N1), 660 rpc:call(N1,erlang,halt,[]), 661 cover_which_nodes([]), 662 663 check_f_calls(1,0), 664 665 %% Restart the node and check that cover does not reconnect 666 {ok,N1} = test_server:start_node(NodeName,peer, 667 [{args," -pa " ++ DataDir}, 668 {start_cover,false}]), 669 timer:sleep(300), 670 cover_which_nodes([]), 671 Beam = rpc:call(N1,code,which,[f]), 672 false = (Beam==cover_compiled), 673 674 %% One more call... 675 rpc:call(N1,f,f1,[]), 676 cover:flush(N1), 677 678 %% Ensure that the last call is not counted 679 check_f_calls(1,0), 680 681 cover:stop(), 682 test_server:stop_node(N1), 683 ok. 684 685%% Test that a node which is stopped while it is marked as lost is not 686%% reconnected if it is restarted (OTP-10638) 687stop_node_after_disconnect(Config) -> 688 DataDir = proplists:get_value(data_dir, Config), 689 ok = file:set_cwd(DataDir), 690 691 {ok,f} = compile:file(f), 692 693 NodeName = cover_SUITE_stop_node_after_disconnect, 694 {ok,N1} = test_server:start_node(NodeName,peer, 695 [{args," -pa " ++ DataDir}, 696 {start_cover,false}]), 697 {ok,f} = cover:compile(f), 698 {ok,[N1]} = cover:start(nodes()), 699 700 %% A call to check later 701 rpc:call(N1,f,f1,[]), 702 703 %% Flush the node, then terminate the node to make it marked as lost 704 cover:flush(N1), 705 rpc:call(N1,erlang,halt,[]), 706 707 check_f_calls(1,0), 708 709 %% Stop cover on node 710 cover:stop(N1), 711 712 %% Restart the node and check that cover does not reconnect 713 {ok,N1} = test_server:start_node(NodeName,peer, 714 [{args," -pa " ++ DataDir}, 715 {start_cover,false}]), 716 timer:sleep(300), 717 cover_which_nodes([]), 718 Beam = rpc:call(N1,code,which,[f]), 719 false = (Beam==cover_compiled), 720 721 %% One more call... 722 rpc:call(N1,f,f1,[]), 723 cover:flush(N1), 724 725 %% Ensure that the last call is not counted 726 check_f_calls(1,0), 727 728 cover:stop(), 729 test_server:stop_node(N1), 730 ok. 731 732distribution_performance(Config) -> 733 PrivDir = proplists:get_value(priv_dir,Config), 734 Dir = filename:join(PrivDir,"distribution_performance"), 735 AllFiles = filename:join(Dir,"*"), 736 ok = filelib:ensure_dir(AllFiles), 737 code:add_patha(Dir), 738 M = 9, % Generate M modules 739 F = 210, % with F functions 740 C = 10, % and each function of C clauses 741 Mods = generate_modules(M,F,C,Dir), 742 743 % test_server:break(""), 744 745 NodeName = cover_SUITE_distribution_performance, 746 {ok,N1} = test_server:start_node(NodeName,peer,[{start_cover,false}]), 747 %% CFun = fun() -> 748 %% [{ok,_} = cover:compile_beam(Mod) || Mod <- Mods] 749 %% end, 750 CFun = fun() -> cover:compile_beam(Mods) end, 751 {CT,_CA} = timer:tc(CFun), 752 % erlang:display(_CA), 753 erlang:display({compile,CT}), 754 755 {SNT,_} = timer:tc(fun() -> {ok,[N1]} = cover:start(nodes()) end), 756 erlang:display({start_node,SNT}), 757 758 [1 = rpc:call(N1,Mod,f1,[1]) || Mod <- Mods], 759 760 % Fun = fun() -> [cover:analyse(Mod,calls,function) || Mod<-Mods] end, 761 % Fun = fun() -> analyse_all(Mods,calls,function) end, 762 % Fun = fun() -> cover:analyse('_',calls,function) end, 763 Fun = fun() -> cover:analyse(Mods,calls,function) end, 764 765 % Fun = fun() -> [begin cover:analyse_to_file(Mod,[html]) end || Mod<-Mods] end, 766 % Fun = fun() -> analyse_all_to_file(Mods,[html]) end, 767 % Fun = fun() -> cover:analyse_to_file(Mods,[html]) end, 768 % Fun = fun() -> cover:analyse_to_file([html]) end, 769 770 % Fun = fun() -> cover:reset() end, 771 772 {AT,_A} = timer:tc(Fun), 773 erlang:display({analyse,AT}), 774 % erlang:display(lists:sort([X || X={_MFA,N} <- lists:append([L || {ok,L}<-A]), N=/=0])), 775 776 %% fprof:apply(Fun, [],[{procs,[whereis(cover_server)]}]), 777 %% fprof:profile(), 778 %% fprof:analyse(dest,[]), 779 780 {SNT2,_} = timer:tc(fun() -> test_server:stop_node(N1) end), 781 erlang:display({stop_node,SNT2}), 782 783 code:del_path(Dir), 784 Files = filelib:wildcard(AllFiles), 785 [ok = file:delete(File) || File <- Files], 786 ok = file:del_dir(Dir), 787 ok. 788 789%% Run analysis in parallel 790analyse_all(Mods,Analysis,Level) -> 791 Pids = [begin 792 Pid = spawn(fun() -> 793 {ok,A} = cover:analyse(Mod,Analysis,Level), 794 exit(A) 795 end), 796 erlang:monitor(process,Pid), 797 Pid 798 end || Mod <- Mods], 799 get_downs(Pids,[]). 800 801analyse_all_to_file(Mods,Opts) -> 802 Pids = [begin 803 Pid = cover:async_analyse_to_file(Mod,Opts), 804 erlang:monitor(process,Pid), 805 Pid 806 end || Mod <- Mods], 807 get_downs(Pids,[]). 808 809get_downs([],Acc) -> 810 Acc; 811get_downs(Pids,Acc) -> 812 receive 813 {'DOWN', _Ref, _Type, Pid, A} -> 814 get_downs(lists:delete(Pid,Pids),[A|Acc]) 815 end. 816 817generate_modules(0,_,_,_) -> 818 []; 819generate_modules(M,F,C,Dir) -> 820 ModStr = "m" ++ integer_to_list(M), 821 Mod = list_to_atom(ModStr), 822 Src = ["-module(",ModStr,").\n" 823 "-compile(export_all).\n" | 824 generate_functions(F,C)], 825 Erl = filename:join(Dir,ModStr++".erl"), 826 ok = file:write_file(Erl,Src), 827 {ok,Mod} = compile:file(Erl,[{outdir,Dir},debug_info,report]), 828 [Mod | generate_modules(M-1,F,C,Dir)]. 829 830generate_functions(0,_) -> 831 []; 832generate_functions(F,C) -> 833 Func = "f" ++ integer_to_list(F), 834 [generate_clauses(C,Func) | generate_functions(F-1,C)]. 835 836generate_clauses(0,_) -> 837 []; 838generate_clauses(C,Func) -> 839 CStr = integer_to_list(C), 840 Sep = if C==1 -> "."; true -> ";" end, 841 [Func,"(",CStr,") -> ",CStr,Sep,"\n" | 842 generate_clauses(C-1,Func)]. 843 844 845export_import(Config) when is_list(Config) -> 846 DataDir = proplists:get_value(data_dir, Config), 847 ok = file:set_cwd(DataDir), 848 PortCount = length(erlang:ports()), 849 850 %% Export one module 851 {ok,f} = cover:compile(f), 852 f:f1(), 853 %% check that no info is written about where data comes from when no 854 %% files are imported 855 test_server:capture_start(), 856 check_f_calls(1,0), 857 [] = test_server:capture_get(), 858 test_server:capture_stop(), 859 ok = cover:export("f_exported",f), 860 check_f_calls(1,0), 861 ok = cover:stop(), 862 863 %% Check that same data exists after import and that info is written about 864 %% data comming from imported file 865 ok = cover:import("f_exported"), 866 test_server:capture_start(), 867 check_f_calls(1,0), 868 [Text1] = test_server:capture_get(), 869 "Analysis includes data from imported files"++_ = lists:flatten(Text1), 870 test_server:capture_stop(), 871 872 %% Export all modules 873 {ok,a} = cover:compile(a), 874 test_server:capture_start(), 875 ok = cover:export("all_exported"), 876 [] = test_server:capture_get(), 877 % "Export includes data from imported files"++_ = lists:flatten(Text2), 878 test_server:capture_stop(), 879 ok = cover:stop(), 880 ok = cover:import("all_exported"), 881 check_f_calls(1,0), 882 883 %% Check that data is reset when module is compiled again, and that 884 %% warning is written when data is deleted for imported module. 885 test_server:capture_start(), 886 {ok,f} = cover:compile(f), 887 timer:sleep(10), % capture needs some time 888 [Text3] = test_server:capture_get(), 889 "WARNING: Deleting data for module f imported from" ++ _ = lists:flatten(Text3), 890 test_server:capture_stop(), 891 check_f_calls(0,0), 892 893 %% Check that data is summed up when first compiled and then imported 894 %% The module which has been compiled (f) is loaded from the file 895 %% all_exported again (since it has been reset during cover compiling), 896 %% but the other module (a) is not loaded since it is already loaded 897 f:f1(), 898 f:f2(), 899 ok = cover:import("f_exported"), 900 test_server:capture_start(), 901 ok = cover:import("all_exported"), 902 [Text4] = test_server:capture_get(), % a is not loaded again 903 "WARNING: Module a already imported from " ++ _ = lists:flatten(Text4), 904 test_server:capture_stop(), 905 check_f_calls(3,1), 906 907 %% Check that warning is written when same file is imported twice, 908 %% and that data is not imported again 909 test_server:capture_start(), 910 ok = cover:import("all_exported"), 911 [Text5,Text6] = test_server:capture_get(), 912 "WARNING: Module f already imported from " ++ _ = lists:flatten(Text5), 913 "WARNING: Module a already imported from " ++ _ = lists:flatten(Text6), 914 test_server:capture_stop(), 915 check_f_calls(3,1), 916 917 %% Check that reset removes all data and that the file which has been 918 %% reset can be imported again with no warning 919 cover:reset(f), 920 check_f_calls(0,0), 921 test_server:capture_start(), 922 ok = cover:import("all_exported"), 923 [Text7] = test_server:capture_get(), % warning only on mod a 924 "WARNING: Module a already imported from " ++ _ = lists:flatten(Text7), 925 test_server:capture_stop(), 926 check_f_calls(1,0), 927 928 %% same as above - only reset all 929 cover:reset(), 930 check_f_calls(0,0), 931 test_server:capture_start(), 932 ok = cover:import("all_exported"), 933 [] = test_server:capture_get(), % no warnings 934 test_server:capture_stop(), 935 check_f_calls(1,0), 936 937 %% Check no raw files are left open 938 PortCount = length(erlang:ports()), 939 940 %% Cleanup 941 ok = cover:stop(), 942 Files = lsfiles(), 943 remove(["f_exported","all_exported"|files(Files, ".beam")]). 944 945 946otp_5031(Config) when is_list(Config) -> 947 ct:timetrap({seconds, 10}), 948 949 {ok,N1} = test_server:start_node(cover_SUITE_otp_5031,slave,[]), 950 {ok,[N1]} = cover:start(N1), 951 {error,not_main_node} = rpc:call(N1,cover,modules,[]), 952 cover:stop(), 953 test_server:stop_node(N1), 954 ok. 955 956%% Test the \'Exclude Included Functions\' functionality 957eif(Config) when is_list(Config) -> 958 ok = file:set_cwd(filename:join(proplists:get_value(data_dir, Config), 959 "included_functions")), 960 {ok, cover_inc} = compile:file(cover_inc,[debug_info]), 961 {ok, cover_inc} = cover:compile_beam(cover_inc), 962 963 %% This function will cause an included function to be executed. 964 %% The analysis should only show the lines that actually exist 965 %% in cover_inc.beam - not the ones from the included file. 966 cover_inc:func(), 967 {ok, [_, _]} = cover:analyse(cover_inc, line), 968 ok. 969 970otp_5305(Config) when is_list(Config) -> 971 ok = file:set_cwd(proplists:get_value(priv_dir, Config)), 972 973 File = "t.erl", 974 Test = <<"-module(t). 975 -export([t/0]). 976 -include_lib(\"stdlib/include/ms_transform.hrl\"). 977 t() -> 978 ets:fun2ms(fun(X) -> X end). 979 ">>, 980 ok = file:write_file(File, Test), 981 {ok, t} = cover:compile(File), 982 ok = file:delete(File), 983 ok. 984 985otp_5418(Config) when is_list(Config) -> 986 ok = file:set_cwd(proplists:get_value(priv_dir, Config)), 987 988 File = "t.erl", 989 Test = <<"-module(t). 990 ">>, 991 ok = file:write_file(File, Test), 992 {ok, t} = cover:compile(File), 993 {ok,{t,{0,0}}} = cover:analyse(t, module), 994 ok = file:delete(File), 995 ok. 996 997otp_6115(Config) when is_list(Config) -> 998 {ok, CWD} = file:get_cwd(), 999 Dir = filename:join(proplists:get_value(data_dir, Config), otp_6115), 1000 ok = file:set_cwd(Dir), 1001 {ok, f1} = compile:file(f1, [debug_info]), 1002 {ok, f2} = compile:file(f2, [debug_info]), 1003 1004 %% Cover compile f1, but not f2 1005 {ok, f1} = cover:compile(f1), 1006 1007 %% This test used to ensure that a process containing a 1008 %% fun refering to cover compiled code was killed. 1009 %% check_process_code may however ignore funs as of ERTS 1010 %% version 8.1. The test has therefore been rewritten to 1011 %% check that a process with a direct reference (in this 1012 %% case a return address) to the code is killed. 1013 %% 1014 %% If f1 is cover compiled, a process P is started with a 1015 %% direct reference to the f1, and cover:stop() is called, 1016 %% then P should be killed. 1017 %% This is because of the return address to the cover 1018 %% compiled code which should be *unloaded* when cover:stop() is 1019 %% called -- running cover compiled code when there is no cover 1020 %% server and thus no ets tables to bump counters in, makes no 1021 %% sense. 1022 Pid = spawn(fun () -> f1:non_tail_call_f2_wait() end), 1023 1024 %% Now stop cover 1025 cover:stop(), 1026 1027 %% Ensure that f1 is loaded (and not cover compiled), and that 1028 %% both Pid is dead. 1029 case code:which(f1) of 1030 Beam when is_list(Beam) -> 1031 ok; 1032 Other -> 1033 ct:fail({"f1 is not reloaded", Other}) 1034 end, 1035 case process_info(Pid) of 1036 undefined -> 1037 ok; 1038 _PI -> 1039 RefToOldP = erlang:check_process_code(Pid, f1), 1040 ct:fail({"Pid still alive", RefToOldP}) 1041 end, 1042 1043 file:set_cwd(CWD), 1044 ok. 1045 1046%% andalso/orelse 1047otp_7095(Config) when is_list(Config) -> 1048 ok = file:set_cwd(proplists:get_value(priv_dir, Config)), 1049 1050 File = "t.erl", 1051 Test = <<"-module(t). 1052 -export([t/0]). 1053 t() -> 1054 t1(), 1055 t2(), 1056 t3(), 1057 t4(), 1058 t5(), 1059 put(t6, 0), 1060 0 = t6(), 1061 1 = erase(t6), 1062 t7(), 1063 put(t8, 0), 1064 {'EXIT',{{badarg,0},_}} = (catch t8()), 1065 1 = erase(t8), 1066 t9(), 1067 ok. 1068 1069 t1() -> 1070 false % 20 1071 andalso 1072 true. % 22 1073 1074 t2() -> 1075 true % 25 1076 andalso 1077 true. % 27 1078 1079 t3() -> 1080 false % 30 1081 orelse 1082 true. % 32 1083 1084 t4() -> 1085 true % 35 1086 orelse 1087 true. % 37 1088 1089 t5() -> 1090 true % 40 1091 andalso 1092 true % 42 1093 andalso 1094 false. % 44 1095 1096 t6() -> 1097 true andalso % 47 1098 add_one(t6). % 48 1099 1100 t7() -> 1101 true % 51 1102 andalso 1103 false % 53 1104 andalso 1105 not_ok. % 55 1106 1107 t8() -> 1108 true % 58 1109 andalso 1110 true % 60 1111 andalso 1112 add_one(t8) % 62 1113 andalso 1114 false. % 64 1115 1116 t9() -> 1117 if % 67 1118 true -> 1119 true % 69 1120 andalso 1121 false % 71 1122 end 1123 orelse 1124 case ok of % 74 1125 true -> 1126 a; % 76 1127 _ -> 1128 true % 78 1129 end. 1130 1131 add_one(T) -> 1132 put(T, get(T) + 1). % 82 1133 ">>, 1134 ok = file:write_file(File, Test), 1135 {ok, t} = cover:compile(File), 1136 ok = t:t(), 1137 {ok,[{{t,4},1},{{t,5},1},{{t,6},1},{{t,7},1},{{t,8},1},{{t,9},1}, 1138 {{t,10},1},{{t,11},1},{{t,12},1},{{t,13},1},{{t,14},1}, 1139 {{t,15},1},{{t,16},1},{{t,17},1}, 1140 {{t,20},1},{{t,22},0}, 1141 {{t,25},1},{{t,27},1}, 1142 {{t,30},1},{{t,32},1}, 1143 {{t,35},1},{{t,37},0}, 1144 {{t,40},1},{{t,42},1},{{t,44},1}, 1145 {{t,47},1},{{t,48},1}, 1146 {{t,51},1},{{t,53},1},{{t,55},0}, 1147 {{t,58},1},{{t,60},1},{{t,62},1},{{t,64},0}, 1148 {{t,67},1},{{t,69},1},{{t,71},1},{{t,74},1}, 1149 {{t,76},0},{{t,78},1}, 1150 {{t,82},2}]} = cover:analyse(t, calls, line), 1151 ok = file:delete(File), 1152 ok. 1153 1154 1155%% OTP-8270. Bug. 1156otp_8270(Config) when is_list(Config) -> 1157 DataDir = proplists:get_value(data_dir, Config), 1158 ok = file:set_cwd(DataDir), 1159 1160 PrivDir = proplists:get_value(priv_dir, Config), 1161 1162 As = [{args," -pa " ++ PrivDir}], 1163 {ok,N1} = test_server:start_node(cover_n1,slave,As), 1164 {ok,N2} = test_server:start_node(cover_n2,slave,As), 1165 {ok,N3} = test_server:start_node(cover_n3,slave,As), 1166 1167 timer:sleep(500), 1168 {ok,[_,_,_]} = cover:start(nodes()), 1169 1170 Test = <<"-module(m).\n" 1171 "-compile(export_all).\n" 1172 "t() -> t(0).\n" 1173 "l() ->\n" 1174 " catch ets:tab2list(cover_internal_data_table).\n" 1175 "t(Sz) ->\n" 1176 " case ets:info(cover_internal_data_table, size) of\n" 1177 " Sz ->\n" 1178 " m:t(Sz); % Not a local call! Newly loaded code is entered.\n" 1179 " NSz ->\n" 1180 " % error_logger:info_msg(\"~p: ~p ~p change~n L1 ~p~n\", \n" 1181 " % [node(), Sz, NSz, l()]),\n" 1182 " m:t(NSz)\n" 1183 " end.\n">>, 1184 _File = c_mod(m, Test, Config), 1185 Fun = fun m:t/0, 1186 Pid1 = spawn(Fun), 1187 Pid2 = spawn(N1, Fun), 1188 Pid3 = spawn(N2, Fun), 1189 Pid4 = spawn(N3, Fun), 1190 1191 {ok, m} = cover:compile_beam(m), 1192 1193 timer:sleep(1000), 1194 1195 Info = erlang:process_info(Pid1), 1196 N1_info = rpc:call(N1, erlang, process_info, [Pid2]), 1197 N2_info = rpc:call(N2, erlang, process_info, [Pid3]), 1198 N3_info = rpc:call(N3, erlang, process_info, [Pid4]), 1199 1200 true = is_list(Info), 1201 {N1,true} = {N1,is_list(N1_info)}, 1202 {N2,true} = {N2,is_list(N2_info)}, 1203 {N3,true} = {N3,is_list(N3_info)}, 1204 1205 exit(Pid1,kill), 1206 test_server:stop_node(N1), 1207 test_server:stop_node(N2), 1208 test_server:stop_node(N3), 1209 ok. 1210 1211%% OTP-8273. Bug. 1212otp_8273(Config) when is_list(Config) -> 1213 Test = <<"-module(t). 1214 -export([t/0]). 1215 t() -> 1216 foo = true andalso foo, 1217 bar = false orelse bar, 1218 ok. 1219 ">>, 1220 File = cc_mod(t, Test, Config), 1221 ok = t:t(), 1222 ok = file:delete(File), 1223 1224 ok. 1225 1226%% OTP-8340. Bug. 1227otp_8340(Config) when is_list(Config) -> 1228 [{{t,1},1},{{t,2},1},{{t,4},1}] = 1229 analyse_expr(<<"<< \n" 1230 " <<3:2, \n" 1231 " SeqId:62>> \n" 1232 " || SeqId <- [64] >>">>, Config), 1233 ok. 1234 1235%% Clauses on the same line. 1236otp_8188(Config) when is_list(Config) -> 1237 %% This example covers the bug report: 1238 Test = <<"-module(t). 1239 -export([test/1]). 1240 1241 -define(FOOBAR(X), 1242 case X of 1243 ok -> true; 1244 _ -> false 1245 end). 1246 1247 test(X)-> 1248 _Res = 1249 ?FOOBAR(X). 1250 ">>, 1251 File = cc_mod(t, Test, Config), 1252 false = t:test(nok), 1253 {ok,[{{t,11},1},{{t,12},1}]} = cover:analyse(t, calls, line), 1254 ok = file:delete(File), 1255 1256 %% Bit string comprehensions are now traversed; 1257 %% the handling of list comprehensions has been improved: 1258 comprehension_8188(Config), 1259 1260 %% Variants of the reported bug: 1261 bug_8188(Config), 1262 ok. 1263 1264bug_8188(Cf) -> 1265 [{{t,1},1},{{t,2},1},{{t,3},1}] = 1266 analyse_expr(<<"A = 3,\n" % 1 1267 " case A of\n" % 1 1268 " 2 -> two; 3 -> three end, A + 2">>, % 1 1269 Cf), 1270 1271 [{{t,1},1}, 1272 {{t,2},0}, 1273 {{t,3},1}, 1274 {{t,4},1}, 1275 {{t,5},1}, 1276 {{t,6},0}, 1277 {{t,7},1}, 1278 {{t,9},2}] = 1279 analyse_expr(<<"case two() of\n" % 1 1280 " 1 -> 2;\n" % 0 1281 " _ -> begin 3 end\n" % 1 1282 " +\n" % 1 1283 " begin 4 end end, case two() of\n" % 1 1284 " 1 -> a;\n" % 0 1285 " 2 -> b; 3 -> c\n" % 1 1286 " end.\n" 1287 "two() -> 2">>, Cf), % 2 1288 1289 [{{t,1},1}, {{t,2},1}, {{t,3},1}, 1290 {{t,4},1}, {{t,5},1}, {{t,6},0}] = 1291 analyse_expr(<<" self() ! 1,\n" 1292 " receive \n" 1293 " X=1 -> a;\n" 1294 " X=2 -> b end, case X of \n" 1295 " 1 -> a;\n" 1296 " 2 -> b\n" 1297 " end">>, Cf), 1298 1299 T0 = <<"t1(X) ->\n " 1300 "case X of\n" 1301 " 1 -> A=a,B=A,B; % bump Li\n" 1302 " 2 -> b; 3 -> case X of % 2 -> b shall bump Li\n" 1303 " 3 -> a; % bump Li\n" 1304 " 2 -> b end; 4 -> d end, case X of % Li\n" 1305 " 1 -> a;\n" 1306 " 2 -> b; 3 -> c;\n" 1307 " 4 -> d\n" 1308 " end">>, 1309 1310 T1 = [<<"a = t1(1). ">>,T0], 1311 [{{t,1},1}, {{t,2},1}, {{t,3},1}, {{t,4},0}, 1312 {{t,5},0}, {{t,6},1}, {{t,7},1}, {{t,8},0}, {{t,9},0}] = 1313 analyse_expr(T1, Cf), 1314 1315 T2 = [<<"b = t1(2). ">>,T0], 1316 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},1}, 1317 {{t,5},0}, {{t,6},1}, {{t,7},0}, {{t,8},1}, {{t,9},0}] = 1318 analyse_expr(T2, Cf), 1319 1320 T3 = [<<"c = t1(3). ">>,T0], 1321 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},1}, 1322 {{t,5},1}, {{t,6},1}, {{t,7},0}, {{t,8},1}, {{t,9},0}] = 1323 analyse_expr(T3, Cf), 1324 1325 T4 = [<<"d = t1(4). ">>,T0], 1326 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},0}, 1327 {{t,5},0}, {{t,6},1}, {{t,7},0}, {{t,8},0}, {{t,9},1}] = 1328 analyse_expr(T4, Cf), 1329 1330 [{{t,1},1},{{t,2},1},{{t,3},1},{{t,4},1},{{t,5},1}] = 1331 analyse_expr( 1332 <<"2 = x3(1). " 1333 "x3(X) ->\n" 1334 " case X of \n" 1335 " 1 -> case X of\n" 1336 " 1 -> a, Y = 2;\n" 1337 " 2 -> b, Y = 3 end, Y; 2 -> Y = 4 end, Y">>, Cf), 1338 1339 [{{t,1},1},{{t,2},1},{{t,3},1},{{t,4},1}] = 1340 analyse_expr( 1341 <<"1 = x4(1). " 1342 "x4(X) ->\n" 1343 " case X of\n" 1344 " 1 -> case X of\n" 1345 " 1 -> Y = 1 end, case X of 1 -> Y = 1 end, Y end">>, 1346 Cf), 1347 1348 T10 = <<"t1(X) ->\n" 1349 "if\n" 1350 " X =:= 1 -> a;\n" 1351 " X =:= 2 -> b; X =:= 3 -> c end, case X of \n" 1352 " 1 -> a;\n" 1353 " 2 -> b; 3 -> c end, case X of\n" 1354 " 1 -> a;\n" 1355 " 2 -> b; 3 -> c\n" 1356 " end">>, 1357 T11 = [<<"a = t1(1). ">>,T10], 1358 [{{t,1},1}, {{t,2},1}, {{t,3},1}, {{t,4},1}, 1359 {{t,5},1}, {{t,6},1}, {{t,7},1}, {{t,8},0}] = analyse_expr(T11, Cf), 1360 1361 T12 = [<<"b = t1(2). ">>,T10], 1362 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},1}, 1363 {{t,5},0}, {{t,6},1}, {{t,7},0}, {{t,8},1}] = analyse_expr(T12, Cf), 1364 1365 T13 = [<<"c = t1(3). ">>,T10], 1366 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},1}, 1367 {{t,5},0}, {{t,6},1}, {{t,7},0}, {{t,8},1}] = analyse_expr(T13, Cf), 1368 1369 T20 = <<"t1(X) ->\n" 1370 "case X of\n" 1371 " 1 -> a;\n" 1372 " 2 -> b; 3 -> case X of\n" 1373 " 1 -> a;\n" 1374 " 2 -> b; 3 -> c end end, case X of\n" 1375 " 1 -> a;\n" 1376 " 2 -> b; 3 -> c\n" 1377 " end">>, 1378 1379 T21 = [<<"a = t1(1). ">>,T20], 1380 [{{t,1},1}, {{t,2},1}, {{t,3},1}, {{t,4},0}, 1381 {{t,5},0}, {{t,6},1}, {{t,7},1}, {{t,8},0}] = analyse_expr(T21, Cf), 1382 1383 T22 = [<<"b = t1(2). ">>,T20], 1384 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},1}, 1385 {{t,5},0}, {{t,6},1}, {{t,7},0}, {{t,8},1}] = analyse_expr(T22, Cf), 1386 1387 T23 = [<<"c = t1(3). ">>,T20], 1388 [{{t,1},1}, {{t,2},1}, {{t,3},0}, {{t,4},1}, 1389 {{t,5},0}, {{t,6},1}, {{t,7},0}, {{t,8},1}] = analyse_expr(T23, Cf), 1390 1391 T30 = << 1392 "t1(X) ->\n" 1393 "case X of\n" 1394 " 1 -> a;\n" 1395 " 2 -> b; 3 -> case X of 1 -> a; 2 -> b; 3 -> c end end, case X of\n" 1396 " 1 -> a;\n" 1397 " 2 -> b; 3 -> c\n" 1398 " end\n">>, 1399 1400 T31 = [<<"a = t1(1). ">>,T30], 1401 [{{t,1},1}, {{t,2},1}, {{t,3},1}, 1402 {{t,4},1}, {{t,5},1}, {{t,6},0}] = analyse_expr(T31, Cf), 1403 1404 T32 = [<<"b = t1(2). ">>,T30], 1405 [{{t,1},1}, {{t,2},1}, {{t,3},0}, 1406 {{t,4},1}, {{t,5},0}, {{t,6},1}] = analyse_expr(T32, Cf), 1407 1408 T33 = [<<"c = t1(3). ">>,T30], 1409 [{{t,1},1}, {{t,2},1}, {{t,3},0}, 1410 {{t,4},1}, {{t,5},0}, {{t,6},1}] = analyse_expr(T33, Cf), 1411 1412 %% 'try' now traverses the body as a body... 1413 [{{t,1},1},{{t,2},1},{{t,3},1},{{t,4},0},{{t,6},1}] = 1414 analyse_expr(<<"try \n" 1415 " B = 2, \n" 1416 " C = erlang:error(foo), \n" 1417 " {B,C} \n" 1418 "catch _:_ -> \n" 1419 " foo \n" 1420 "end">>, Cf), 1421 1422 %% receive after: 1423 [{{t,1},1},{{t,2},0},{{t,3},1}] = 1424 analyse_expr(<<"receive \n" 1425 " X=1 -> a; \n" 1426 " X=2 -> b after begin 10 end -> X=3 end">>, Cf), 1427 [{{t,1},1},{{t,2},0},{{t,3},1}] = 1428 analyse_expr(<<"receive \n" 1429 " X=1 -> a; \n" 1430 " X=2 -> b after 10 -> begin X=3 end end">>, Cf), 1431 ok. 1432 1433comprehension_8188(Cf) -> 1434 [{{t,1},1}] = analyse_expr(<<"[begin X end || X <- [1,2,3], X > 1]">>, Cf), 1435 [{{t,1},1},{{t,2},1}] = analyse_expr(<<"[begin X end || \n" 1436 " X <- [1,2,3], X > 1]">>, Cf), 1437 [{{t,1},1},{{t,2},1},{{t,3},3}] = analyse_expr(<<"[begin X end || \n " 1438 " X <- [1,2,3], \n " 1439 " X > 1]">>, Cf), 1440 [{{t,1},1},{{t,3},1},{{t,4},3}] = analyse_expr(<<"[begin X end || \n " 1441 " X <- \n " 1442 " [1,2,3], \n " 1443 " X > 1]">>, Cf), 1444 [{{t,1},1},{{t,2},2}] = analyse_expr(<<"[ \n " 1445 " X || X <- [1,2,3], X > 1]">>, Cf), 1446 [{{t,1},1},{{t,2},2},{{t,3},3}] = analyse_expr(<<"[ \n" 1447 " X || X <- [1,2,3], \n" 1448 " X > 1]">>, Cf), 1449 [{{t,1},1},{{t,2},1},{{t,3},2}] = analyse_expr(<<"[ \n " 1450 " X || X <- [1,2,3], X > 1, \n" 1451 " X > 2]">>, Cf), 1452 1453 [{{t,1},1}, 1454 {{t,3},2}, 1455 {{t,5},1}, 1456 {{t,7},1}, 1457 {{t,8},0}, 1458 {{t,12},3}, 1459 {{t,15},2}, 1460 {{t,17},2}, 1461 {{t,18},1}] = analyse_expr(<<"[ \n" % 1 1462 " begin\n" 1463 " X * 2\n" % 2 1464 " end ||\n" 1465 " X <- [1,\n" % 1 1466 " case two() of\n" 1467 " 2 -> 2;\n" % 1 1468 " _ -> two\n" % 0 1469 " end,\n" 1470 " 3],\n" 1471 " begin\n" 1472 " math:sqrt(X) > 1.0\n" % 3 1473 " end,\n" 1474 " begin\n" 1475 " true\n" % 2 1476 " end,\n" 1477 " true]. \n" % 2 1478 " two() -> 2">>, Cf), % 1 1479 1480 [{{t,1},1}, 1481 {{t,2},2}, 1482 {{t,3},1}, 1483 {{t,5},1}, 1484 {{t,6},0}, 1485 {{t,9},3}, 1486 {{t,10},2}, 1487 {{t,11},2}, 1488 {{t,12},1}] = analyse_expr(<<"[ \n" 1489 " X * 2 || \n" % 2 1490 " X <- [1,\n" % 1 1491 " case two() of\n" 1492 " 2 -> 2;\n" % 1 1493 " _ -> two\n" % 0 1494 " end,\n" 1495 " 3],\n" 1496 " math:sqrt(X) > 1.0,\n" % 3 1497 " true,\n" % 2 1498 " true]. \n" % 2 1499 " two() -> 2">>, Cf), % 1 1500 1501 [{{t,1},1}, 1502 {{t,2},2}, 1503 {{t,3},1}, 1504 {{t,4},1}, 1505 {{t,5},0}, 1506 {{t,8},1}, 1507 {{t,9},0}, 1508 {{t,12},3}, 1509 {{t,13},2}, 1510 {{t,14},2}] = analyse_expr(<<"<< \n" % 1 1511 " << (X*2) >> || \n" % 2 1512 " <<X>> <= << (case two() of\n" 1513 " 2 -> 1;\n" % 1 1514 " _ -> 2\n" % 0 1515 " end)/integer,\n" 1516 " (case two() of \n" 1517 " 2 -> 2;\n" % 1 1518 " _ -> two\n" % 0 1519 " end)/integer,\n" 1520 " 3 >>, \n" 1521 " math:sqrt(X) > 1.0,\n" % 3 1522 " true >>.\n" % 2 1523 "two() -> 2">>, Cf), 1524 [{{t,1},1}, 1525 {{t,2},4}, 1526 {{t,4},1}, 1527 {{t,6},1}, 1528 {{t,7},0}, 1529 {{t,10},3}, 1530 {{t,11},2}, 1531 {{t,12},4}, 1532 {{t,13},1}] = analyse_expr(<<"<< \n" % 1 1533 " << (2)\n" % 4 1534 " :(8) >> || \n" 1535 " <<X>> <= << 1,\n" % 1 1536 " (case two() of \n" 1537 " 2 -> 2;\n" % 1 1538 " _ -> two\n" % 0 1539 " end)/integer,\n" 1540 " 3 >>, \n" 1541 " math:sqrt(X) > 1.0,\n" % 3 1542 " <<_>> <= << 1, 2 >>,\n" % 2 1543 " true >>.\n" % 4 1544 "two() -> 2">>, Cf), % 1 1545 ok. 1546 1547eep37(Config) when is_list(Config) -> 1548 [{{t,1},1},{{t,2},1},{{t,4},6},{{t,6},1},{{t,8},1}] = 1549 analyse_expr(<<"begin\n" % 1 1550 " F =\n" % 1 1551 " fun Fact(N) when N > 0 ->\n" 1552 " N * Fact(N - 1);\n" % 6 1553 " Fact(0) ->\n" 1554 " 1\n" % 1 1555 " end,\n" 1556 " F(6)\n" % 1 1557 "end\n">>, 1558 Config), 1559 ok. 1560 1561otp_10979_hanging_node(_Config) -> 1562 1563 P1 = processes(), 1564 1565 cover:stop(non_existing_node), 1566 cover:stop(), 1567 1568 P2 = processes(), 1569 1570 case P2--P1 of 1571 [] -> 1572 ok; 1573 New -> 1574 [io:format("New: ~p, ~p~n",[P,process_info(P)]) || P<-New], 1575 ct:fail(hanging_process) 1576 end, 1577 1578 ok. 1579 1580otp_14817(Config) when is_list(Config) -> 1581 Test = <<"-module(otp_14817). 1582 -export([a/0, b/0, c/0, d/0]). 1583 a() -> ok. b() -> ok. c() -> ok. 1584 d() -> ok. 1585 ">>, 1586 File = cc_mod(otp_14817, Test, Config), 1587 ok = otp_14817:a(), 1588 ok = otp_14817:b(), 1589 ok = otp_14817:c(), 1590 ok = otp_14817:d(), 1591 {ok,[{{otp_14817,3},1}, 1592 {{otp_14817,3},1}, 1593 {{otp_14817,3},1}, 1594 {{otp_14817,4},1}]} = 1595 cover:analyse(otp_14817, calls, line), 1596 {ok, CovOut} = cover:analyse_to_file(otp_14817), 1597 {ok, Bin} = file:read_file(CovOut), 1598 <<"3..|",_/binary>> = string:find(Bin, "3..|"), 1599 <<"1..|",_/binary>> = string:find(Bin, "1..|"), 1600 ok = file:delete(File), 1601 ok = file:delete(CovOut), 1602 ok. 1603 1604%% Tests a bug where cover failed for an export named clauses 1605cover_clauses(Config) when is_list(Config) -> 1606 Test = <<"-module(cover_clauses). 1607 -export([clauses/0]). 1608 clauses() -> ok. 1609 ">>, 1610 File = cc_mod(cover_clauses, Test, Config), 1611 ok. 1612 1613%% Take compiler options from beam in cover:compile_beam 1614compile_beam_opts(Config) when is_list(Config) -> 1615 {ok, Cwd} = file:get_cwd(), 1616 ok = file:set_cwd(proplists:get_value(priv_dir, Config)), 1617 IncDir = filename:join(proplists:get_value(data_dir, Config), 1618 "included_functions"), 1619 File = filename:join([proplists:get_value(data_dir, Config), "otp_11439", "t.erl"]), 1620 %% use all compiler options allowed by cover:filter_options 1621 %% i and d don't make sense when compiling from beam though 1622 {ok, t} = 1623 compile:file(File, [{i, IncDir}, 1624 {d, 'BOOL'}, 1625 {d, 'MACRO', macro_defined}, 1626 export_all, 1627 debug_info, 1628 return_errors]), 1629 code:purge(t), 1630 code:delete(t), 1631 Exports = 1632 [{func1,0}, 1633 {macro, 0}, 1634 {exported,0}, 1635 {nonexported,0}, 1636 {module_info,0}, 1637 {module_info,1}], 1638 Exports = t:module_info(exports), 1639 {ok, t} = cover:compile_beam("t"), 1640 Exports = t:module_info(exports), 1641 ok = file:delete("t.beam"), 1642 ok = file:set_cwd(Cwd), 1643 ok. 1644 1645%% Don't crash if beam is not available 1646analyse_no_beam(Config) when is_list(Config) -> 1647 {ok, Cwd} = file:get_cwd(), 1648 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 1649 1650 code:purge(t), 1651 code:delete(t), 1652 1653 {ok,_} = file:copy("compile_beam/t.erl", "t.erl"), 1654 {ok,t} = compile:file(t, [debug_info]), 1655 {module,t} = code:load_file(t), 1656 {ok,t} = cover:compile_beam(t), 1657 t:f(), 1658 ok = cover:export("t.coverdata"), 1659 1660 code:purge(t), 1661 code:delete(t), 1662 1663 %% this is just so that cover realises (without stopping) 1664 %% that this module is not cover compiled any more 1665 {error, {not_cover_compiled,t}} = cover:analyse(t), 1666 1667 %% source and beam not available any more 1668 ok = file:delete("t.erl"), 1669 ok = file:delete("t.beam"), 1670 1671 ok = cover:import("t.coverdata"), 1672 1673 {error,{no_source_code_found,t}} = cover:analyse_to_file(t), 1674 {result,[],[{no_source_code_found,t}]} = cover:analyse_to_file([t]), 1675 1676 ok = file:delete("t.coverdata"), 1677 ok = file:set_cwd(Cwd), 1678 ok. 1679 1680%% When including eunit.hrl, a parse transform adds the function 1681%% test/0 to line 0 in your module. A bug in OTP-18.0 caused 1682%% cover:analyse_to_file/1 to fail to insert cover data in the output 1683%% file in this situation. The test below tests that this bug is 1684%% corrected. 1685line_0(Config) -> 1686 ok = file:set_cwd(filename:join(proplists:get_value(data_dir, Config), 1687 "include_eunit_hrl")), 1688 {ok, cover_inc_eunit} = compile:file(cover_inc_eunit,[debug_info]), 1689 {ok, cover_inc_eunit} = cover:compile_beam(cover_inc_eunit), 1690 {ok, CovOut} = cover:analyse_to_file(cover_inc_eunit), 1691 1692 {ok,Bin} = file:read_file(CovOut), 1693 Match = <<"0..| ok.\n">>, % "0.." is missing when bug is there 1694 S = byte_size(Bin)-byte_size(Match), 1695 <<_:S/binary,Match/binary>> = Bin, 1696 ok. 1697 1698 1699%% OTP-13200: Return error instead of crashing when trying to compile 1700%% a beam which has no 'file' attribute. 1701compile_beam_no_file(Config) -> 1702 PrivDir = proplists:get_value(priv_dir,Config), 1703 Dir = filename:join(PrivDir,"compile_beam_no_file"), 1704 ok = filelib:ensure_dir(filename:join(Dir,"*")), 1705 code:add_patha(Dir), 1706 Str = lists:concat( 1707 ["-module(nofile).\n" 1708 "-compile(export_all).\n" 1709 "foo() -> ok.\n"]), 1710 TT = do_scan(Str), 1711 Forms = [ begin {ok,Y} = erl_parse:parse_form(X),Y end || X <- TT ], 1712 {ok,_,Bin} = compile:forms(Forms,[debug_info]), 1713 BeamFile = filename:join(Dir,"nofile.beam"), 1714 ok = file:write_file(BeamFile,Bin), 1715 {error,{no_file_attribute,BeamFile}} = cover:compile_beam(nofile), 1716 [{error,{no_file_attribute,BeamFile}}] = cover:compile_beam_directory(Dir), 1717 ok. 1718 1719%% GH-4353: Don't crash when the backend for generating the abstract code 1720%% is missing. 1721compile_beam_missing_backend(Config) -> 1722 PrivDir = proplists:get_value(priv_dir, Config), 1723 Dir = filename:join(PrivDir, ?FUNCTION_NAME), 1724 ok = filelib:ensure_dir(filename:join(Dir, "*")), 1725 code:add_patha(Dir), 1726 Str = lists:append( 1727 ["-module(no_backend).\n" 1728 "-compile(export_all).\n" 1729 "foo() -> ok.\n"]), 1730 TT = do_scan(Str), 1731 Forms = [ begin {ok,Y} = erl_parse:parse_form(X),Y end || X <- TT ], 1732 {ok,_,Bin} = compile:forms(Forms, [debug_info]), 1733 1734 %% Create a debug_info chunk with a non-existing backend. 1735 {ok,no_backend,All0} = beam_lib:all_chunks(Bin), 1736 FakeBackend = definitely__not__an__existing__backend, 1737 FakeDebugInfo = {debug_info_v1,FakeBackend,nothing_here}, 1738 All = lists:keyreplace("Dbgi", 1, All0, {"Dbgi", term_to_binary(FakeDebugInfo)}), 1739 {ok,NewBeam} = beam_lib:build_module(All), 1740 BeamFile = filename:join(Dir, "no_backend.beam"), 1741 ok = file:write_file(BeamFile, NewBeam), 1742 1743 {error,{{missing_backend,FakeBackend},BeamFile}} = cover:compile_beam(no_backend), 1744 [{error,{{missing_backend,FakeBackend},BeamFile}}] = cover:compile_beam_directory(Dir), 1745 1746 ok. 1747 1748do_scan([]) -> 1749 []; 1750do_scan(Str) -> 1751 {done,{ok,T,_},C} = erl_scan:tokens([],Str,0), 1752 [ T | do_scan(C) ]. 1753 1754 1755%% PR 856. Fix a bc bug. 1756otp_13277(Config) -> 1757 Test = <<"-module(t). 1758 -export([t/0]). 1759 1760 pad(A, L) -> 1761 P = << <<\"#\">> || _ <- lists:seq(1, L) >>, 1762 <<A/binary, P/binary>>. 1763 1764 t() -> 1765 pad(<<\"hi\">>, 2). 1766 ">>, 1767 File = cc_mod(t, Test, Config), 1768 <<"hi##">> = t:t(), 1769 ok = file:delete(File), 1770 ok. 1771 1772%% Test general expressions in a binary comprehension. 1773otp_13289(Config) -> 1774 Test = <<"-module(t). 1775 -export([t/0]). 1776 1777 t() -> 1778 << (id(<<I>>)) || I <- [1,2,3] >>. 1779 1780 id(I) -> I. 1781 ">>, 1782 File = cc_mod(t, Test, Config), 1783 <<1,2,3>> = t:t(), 1784 ok = file:delete(File), 1785 ok. 1786 1787local_only(Config) -> 1788 ok = file:set_cwd(proplists:get_value(data_dir, Config)), 1789 1790 %% Trying restricting to local nodes too late. 1791 cover:start(), 1792 {ok,a} = cover:compile(a), 1793 [a] = cover:modules(), 1794 {error,too_late} = cover:local_only(), 1795 cover:stop(), 1796 1797 %% Now test local only mode. 1798 cover:start(), 1799 ok = cover:local_only(), 1800 [] = cover:modules(), 1801 {ok,a} = cover:compile(a), 1802 [a] = cover:modules(), 1803 done = a:start(5), 1804 {ok, {a,{17,2}}} = cover:analyse(a, coverage, module), 1805 {ok, [{{a,exit_kalle,0},{1,0}}, 1806 {{a,loop,3},{5,1}}, 1807 {{a,pong,1},{1,0}}, 1808 {{a,start,1},{6,0}}, 1809 {{a,stop,1},{0,1}}, 1810 {{a,trycatch,1},{4,0}}]} = 1811 cover:analyse(a, coverage, function), 1812 1813 %% Make sure that it is not possible to run cover on 1814 %% slave nodes. 1815 {ok,Name} = test_server:start_node(?FUNCTION_NAME, slave, []), 1816 {error,local_only} = cover:start([Name]), 1817 test_server:stop_node(Name), 1818 ok. 1819 1820%% ERL-943; We should not crash on startup when multiple servers race to 1821%% register the server name. 1822startup_race(Config) when is_list(Config) -> 1823 PidRefs = [spawn_monitor(fun() -> 1824 case cover:start() of 1825 {error, {already_started, _Pid}} -> 1826 ok; 1827 {ok, _Pid} -> 1828 ok 1829 end 1830 end) || _<- lists:seq(1,8)], 1831 startup_race_1(PidRefs). 1832 1833startup_race_1([{Pid, Ref} | PidRefs]) -> 1834 receive 1835 {'DOWN', Ref, process, Pid, normal} -> 1836 startup_race_1(PidRefs); 1837 {'DOWN', Ref, process, Pid, _Other} -> 1838 ct:fail("Cover server crashed on startup.") 1839 after 5000 -> 1840 ct:fail("Timed out.") 1841 end; 1842startup_race_1([]) -> 1843 cover:stop(), 1844 ok. 1845 1846otp_16476(Config) when is_list(Config) -> 1847 Mod = obvious_booleans, 1848 Dir = filename:join(proplists:get_value(data_dir, Config), 1849 ?FUNCTION_NAME), 1850 ok = file:set_cwd(Dir), 1851 {ok, Mod} = compile:file(Mod, [debug_info]), 1852 {ok, Mod} = cover:compile(Mod), 1853 ok = Mod:Mod(), 1854 ok = cover:stop(), 1855 ok. 1856 1857%%--Auxiliary------------------------------------------------------------ 1858 1859analyse_expr(Expr, Config) -> 1860 Binary = [<<"-module(t). " 1861 "-export([t/0]). " 1862 "t() -> ">>, Expr, <<".\n">>], 1863 File = cc_mod(t, Binary, Config), 1864 t:t(), 1865 {ok, Result} = cover:analyse(t, calls, line), 1866 ok = file:delete(File), 1867 Result. 1868 1869cc_mod(M, Binary, Config) -> 1870 {ok, Dir} = file:get_cwd(), 1871 PrivDir = proplists:get_value(priv_dir, Config), 1872 ok = file:set_cwd(PrivDir), 1873 File = atom_to_list(M) ++ ".erl", 1874 try 1875 ok = file:write_file(File, Binary), 1876 {ok, M} = cover:compile(File), 1877 filename:join(PrivDir, File) 1878 after file:set_cwd(Dir) 1879 end. 1880 1881c_mod(M, Binary, Config) -> 1882 {ok, Dir} = file:get_cwd(), 1883 PrivDir = proplists:get_value(priv_dir, Config), 1884 ok = file:set_cwd(PrivDir), 1885 File = atom_to_list(M) ++ ".erl", 1886 try 1887 ok = file:write_file(File, Binary), 1888 {ok, M} = compile:file(File, [debug_info]), 1889 code:purge(M), 1890 AbsFile = filename:rootname(File, ".erl"), 1891 code:load_abs(AbsFile, M), 1892 filename:join(PrivDir, File) 1893 after file:set_cwd(Dir) 1894 end. 1895 1896lsfiles() -> 1897 {ok, CWD} = file:get_cwd(), 1898 lsfiles(CWD). 1899 1900lsfiles(Dir) -> 1901 {ok, Files} = file:list_dir(Dir), 1902 Files. 1903 1904files(Files, Ext) -> 1905 lists:filter(fun(File) -> 1906 case filename:extension(File) of 1907 Ext -> true; 1908 _ -> false 1909 end 1910 end, 1911 Files). 1912 1913remove([File|Files]) -> 1914 ok = file:delete(File), 1915 remove(Files); 1916remove([]) -> 1917 ok. 1918 1919decompile([Mod|Mods]) -> 1920 code:purge(Mod), 1921 code:delete(Mod), 1922 decompile(Mods); 1923decompile([]) -> 1924 ok. 1925 1926is_unloaded(What) -> 1927 if 1928 is_list(What) -> true; 1929 What==non_existing -> true; 1930 true -> false 1931 end. 1932 1933check_f_calls(F1,F2) -> 1934 {ok,A} = cover:analyse(f,calls,function), 1935 {_,F1} = lists:keyfind({f,f1,0},1,A), 1936 {_,F2} = lists:keyfind({f,f2,0},1,A). 1937 1938cover_which_nodes(Expected) -> 1939 case cover:which_nodes() of 1940 Expected -> 1941 ok; 1942 Other -> 1943 {Time,ok} = timer:tc(fun Retry() -> 1944 case cover:which_nodes() of 1945 Expected -> ok; 1946 _ -> 1947 timer:sleep(100), 1948 Retry() 1949 end 1950 end), 1951 io:format("~p ms before cover:which_nodes() returned ~p", 1952 [Time,Expected]), 1953 Expected = Other 1954 end. 1955