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.

193 lines
7.4 KiB

преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
  1. %%% Mock a package resource and create an app magically for each URL submitted.
  2. -module(mock_pkg_resource).
  3. -export([mock/0, mock/1, unmock/0]).
  4. -define(MOD, rebar_pkg_resource).
  5. -include("rebar.hrl").
  6. %%%%%%%%%%%%%%%%%
  7. %%% Interface %%%
  8. %%%%%%%%%%%%%%%%%
  9. %% @doc same as `mock([])'.
  10. mock() -> mock([]).
  11. %% @doc Mocks a fake version of the git resource fetcher that creates
  12. %% empty applications magically, rather than trying to download them.
  13. %% Specific config options are explained in each of the private functions.
  14. -spec mock(Opts) -> ok when
  15. Opts :: [Option],
  16. Option :: {upgrade, [App]}
  17. | {cache_dir, string()}
  18. | {default_vsn, Vsn}
  19. | {override_vsn, [{App, Vsn}]}
  20. | {not_in_index, [{App, Vsn}]}
  21. | {pkgdeps, [{{App,Vsn}, [Dep]}]},
  22. App :: string(),
  23. Dep :: {App, string(), {pkg, App, Vsn, Hash}},
  24. Vsn :: string(),
  25. Hash :: string() | undefined.
  26. mock(Opts) ->
  27. meck:new(?MOD, [no_link, passthrough]),
  28. mock_lock(Opts),
  29. mock_update(Opts),
  30. mock_vsn(Opts),
  31. mock_download(Opts),
  32. mock_pkg_index(Opts),
  33. ok.
  34. unmock() ->
  35. meck:unload(?MOD),
  36. meck:unload(rebar_packages).
  37. %%%%%%%%%%%%%%%
  38. %%% Private %%%
  39. %%%%%%%%%%%%%%%
  40. %% @doc creates values for a lock file.
  41. mock_lock(_) ->
  42. meck:expect(?MOD, lock, fun(AppInfo, _) ->
  43. {pkg, Name, Vsn, Hash, _RepoConfig} = rebar_app_info:source(AppInfo),
  44. {pkg, Name, Vsn, Hash}
  45. end).
  46. %% @doc The config passed to the `mock/2' function can specify which apps
  47. %% should be updated on a per-name basis: `{update, ["App1", "App3"]}'.
  48. mock_update(Opts) ->
  49. ToUpdate = proplists:get_value(upgrade, Opts, []),
  50. meck:expect(
  51. ?MOD, needs_update,
  52. fun(AppInfo, _) ->
  53. {pkg, App, _Vsn, _Hash, _} = rebar_app_info:source(AppInfo),
  54. lists:member(binary_to_list(App), ToUpdate)
  55. end).
  56. %% @doc Replicated an unsupported call.
  57. mock_vsn(_Opts) ->
  58. meck:expect(
  59. ?MOD, make_vsn,
  60. fun(_AppInfo, _) ->
  61. {error, "Replacing version of type pkg not supported."}
  62. end).
  63. %% @doc For each app to download, create a dummy app on disk instead.
  64. %% The configuration for this one (passed in from `mock/1') includes:
  65. %%
  66. %% - Specify a version with `{pkg, _, Vsn, _}'
  67. %% - Dependencies for each application must be passed of the form:
  68. %% `{pkgdeps, [{"app1", [{app2, ".*", {pkg, ...}}]}]}' -- basically
  69. %% the `pkgdeps' option takes a key/value list of terms to output directly
  70. %% into a `rebar.config' file to describe dependencies.
  71. mock_download(Opts) ->
  72. Deps = proplists:get_value(pkgdeps, Opts, []),
  73. Config = proplists:get_value(config, Opts, []),
  74. meck:expect(
  75. ?MOD, download,
  76. fun (Dir, AppInfo, _, _) ->
  77. {pkg, AppBin, Vsn, _, _} = rebar_app_info:source(AppInfo),
  78. App = rebar_utils:to_list(AppBin),
  79. filelib:ensure_dir(Dir),
  80. AppDeps = proplists:get_value({App,Vsn}, Deps, []),
  81. {ok, AppInfo1} = rebar_test_utils:create_app(
  82. Dir, App, rebar_utils:to_list(Vsn),
  83. [kernel, stdlib] ++ [element(1,D) || D <- AppDeps]
  84. ),
  85. rebar_test_utils:create_config(Dir, [{deps, AppDeps}]++Config),
  86. TarApp = App++"-"++rebar_utils:to_list(Vsn)++".tar",
  87. Tarball = filename:join([Dir, TarApp]),
  88. Contents = filename:join([Dir, "contents.tar.gz"]),
  89. Files = all_files(rebar_app_info:dir(AppInfo1)),
  90. ok = erl_tar:create(Contents,
  91. archive_names(Dir, App, Vsn, Files),
  92. [compressed]),
  93. ok = erl_tar:create(Tarball,
  94. [{"contents.tar.gz", Contents}],
  95. []),
  96. Cache = proplists:get_value(cache_dir, Opts, filename:join(Dir,"cache")),
  97. Cached = filename:join([Cache, TarApp]),
  98. filelib:ensure_dir(Cached),
  99. rebar_file_utils:mv(Tarball, Cached),
  100. ok
  101. end).
  102. %% @doc On top of the pkg resource mocking, we need to mock the package
  103. %% index.
  104. %%
  105. %% A special option, `{not_in_index, [App]}' lets the index leave out
  106. %% specific applications otherwise listed.
  107. mock_pkg_index(Opts) ->
  108. Deps = proplists:get_value(pkgdeps, Opts, []),
  109. Repos = proplists:get_value(repos, Opts, [<<"hexpm">>]),
  110. Skip = proplists:get_value(not_in_index, Opts, []),
  111. %% Dict: {App, Vsn}: [{<<"link">>, <<>>}, {<<"deps">>, []}]
  112. %% Index: all apps and deps in the index
  113. Dict = find_parts(Deps, Skip),
  114. to_index(Deps, Dict, Repos),
  115. meck:new(rebar_packages, [passthrough, no_link]),
  116. meck:expect(rebar_packages, update_package,
  117. fun(_, _, _State) -> ok end),
  118. meck:expect(rebar_packages, verify_table,
  119. fun(_State) -> true end).
  120. %%%%%%%%%%%%%%%
  121. %%% Helpers %%%
  122. %%%%%%%%%%%%%%%
  123. all_files(Dir) ->
  124. filelib:wildcard(filename:join([Dir, "**"])).
  125. archive_names(Dir, _App, _Vsn, Files) ->
  126. [{(F -- Dir) -- "/", F} || F <- Files].
  127. find_parts(Apps, Skip) -> find_parts(Apps, Skip, dict:new()).
  128. find_parts([], _, Acc) -> Acc;
  129. find_parts([{AppName, Deps}|Rest], Skip, Acc) ->
  130. case lists:member(AppName, Skip) orelse dict:is_key(AppName,Acc) of
  131. true -> find_parts(Rest, Skip, Acc);
  132. false ->
  133. AccNew = dict:store(AppName,
  134. Deps,
  135. Acc),
  136. find_parts(Rest, Skip, AccNew)
  137. end.
  138. parse_deps(Deps) ->
  139. [{maps:get(app, D, Name), {pkg, Name, Constraint, undefined}} || D=#{package := Name,
  140. requirement := Constraint} <- Deps].
  141. to_index(AllDeps, Dict, Repos) ->
  142. catch ets:delete(?PACKAGE_TABLE),
  143. rebar_packages:new_package_table(),
  144. dict:fold(
  145. fun({N, V}, Deps, _) ->
  146. DepsList = [#{package => DKB,
  147. app => DKB,
  148. requirement => DVB,
  149. source => {pkg, DKB, DVB, undefined}}
  150. || {DK, DV} <- Deps,
  151. DKB <- [ec_cnv:to_binary(DK)],
  152. DVB <- [ec_cnv:to_binary(DV)]],
  153. Repo = rebar_test_utils:random_element(Repos),
  154. ets:insert(?PACKAGE_TABLE, #package{key={N, V, Repo},
  155. dependencies=parse_deps(DepsList),
  156. retired=false,
  157. checksum = <<"checksum">>})
  158. end, ok, Dict),
  159. lists:foreach(fun({{Name, Vsn}, _}) ->
  160. case lists:any(fun(R) ->
  161. ets:member(?PACKAGE_TABLE, {ec_cnv:to_binary(Name), Vsn, R})
  162. end, Repos) of
  163. false ->
  164. Repo = rebar_test_utils:random_element(Repos),
  165. ets:insert(?PACKAGE_TABLE, #package{key={ec_cnv:to_binary(Name), Vsn, Repo},
  166. dependencies=[],
  167. retired=false,
  168. checksum = <<"checksum">>});
  169. true ->
  170. ok
  171. end
  172. end, AllDeps).