- %%% Mock a package resource and create an app magically for each URL submitted.
- -module(mock_pkg_resource).
- -export([mock/0, mock/1, unmock/0]).
- -define(MOD, rebar_pkg_resource).
-
- -include("rebar.hrl").
-
- %%%%%%%%%%%%%%%%%
- %%% Interface %%%
- %%%%%%%%%%%%%%%%%
-
- %% @doc same as `mock([])'.
- mock() -> mock([]).
-
- %% @doc Mocks a fake version of the git resource fetcher that creates
- %% empty applications magically, rather than trying to download them.
- %% Specific config options are explained in each of the private functions.
- -spec mock(Opts) -> ok when
- Opts :: [Option],
- Option :: {upgrade, [App]}
- | {cache_dir, string()}
- | {default_vsn, Vsn}
- | {override_vsn, [{App, Vsn}]}
- | {not_in_index, [{App, Vsn}]}
- | {pkgdeps, [{{App,Vsn}, [Dep]}]},
- App :: string(),
- Dep :: {App, string(), {pkg, App, Vsn, InnerHash, OuterHash}},
- Vsn :: string(),
- InnerHash :: string() | undefined,
- OuterHash :: string() | undefined.
- mock(Opts) ->
- meck:new(?MOD, [no_link, passthrough]),
- mock_lock(Opts),
- mock_update(Opts),
- mock_vsn(Opts),
- mock_download(Opts),
- mock_pkg_index(Opts),
- ok.
-
- unmock() ->
- meck:unload(?MOD),
- meck:unload(rebar_packages).
-
- %%%%%%%%%%%%%%%
- %%% Private %%%
- %%%%%%%%%%%%%%%
-
- %% @doc creates values for a lock file.
- mock_lock(_) ->
- meck:expect(?MOD, lock, fun(AppInfo, _) ->
- {pkg, Name, Vsn, InnerHash, OuterHash, _RepoConfig} = rebar_app_info:source(AppInfo),
- {pkg, Name, Vsn, InnerHash, OuterHash}
- end).
-
- %% @doc The config passed to the `mock/2' function can specify which apps
- %% should be updated on a per-name basis: `{update, ["App1", "App3"]}'.
- mock_update(Opts) ->
- ToUpdate = proplists:get_value(upgrade, Opts, []),
- meck:expect(
- ?MOD, needs_update,
- fun(AppInfo, _) ->
- {pkg, App, _Vsn, _InnerHash, _OuterHash, _} = rebar_app_info:source(AppInfo),
- lists:member(binary_to_list(App), ToUpdate)
- end).
-
- %% @doc Replicated an unsupported call.
- mock_vsn(_Opts) ->
- meck:expect(
- ?MOD, make_vsn,
- fun(_AppInfo, _) ->
- {error, "Replacing version of type pkg not supported."}
- end).
-
- %% @doc For each app to download, create a dummy app on disk instead.
- %% The configuration for this one (passed in from `mock/1') includes:
- %%
- %% - Specify a version with `{pkg, _, Vsn, _}'
- %% - Dependencies for each application must be passed of the form:
- %% `{pkgdeps, [{"app1", [{app2, ".*", {pkg, ...}}]}]}' -- basically
- %% the `pkgdeps' option takes a key/value list of terms to output directly
- %% into a `rebar.config' file to describe dependencies.
- mock_download(Opts) ->
- Deps = proplists:get_value(pkgdeps, Opts, []),
- Config = proplists:get_value(config, Opts, []),
- meck:expect(
- ?MOD, download,
- fun (Dir, AppInfo, _, _) ->
- {pkg, AppBin, Vsn, _, _, _} = rebar_app_info:source(AppInfo),
- App = rebar_utils:to_list(AppBin),
- filelib:ensure_dir(Dir),
- AppDeps = proplists:get_value({App,Vsn}, Deps, []),
- {ok, AppInfo1} = rebar_test_utils:create_app(
- Dir, App, rebar_utils:to_list(Vsn),
- [kernel, stdlib] ++ [element(1,D) || D <- AppDeps]
- ),
- rebar_test_utils:create_config(Dir, [{deps, AppDeps}]++Config),
-
- TarApp = App++"-"++rebar_utils:to_list(Vsn)++".tar",
-
- Metadata = #{<<"app">> => AppBin,
- <<"version">> => Vsn},
-
- Files = all_files(rebar_app_info:dir(AppInfo1)),
- {ok, #{tarball := Tarball}} = r3_hex_tarball:create(Metadata, archive_names(Dir, Files)),
- Archive = filename:join([Dir, TarApp]),
- file:write_file(Archive, Tarball),
-
- Cache = proplists:get_value(cache_dir, Opts, filename:join(Dir,"cache")),
- Cached = filename:join([Cache, TarApp]),
- filelib:ensure_dir(Cached),
- rebar_file_utils:mv(Archive, Cached),
- ok
- end).
-
- %% @doc On top of the pkg resource mocking, we need to mock the package
- %% index.
- %%
- %% A special option, `{not_in_index, [App]}' lets the index leave out
- %% specific applications otherwise listed.
- mock_pkg_index(Opts) ->
- Deps = proplists:get_value(pkgdeps, Opts, []),
- Repos = proplists:get_value(repos, Opts, [<<"hexpm">>]),
- Skip = proplists:get_value(not_in_index, Opts, []),
- %% Dict: {App, Vsn}: [{<<"link">>, <<>>}, {<<"deps">>, []}]
- %% Index: all apps and deps in the index
-
- Dict = find_parts(Deps, Skip),
- to_index(Deps, Dict, Repos),
- meck:new(rebar_packages, [passthrough, no_link]),
- meck:expect(rebar_packages, update_package,
- fun(_, _, _State) -> ok end),
- meck:expect(rebar_packages, verify_table,
- fun(_State) -> true end).
-
- %%%%%%%%%%%%%%%
- %%% Helpers %%%
- %%%%%%%%%%%%%%%
-
- all_files(Dir) ->
- filelib:wildcard(filename:join([Dir, "**"])).
-
- archive_names(Dir, Files) ->
- [{(F -- Dir) -- "/", F} || F <- Files].
-
- find_parts(Apps, Skip) -> find_parts(Apps, Skip, dict:new()).
-
- find_parts([], _, Acc) -> Acc;
- find_parts([{AppName, Deps}|Rest], Skip, Acc) ->
- case lists:member(AppName, Skip) orelse dict:is_key(AppName,Acc) of
- true -> find_parts(Rest, Skip, Acc);
- false ->
- AccNew = dict:store(AppName,
- Deps,
- Acc),
- find_parts(Rest, Skip, AccNew)
- end.
-
- parse_deps(Deps) ->
- [{maps:get(app, D, Name), {pkg, Name, Constraint, undefined, undefined}} || D=#{package := Name,
- requirement := Constraint} <- Deps].
-
- to_index(AllDeps, Dict, Repos) ->
- catch ets:delete(?PACKAGE_TABLE),
- rebar_packages:new_package_table(),
-
- dict:fold(
- fun({N, V}, Deps, _) ->
- DepsList = [#{package => DKB,
- app => DKB,
- requirement => DVB,
- source => {pkg, DKB, DVB, undefined, undefined}}
- || {DK, DV} <- Deps,
- DKB <- [ec_cnv:to_binary(DK)],
- DVB <- [ec_cnv:to_binary(DV)]],
- Repo = rebar_test_utils:random_element(Repos),
-
- ets:insert(?PACKAGE_TABLE, #package{key={N, ec_semver:parse(V), Repo},
- dependencies=parse_deps(DepsList),
- retired=false,
- inner_checksum = <<"inner_checksum">>,
- outer_checksum = <<"checksum">>})
- end, ok, Dict),
-
- lists:foreach(fun({{Name, Vsn}, _}) ->
- case lists:any(fun(R) ->
- ets:member(?PACKAGE_TABLE, {ec_cnv:to_binary(Name), ec_semver:parse(Vsn), R})
- end, Repos) of
- false ->
- Repo = rebar_test_utils:random_element(Repos),
- ets:insert(?PACKAGE_TABLE, #package{key={ec_cnv:to_binary(Name), ec_semver:parse(Vsn), Repo},
- dependencies=[],
- retired=false,
- inner_checksum = <<"inner_checksum">>,
- outer_checksum = <<"checksum">>});
- true ->
- ok
- end
- end, AllDeps).
-
|