You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 rivejä
17 KiB

8 vuotta sitten
8 vuotta sitten
8 vuotta sitten
8 vuotta sitten
8 vuotta sitten
8 vuotta sitten
9 vuotta sitten
9 vuotta sitten
9 vuotta sitten
9 vuotta sitten
  1. -module(rebar_dialyzer_SUITE).
  2. -export([suite/0,
  3. init_per_suite/1,
  4. end_per_suite/1,
  5. init_per_group/2,
  6. end_per_group/2,
  7. init_per_testcase/2,
  8. all/0,
  9. groups/0,
  10. empty_base_plt/1,
  11. empty_app_plt/1,
  12. empty_app_succ_typings/1,
  13. update_base_plt/1,
  14. update_app_plt/1,
  15. build_release_plt/1,
  16. plt_apps_option/1,
  17. exclude_and_extra/1,
  18. cli_args/1,
  19. single_app_succ_typing/1,
  20. extra_src_dirs/1]).
  21. -include_lib("common_test/include/ct.hrl").
  22. -include_lib("eunit/include/eunit.hrl").
  23. -include_lib("kernel/include/file.hrl").
  24. suite() ->
  25. [].
  26. init_per_suite(Config) ->
  27. Config.
  28. end_per_suite(_Config) ->
  29. ok.
  30. init_per_group(empty, Config) ->
  31. [{base_plt_apps, []} | Config];
  32. init_per_group(_Group, Config) ->
  33. [{base_plt_apps, [erts]} | Config].
  34. end_per_group(_Group, _Config) ->
  35. ok.
  36. init_per_testcase(Testcase, Config) ->
  37. PrivDir = ?config(priv_dir, Config),
  38. Prefix = ec_cnv:to_list(Testcase),
  39. BasePrefix = Prefix ++ "_base",
  40. Opts = [{plt_prefix, Prefix},
  41. {plt_location, PrivDir},
  42. {base_plt_prefix, BasePrefix},
  43. {base_plt_location, PrivDir},
  44. {base_plt_apps, ?config(base_plt_apps, Config)}],
  45. Suffix = "_" ++ rebar_utils:otp_release() ++ "_plt",
  46. [{plt, filename:join(PrivDir, Prefix ++ Suffix)},
  47. {base_plt, filename:join(PrivDir, BasePrefix ++ Suffix)},
  48. {rebar_config, [{dialyzer, Opts}]} |
  49. rebar_test_utils:init_rebar_state(Config)].
  50. all() ->
  51. [{group, empty}, {group, build_and_check}, {group, update}].
  52. groups() ->
  53. [{empty, [empty_base_plt, empty_app_plt, empty_app_succ_typings]},
  54. {build_and_check, [cli_args,
  55. single_app_succ_typing,
  56. build_release_plt,
  57. plt_apps_option,
  58. exclude_and_extra,
  59. extra_src_dirs]},
  60. {update, [update_base_plt, update_app_plt]}].
  61. empty_base_plt(Config) ->
  62. AppDir = ?config(apps, Config),
  63. RebarConfig = ?config(rebar_config, Config),
  64. BasePlt = ?config(base_plt, Config),
  65. Plt = ?config(plt, Config),
  66. Name = rebar_test_utils:create_random_name("app1_"),
  67. Vsn = rebar_test_utils:create_random_vsn(),
  68. rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]),
  69. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  70. {ok, [{app, Name}]}),
  71. {ok, BasePltFiles} = plt_files(BasePlt),
  72. ?assertEqual([], BasePltFiles),
  73. ErtsFiles = erts_files(),
  74. {ok, PltFiles} = plt_files(Plt),
  75. ?assertEqual(ErtsFiles, PltFiles),
  76. ok.
  77. empty_app_plt(Config) ->
  78. AppDir = ?config(apps, Config),
  79. RebarConfig = ?config(rebar_config, Config),
  80. BasePlt = ?config(base_plt, Config),
  81. Plt = ?config(plt, Config),
  82. Name = rebar_test_utils:create_random_name("app1_"),
  83. Vsn = rebar_test_utils:create_random_vsn(),
  84. rebar_test_utils:create_app(AppDir, Name, Vsn, []),
  85. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  86. {ok, [{app, Name}]}),
  87. {ok, BasePltFiles} = plt_files(BasePlt),
  88. ?assertEqual([], BasePltFiles),
  89. {ok, PltFiles} = plt_files(Plt),
  90. ?assertEqual([], PltFiles),
  91. ok.
  92. empty_app_succ_typings(Config) ->
  93. AppDir = ?config(apps, Config),
  94. RebarConfig = ?config(rebar_config, Config),
  95. Name = rebar_test_utils:create_random_name("app1_"),
  96. Vsn = rebar_test_utils:create_random_vsn(),
  97. rebar_test_utils:create_empty_app(AppDir, Name, Vsn, []),
  98. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  99. {ok, [{app, Name}]}),
  100. ok.
  101. update_base_plt(Config) ->
  102. AppDir = ?config(apps, Config),
  103. RebarConfig = ?config(rebar_config, Config),
  104. BasePlt = ?config(base_plt, Config),
  105. Plt = ?config(plt, Config),
  106. Name = rebar_test_utils:create_random_name("app1_"),
  107. Vsn = rebar_test_utils:create_random_vsn(),
  108. rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]),
  109. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  110. {ok, [{app, Name}]}),
  111. ErtsFiles = erts_files(),
  112. {ok, BasePltFiles} = plt_files(BasePlt),
  113. ?assertEqual(ErtsFiles, BasePltFiles),
  114. alter_plt(BasePlt),
  115. ok = file:delete(Plt),
  116. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  117. {ok, [{app, Name}]}),
  118. {ok, BasePltFiles2} = plt_files(BasePlt),
  119. ?assertEqual(ErtsFiles, BasePltFiles2),
  120. {ok, PltFiles} = plt_files(Plt),
  121. ?assertEqual(ErtsFiles, PltFiles),
  122. add_missing_file(BasePlt),
  123. ok = file:delete(Plt),
  124. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  125. {ok, [{app, Name}]}),
  126. {ok, BasePltFiles3} = plt_files(BasePlt),
  127. ?assertEqual(ErtsFiles, BasePltFiles3).
  128. update_app_plt(Config) ->
  129. AppDir = ?config(apps, Config),
  130. RebarConfig = ?config(rebar_config, Config),
  131. Plt = ?config(plt, Config),
  132. Name = rebar_test_utils:create_random_name("app1_"),
  133. Vsn = rebar_test_utils:create_random_vsn(),
  134. rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]),
  135. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  136. {ok, [{app, Name}]}),
  137. ErtsFiles = erts_files(),
  138. {ok, PltFiles} = plt_files(Plt),
  139. ?assertEqual(ErtsFiles, PltFiles),
  140. alter_plt(Plt),
  141. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  142. {ok, [{app, Name}]}),
  143. {ok, PltFiles2} = plt_files(Plt),
  144. ?assertEqual(ErtsFiles, PltFiles2),
  145. ok = file:delete(Plt),
  146. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  147. {ok, [{app, Name}]}),
  148. {ok, PltFiles3} = plt_files(Plt),
  149. ?assertEqual(ErtsFiles, PltFiles3),
  150. add_missing_file(Plt),
  151. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  152. {ok, [{app, Name}]}),
  153. {ok, PltFiles4} = plt_files(Plt),
  154. ?assertEqual(ErtsFiles, PltFiles4).
  155. build_release_plt(Config) ->
  156. AppDir = ?config(apps, Config),
  157. RebarConfig = ?config(rebar_config, Config),
  158. BasePlt = ?config(base_plt, Config),
  159. Plt = ?config(plt, Config),
  160. Name1 = rebar_test_utils:create_random_name("relapp1_"),
  161. Vsn1 = rebar_test_utils:create_random_vsn(),
  162. rebar_test_utils:create_app(filename:join([AppDir,"apps",Name1]), Name1, Vsn1,
  163. [erts]),
  164. Name2 = rebar_test_utils:create_random_name("relapp2_"),
  165. Vsn2 = rebar_test_utils:create_random_vsn(),
  166. rebar_test_utils:create_app(filename:join([AppDir,"apps",Name2]), Name2, Vsn2,
  167. [erts, ec_cnv:to_atom(Name1)]),
  168. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
  169. {ok, [{app, Name1}, {app, Name2}]}),
  170. ErtsFiles = erts_files(),
  171. {ok, BasePltFiles} = plt_files(BasePlt),
  172. ?assertEqual(ErtsFiles, BasePltFiles),
  173. {ok, PltFiles} = plt_files(Plt),
  174. ?assertEqual(ErtsFiles, PltFiles).
  175. plt_apps_option(Config) ->
  176. AppDir = ?config(apps, Config),
  177. RebarConfig = ?config(rebar_config, Config),
  178. Plt = ?config(plt, Config),
  179. State = ?config(state, Config),
  180. %% Create applications
  181. Name1 = rebar_test_utils:create_random_name("app1_"),
  182. Vsn1 = rebar_test_utils:create_random_vsn(),
  183. rebar_test_utils:create_app(filename:join([AppDir,"deps",Name1]), Name1, Vsn1,
  184. []),
  185. App1 = ec_cnv:to_atom(Name1),
  186. Name2 = rebar_test_utils:create_random_name("app2_"),
  187. Vsn2 = rebar_test_utils:create_random_vsn(),
  188. rebar_test_utils:create_app(filename:join([AppDir,"deps",Name2]), Name2, Vsn2,
  189. [App1]), % App2 depends on App1
  190. App2 = ec_cnv:to_atom(Name2),
  191. Name3 = rebar_test_utils:create_random_name("app3_"), % the project application
  192. Vsn3 = rebar_test_utils:create_random_vsn(),
  193. rebar_test_utils:create_app(AppDir, Name3, Vsn3,
  194. [App2]), % App3 depends on App2
  195. %% Dependencies settings
  196. State1 = rebar_state:add_resource(State, {localfs, rebar_localfs_resource}),
  197. Config1 = [{state, State1} | Config],
  198. RebarConfig1 = merge_config(
  199. [{deps,
  200. [
  201. {App1, {localfs, filename:join([AppDir,"deps",Name1])}},
  202. {App2, {localfs, filename:join([AppDir,"deps",Name2])}}
  203. ]}],
  204. RebarConfig),
  205. %% Dialyzer: plt_apps = top_level_deps (default)
  206. rebar_test_utils:run_and_check(Config1, RebarConfig1, ["dialyzer"],
  207. {ok, [{app, Name3}]}),
  208. %% NOTE: `erts` is included in `base_plt_apps`
  209. {ok, PltFiles1} = plt_files(Plt),
  210. ?assertEqual([App2, erts], get_apps_from_beam_files(PltFiles1)),
  211. %% Dialyzer: plt_apps = all_deps
  212. RebarConfig2 = merge_config([{dialyzer, [{plt_apps, all_deps}]}],
  213. RebarConfig1),
  214. rebar_test_utils:run_and_check(Config1, RebarConfig2, ["dialyzer"],
  215. {ok, [{app, Name3}]}),
  216. {ok, PltFiles2} = plt_files(Plt),
  217. ?assertEqual([App1, App2, erts], get_apps_from_beam_files(PltFiles2)).
  218. exclude_and_extra(Config) ->
  219. AppDir = ?config(apps, Config),
  220. RebarConfig = ?config(rebar_config, Config),
  221. BasePlt = ?config(base_plt, Config),
  222. Plt = ?config(plt, Config),
  223. {value, {dialyzer, Opts}, Rest} = lists:keytake(dialyzer, 1, RebarConfig),
  224. % Remove erts => []
  225. % Add erlang+zlib => [erlang, zlib],
  226. % Add erl_prim_loader+init => [erl_prim_loader, init, erlang, zlib]
  227. % Remove zlib+init => [erl_prim_loader, erlang]
  228. Opts2 = [{exclude_apps, [erts]},
  229. {base_plt_mods, [erlang, zlib]},
  230. {plt_extra_mods, [erl_prim_loader, init]},
  231. {exclude_mods, [zlib, init]} |
  232. Opts],
  233. RebarConfig2 = [{dialyzer, Opts2} | Rest],
  234. Name = rebar_test_utils:create_random_name("app1_"),
  235. Vsn = rebar_test_utils:create_random_vsn(),
  236. rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]),
  237. rebar_test_utils:run_and_check(Config, RebarConfig2, ["dialyzer"],
  238. {ok, [{app, Name}]}),
  239. Erlang = code:where_is_file("erlang.beam"),
  240. {ok, BasePltFiles} = plt_files(BasePlt),
  241. ?assertEqual([Erlang], BasePltFiles),
  242. Pair = lists:sort([Erlang, code:where_is_file("erl_prim_loader.beam")]),
  243. {ok, PltFiles} = plt_files(Plt),
  244. ?assertEqual(Pair, PltFiles).
  245. cli_args(Config) ->
  246. AppDir = ?config(apps, Config),
  247. [{dialyzer, Opts}] = ?config(rebar_config, Config),
  248. BasePlt = ?config(base_plt, Config),
  249. Plt = ?config(plt, Config),
  250. {value, {_, Prefix}, Opts1} = lists:keytake(plt_prefix, 1, Opts),
  251. {value, {_, BasePrefix}, Opts2} = lists:keytake(base_plt_prefix, 1, Opts1),
  252. {value, {_, Location}, Opts3} = lists:keytake(plt_location, 1, Opts2),
  253. {value, {_, BasePltLocation}, Opts4} = lists:keytake(base_plt_location, 1, Opts3),
  254. RebarConfig = [{dialyzer, Opts4}],
  255. Name1 = rebar_test_utils:create_random_name("relapp1_"),
  256. Vsn1 = rebar_test_utils:create_random_vsn(),
  257. rebar_test_utils:create_app(filename:join([AppDir,"apps",Name1]), Name1, Vsn1,
  258. [erts]),
  259. Name2 = rebar_test_utils:create_random_name("relapp2_"),
  260. Vsn2 = rebar_test_utils:create_random_vsn(),
  261. rebar_test_utils:create_app(filename:join([AppDir,"apps",Name2]), Name2, Vsn2,
  262. [erts, ec_cnv:to_atom(Name1)]),
  263. rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer",
  264. "--plt-location=" ++ Location,
  265. "--base-plt-location=" ++ BasePltLocation,
  266. "--plt-prefix=" ++ Prefix,
  267. "--base-plt-prefix=" ++ BasePrefix,
  268. "--statistics"],
  269. {ok, [{app, Name1}, {app, Name2}]}),
  270. ErtsFiles = erts_files(),
  271. {ok, BasePltFiles} = plt_files(BasePlt),
  272. ?assertEqual(ErtsFiles, BasePltFiles),
  273. {ok, PltFiles} = plt_files(Plt),
  274. ?assertEqual(ErtsFiles, PltFiles).
  275. single_app_succ_typing(Config) ->
  276. AppDir = ?config(apps, Config),
  277. State = ?config(state, Config),
  278. Plt = ?config(plt, Config),
  279. RebarConfig = ?config(rebar_config, Config),
  280. %% test workflow:
  281. %% (a) build PLT containing all project apps and dependencies (no success typing yet)!
  282. %% (b) perform success-typing for all apps (warnings expected)
  283. %% (c) perform success-typing for app with and without warnings
  284. Name = rebar_test_utils:create_random_name("app1_"),
  285. Vsn = rebar_test_utils:create_random_vsn(),
  286. %% contains warnings in tests
  287. rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, []),
  288. %% second app, depending on first, should not produce warnings
  289. App1 = ec_cnv:to_atom(Name),
  290. %%% second app depends on first
  291. Name2 = rebar_test_utils:create_random_name("app2_"),
  292. Vsn2 = rebar_test_utils:create_random_vsn(),
  293. rebar_test_utils:create_app(filename:join([AppDir,"apps",Name2]), Name2, Vsn2,
  294. [App1]), % App2 depends on App1
  295. App2 = ec_cnv:to_atom(Name2),
  296. %% start with building all apps into PLT
  297. RebarConfig2 = merge_config([{dialyzer, [{plt_apps, all_apps}]}],
  298. RebarConfig),
  299. {ok, _} =
  300. rebar3:run(rebar_state:new(State, RebarConfig2, AppDir), ["dialyzer", "--succ-typings=false"]),
  301. %% verify all project apps are in PLT
  302. {ok, PltFiles} = plt_files(Plt),
  303. ?assertEqual([App1, App2, erts], get_apps_from_beam_files(PltFiles)),
  304. %% warnings when apps are not specified
  305. Command0 = ["as", "test", "dialyzer"],
  306. % there are few warnings for generated test (see rebar_test_utils:erl_eunit_suite_file/1)
  307. {error, {rebar_prv_dialyzer, {dialyzer_warnings, _}}} =
  308. rebar3:run(rebar_state:new(State, RebarConfig, AppDir), Command0),
  309. %% warnings from App
  310. Command1 = ["as", "test", "dialyzer", "--app=" ++ Name],
  311. % there are few warnings for generated test (see rebar_test_utils:erl_eunit_suite_file/1)
  312. {error, {rebar_prv_dialyzer, {dialyzer_warnings, _}}} =
  313. rebar3:run(rebar_state:new(State, RebarConfig, AppDir), Command1),
  314. %% no warnings from App2
  315. Command2 = ["as", "test", "dialyzer", "--app=" ++ Name2],
  316. {ok, _} =
  317. rebar3:run(rebar_state:new(State, RebarConfig, AppDir), Command2).
  318. extra_src_dirs(Config) ->
  319. AppDir = ?config(apps, Config),
  320. State = ?config(state, Config),
  321. RebarConfig = ?config(rebar_config, Config),
  322. Name = rebar_test_utils:create_random_name("app1_"),
  323. Vsn = rebar_test_utils:create_random_vsn(),
  324. rebar_test_utils:create_eunit_app(AppDir, Name, Vsn, []),
  325. % `test` directory is already in `extra_src_dirs` when run test profile
  326. Command = ["as", "test", "dialyzer"],
  327. % there are few warnings for generated test (see rebar_test_utils:erl_eunit_suite_file/1)
  328. {error, {rebar_prv_dialyzer, {dialyzer_warnings, _}}} =
  329. rebar3:run(rebar_state:new(State, RebarConfig, AppDir), Command).
  330. %% Helpers
  331. erts_files() ->
  332. ErtsDir = code:lib_dir(erts, ebin),
  333. ErtsBeams = filelib:wildcard("*.beam", ErtsDir),
  334. ErtsFiles = lists:map(fun(Beam) -> filename:join(ErtsDir, Beam) end,
  335. ErtsBeams),
  336. lists:sort(ErtsFiles).
  337. plt_files(Plt) ->
  338. case dialyzer:plt_info(Plt) of
  339. {ok, Info} ->
  340. Files = proplists:get_value(files, Info),
  341. {ok, lists:sort(Files)};
  342. Other ->
  343. Other
  344. end.
  345. alter_plt(Plt) ->
  346. {ok, Files} = plt_files(Plt),
  347. _ = dialyzer:run([{analysis_type, plt_remove},
  348. {init_plt, Plt},
  349. {files, [hd(Files)]}]),
  350. _ = dialyzer:run([{analysis_type, plt_add},
  351. {init_plt, Plt},
  352. {files, [code:which(dialyzer)]}]),
  353. ok.
  354. add_missing_file(Plt) ->
  355. Source = code:which(dialyzer),
  356. Dest = filename:join(filename:dirname(Plt), "dialyzer.beam"),
  357. {ok, _} = file:copy(Source, Dest),
  358. _ = try
  359. dialyzer:run([{analysis_type, plt_add},
  360. {init_plt, Plt},
  361. {files, [Dest]}])
  362. after
  363. ok = file:delete(Dest)
  364. end,
  365. ok.
  366. -spec merge_config(Config, Config) -> Config when
  367. Config :: [{term(), term()}].
  368. merge_config(NewConfig, OldConfig) ->
  369. dict:to_list(
  370. rebar_opts:merge_opts(dict:from_list(NewConfig),
  371. dict:from_list(OldConfig))).
  372. -spec get_apps_from_beam_files(string()) -> [atom()].
  373. get_apps_from_beam_files(BeamFiles) ->
  374. lists:usort(
  375. [begin
  376. AppNameVsn = filename:basename(filename:dirname(filename:dirname(File))),
  377. [AppName | _] = rebar_string:lexemes(AppNameVsn ++ "-", "-"),
  378. ec_cnv:to_atom(AppName)
  379. end || File <- BeamFiles]).