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.

162 lines
5.9 KiB

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