From bff3b76dba9d87e571024dfc95e7a9965a866eee Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Mon, 4 May 2020 09:20:59 -0600 Subject: [PATCH] very quick test of git_subdir --- test/mock_git_subdir_resource.erl | 142 ++++++++++++++++++++++++++++++ test/rebar_compile_SUITE.erl | 18 +++- test/rebar_test_utils.erl | 31 ++++++- 3 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 test/mock_git_subdir_resource.erl diff --git a/test/mock_git_subdir_resource.erl b/test/mock_git_subdir_resource.erl new file mode 100644 index 00000000..7c1c6e9c --- /dev/null +++ b/test/mock_git_subdir_resource.erl @@ -0,0 +1,142 @@ +%%% Mock a git_subdir resource and create an app magically for each +%%% URL and subdirectory submitted. +-module(mock_git_subdir_resource). +-export([mock/0, mock/1, mock/2, unmock/0]). +-define(MOD, rebar_git_subdir_resource). + +%%%%%%%%%%%%%%%%% +%%% 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 :: {update, [App]} + | {default_vsn, Vsn} + | {override_vsn, [{App, Vsn}]} + | {deps, [{App, [Dep]}]}, + App :: string(), + Dep :: {App, {git_subdir, string(), term(), string()}} + | {pkg, App, term()}, + Vsn :: string(). +mock(Opts) -> + mock(Opts, create_app). + +mock(Opts, CreateType) -> + meck:new(?MOD, [no_link, passthrough]), + mock_lock(Opts), + mock_update(Opts), + mock_vsn(Opts), + mock_download(Opts, CreateType), + ok. + +unmock() -> + meck:unload(?MOD). + +%%%%%%%%%%%%%%% +%%% Private %%% +%%%%%%%%%%%%%%% + +%% @doc creates values for a lock file. The refs are fake, but +%% tags and existing refs declared for a dependency are preserved. +mock_lock(_) -> + meck:expect( + ?MOD, lock, + fun(AppInfo, _) -> + case rebar_app_info:source(AppInfo) of + {git_subdir, Url, {tag, Ref}, Dir} -> {git_subdir, Url, {ref, Ref}, Dir}; + {git_subdir, Url, {ref, Ref}, Dir} -> {git_subdir, Url, {ref, Ref}, Dir}; + {git_subdir, Url, Dir} -> {git_subdir, Url, {ref, "0.0.0"}, Dir}; + {git_subdir, Url, _, Dir} -> {git_subdir, Url, {ref, "0.0.0"}, Dir} + end + 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, []), +% ct:pal("TOUp: ~p", [ToUpdate]), + meck:expect( + ?MOD, needs_update, + fun(AppInfo, _) -> + {git_subdir, Url, _Ref} = rebar_app_info:source(AppInfo), + App = app(Url), +% ct:pal("Needed update? ~p (~p) -> ~p", [App, {Url,_Ref}, lists:member(App, ToUpdate)]), + lists:member(App, ToUpdate) + end). + +%% @doc Tries to fetch a version from the `*.app.src' file or otherwise +%% just returns random stuff, avoiding to check for the presence of git. +%% This probably breaks the assumption that stable references are returned. +%% +%% This function can't respect the `override_vsn' option because if the +%% .app.src file isn't there, we can't find the app name either. +mock_vsn(Opts) -> + Default = proplists:get_value(default_vsn, Opts, "0.0.0"), + meck:expect( + ?MOD, make_vsn, + fun(AppInfo, _) -> + Dir = rebar_app_info:dir(AppInfo), + case filelib:wildcard("*.app.src", filename:join([Dir,"src"])) of + [AppSrc] -> + {ok, App} = file:consult(AppSrc), + Vsn = proplists:get_value(vsn, App), + {plain, Vsn}; + _ -> + {plain, Default} + end + 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, branch, ref, or tag via the `{git_subdir, URL, {_, Vsn}' +%% format to specify a path. +%% - If there is no version submitted (`{git_subdir, URL}'), the function instead +%% reads from the `override_vsn' proplist (`{override_vsn, {"App1","1.2.3"}'), +%% and otherwise uses the value associated with `default_vsn'. +%% - Dependencies for each application must be passed of the form: +%% `{deps, [{"app1", [{app2, ".*", {git_subdir, ...}}]}]}' -- basically +%% the `deps' option takes a key/value list of terms to output directly +%% into a `rebar.config' file to describe dependencies. +mock_download(Opts, CreateType) -> + Deps = proplists:get_value(deps, Opts, []), + Config = proplists:get_value(config, Opts, []), + Default = proplists:get_value(default_vsn, Opts, "0.0.0"), + Overrides = proplists:get_value(override_vsn, Opts, []), + meck:expect( + ?MOD, download, + fun (Dir, AppInfo, _, _) -> + Git = rebar_app_info:source(AppInfo), + filelib:ensure_dir(Dir), + {git_subdir, Url, {_, Vsn}, SubDir} = normalize_git(Git, Overrides, Default), + FullSubDir = filename:join(Dir, SubDir), + filelib:ensure_dir(FullSubDir), + App = app(Url), + AppDeps = proplists:get_value({App,Vsn}, Deps, []), + rebar_test_utils:CreateType( + FullSubDir, App, Vsn, + [kernel, stdlib] ++ [element(1,D) || D <- AppDeps] + ), + rebar_test_utils:create_config(FullSubDir, [{deps, AppDeps}]++Config), + ok + end). + +%%%%%%%%%%%%%%% +%%% Helpers %%% +%%%%%%%%%%%%%%% +app(Path) -> + filename:basename(Path, ".git"). + +normalize_git({git_subdir, Url, SubDir}, Overrides, Default) -> + Vsn = proplists:get_value(app(Url), Overrides, Default), + {git, Url, {tag, Vsn}, SubDir}; +normalize_git({git_subdir, Url, Branch, SubDir}, _, _) when is_list(Branch) -> + {git, Url, {branch, Branch}, SubDir}; +normalize_git(Git, _, _) -> + Git. diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 55e0cda5..3120bfed 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -27,7 +27,7 @@ all() -> parse_transform_test, erl_first_files_test, mib_test, umbrella_mib_first_test, only_default_transitive_deps, clean_all, clean_specific, profile_deps, deps_build_in_prod, only_deps, - override_deps, override_add_deps, override_del_deps, + override_deps, git_subdir_deps, override_add_deps, override_del_deps, override_opts, override_add_opts, override_del_opts, apply_overrides_exactly_once, override_only_deps, profile_override_deps, profile_override_add_deps, profile_override_del_deps, @@ -1531,6 +1531,22 @@ override_deps(Config) -> {dep_not_exist, "other_dep"}]} ). +git_subdir_deps(Config) -> + Deps = rebar_test_utils:expand_deps(git_subdir, [{"some_dep", "0.0.1", [{"other_dep", "0.0.1", []}]}]), + TopDeps = rebar_test_utils:top_level_deps(Deps), + + {SrcDeps, _} = rebar_test_utils:flat_deps(Deps), + mock_git_subdir_resource:mock([{deps, SrcDeps}]), + + RebarConfig = [ + {deps, TopDeps} + ], + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{subdir_dep, "some_dep"}, + {subdir_dep, "other_dep"}]} + ). + override_add_deps(Config) -> Deps = rebar_test_utils:expand_deps(git, [{"some_dep", "0.0.1", [{"other_dep", "0.0.1", []}]}]), TopDeps = rebar_test_utils:top_level_deps(Deps), diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 0f5948d2..b93403ab 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -183,6 +183,12 @@ random_seed() -> -endif. expand_deps(_, []) -> []; +expand_deps(git_subdir, [{Name, Deps} | Rest]) -> + Dep = {Name, {git_subdir, "https://example.org/user/"++Name++".git", {branch, "master"}, "appsubdir"}}, + [{Dep, expand_deps(git_subdir, Deps)} | expand_deps(git_subdir, Rest)]; +expand_deps(git_subdir, [{Name, Vsn, Deps} | Rest]) -> + Dep = {Name, Vsn, {git_subdir, "https://example.org/user/"++Name++".git", {tag, Vsn}, "appsubdir"}}, + [{Dep, expand_deps(git_subdir, Deps)} | expand_deps(git_subdir, Rest)]; expand_deps(git, [{Name, Deps} | Rest]) -> Dep = {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}}, [{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)]; @@ -226,6 +232,12 @@ flat_deps([{{pkg, Name, Vsn, undefined, undefined}, PkgDeps} | Rest], Src, Pkg) Src, Pkg ++ [Current | FlatPkgDeps]); flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest], Src, Pkg) -> + Current = {{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)}, + {FlatDeps, FlatPkgDeps} = flat_deps(Deps), + flat_deps(Rest, + Src ++ [Current | FlatDeps], + Pkg ++ FlatPkgDeps); +flat_deps([{{Name,Ref}, Deps} | Rest], Src, Pkg) -> Current = {{Name,vsn_from_ref(Ref)}, top_level_deps(Deps)}, {FlatDeps, FlatPkgDeps} = flat_deps(Deps), flat_deps(Rest, @@ -233,7 +245,9 @@ flat_deps([{{Name,_Vsn,Ref}, Deps} | Rest], Src, Pkg) -> Pkg ++ FlatPkgDeps). vsn_from_ref({git, _, {_, Vsn}}) -> Vsn; -vsn_from_ref({git, _, Vsn}) -> Vsn. +vsn_from_ref({git, _, Vsn}) -> Vsn; +vsn_from_ref({git_subdir, _, {_, Vsn}, _}) -> Vsn; +vsn_from_ref({git_subdir, _, Vsn, _}) -> Vsn. top_level_deps([]) -> []; top_level_deps([{{pkg, Name, Vsn, undefined, undefined}, _} | Deps]) -> @@ -246,6 +260,7 @@ top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) -> %%%%%%%%%%%%%%% check_results(AppDir, Expected, ProfileRun) -> BuildDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "lib", "*"])), + BuildSubDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "lib", "*", "*"])), PluginDirs = filelib:wildcard(filename:join([AppDir, "_build", ProfileRun, "plugins", "*"])), GlobalPluginDirs = filelib:wildcard(filename:join([AppDir, "global", "plugins", "*"])), CheckoutsDirs = filelib:wildcard(filename:join([AppDir, "_checkouts", "*"])), @@ -259,7 +274,9 @@ check_results(AppDir, Expected, ProfileRun) -> ValidDepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- ValidApps], Deps = rebar_app_discover:find_apps(BuildDirs, all), + SubDeps = rebar_app_discover:find_apps(BuildSubDirs, all), DepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Deps], + SubDirDepsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- SubDeps], Checkouts = rebar_app_discover:find_apps(CheckoutsDirs, all), CheckoutsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Checkouts], Plugins = rebar_app_discover:find_apps(PluginDirs, all), @@ -323,6 +340,18 @@ check_results(AppDir, Expected, ProfileRun) -> ?assertEqual(iolist_to_binary(Vsn), iolist_to_binary(rebar_app_info:original_vsn(App))) end + ; ({subdir_dep, Name}) -> + ct:pal("Subdir Dep Name: ~p", [Name]), + ?assertNotEqual(false, lists:keyfind(Name, 1, SubDirDepsNames)) + ; ({subdir_dep, Name, Vsn}) -> + ct:pal("Subdir Dep Name: ~p, Vsn: ~p", [Name, Vsn]), + case lists:keyfind(Name, 1, SubDirDepsNames) of + false -> + error({dep_not_found, Name}); + {Name, App} -> + ?assertEqual(iolist_to_binary(Vsn), + iolist_to_binary(rebar_app_info:original_vsn(App))) + end ; ({plugin, Name}) -> ct:pal("Plugin Name: ~p", [Name]), ?assertNotEqual(false, lists:keyfind(Name, 1, PluginsNames))