%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 92 -*- %%% Copyright 2012 Erlware, LLC. All Rights Reserved. %%% %%% This file is provided to you under the Apache License, %%% Version 2.0 (the "License"); you may not use this file %%% except in compliance with the License. You may obtain %%% a copy of the License at %%% %%% http://www.apache.org/licenses/LICENSE-2.0 %%% %%% Unless required by applicable law or agreed to in writing, %%% software distributed under the License is distributed on an %%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY %%% KIND, either express or implied. See the License for the %%% specific language governing permissions and limitations %%% under the License. %%%------------------------------------------------------------------- %%% @author Eric Merrit %%% @copyright (C) 2012, Eric Merrit -module(rlx_discover_SUITE). -export([suite/0, init_per_suite/1, end_per_suite/1, init_per_testcase/2, all/0, normal_case/1, no_beam_case/1, bad_ebin_case/1, shallow_app_discovery/1 ]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). suite() -> [{timetrap,{seconds,120}}]. init_per_suite(Config) -> Config. end_per_suite(_Config) -> ok. init_per_testcase(_, Config) -> DataDir = filename:join(proplists:get_value(priv_dir, Config), ?MODULE), LibDir1 = filename:join([DataDir, rlx_test_utils:create_random_name("lib_dir1_")]), LibDir2 = filename:join([DataDir, rlx_test_utils:create_random_name("lib_dir2_")]), ok = rlx_util:mkdir_p(LibDir1), ok = rlx_util:mkdir_p(LibDir2), State = rlx_state:new([], [{lib_dirs, [LibDir1, LibDir2]}], [release]), {ok, State1} = rlx_config:do(State), [{lib1, LibDir1}, {lib2, LibDir2}, {state, State1} | Config]. all() -> [normal_case, no_beam_case, bad_ebin_case, shallow_app_discovery]. normal_case(Config) -> LibDir1 = proplists:get_value(lib1, Config), Apps1 = [(fun({Name, Vsn}) -> create_app(LibDir1, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app1_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], LibDir2 = proplists:get_value(lib2, Config), Apps2 = [(fun({Name, Vsn}) -> create_app(LibDir2, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app2_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], State0 = rlx_state:put(proplists:get_value(state, Config), default_libs, false), {ok, State1} = providers:new(rlx_prv_app_discover, State0), DiscoverProvider = providers:get_provider(app_discover, rlx_state:providers(State1)), {ok, State2} = providers:do(DiscoverProvider, State1), lists:foreach(fun(App) -> ?assertMatch(true, lists:member(App, rlx_state:available_apps(State2))) end, Apps1), lists:foreach(fun(App) -> ?assertMatch(true, lists:member(App, rlx_state:available_apps(State2))) end, Apps2), Length = erlang:length(Apps1) + erlang:length(Apps2), ?assertMatch(Length, erlang:length(rlx_state:available_apps(State2))). no_beam_case(Config) -> %% do not ignore apps with no beam files if no modules in app file LibDir1 = proplists:get_value(lib1, Config), _Apps1 = [(fun({Name, Vsn}) -> create_app(LibDir1, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app1_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], LibDir2 = proplists:get_value(lib2, Config), _Apps2 = [(fun({Name, Vsn}) -> create_app(LibDir2, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app2_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], BadName = rlx_test_utils:create_random_name("error_bad"), BadVsn = rlx_test_utils:create_random_vsn(), AppDir = filename:join([LibDir2, BadName]), write_app_file(AppDir, BadName, BadVsn), State0 = proplists:get_value(state, Config), %% Deliberately disable release discovery when running `rlx_prv_app_discover` State1 = rlx_state:put(State0, disable_rel_discovery, true), {ok, State2} = providers:new(rlx_prv_app_discover, State1), DiscoverProvider = providers:get_provider(app_discover, rlx_state:providers(State2)), ?assertMatch({ok, _}, providers:do(DiscoverProvider, State2)). bad_ebin_case(Config) -> LibDir1 = proplists:get_value(lib1, Config), _Apps1 = [(fun({Name, Vsn}) -> create_app(LibDir1, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app1_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], LibDir2 = proplists:get_value(lib2, Config), _Apps2 = [(fun({Name, Vsn}) -> create_app(LibDir2, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app2_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], BadName = rlx_test_utils:create_random_name("error_bad"), BadVsn = rlx_test_utils:create_random_vsn(), AppDir = filename:join([LibDir2, BadName]), Filename = filename:join([AppDir, <<"ebin">>, BadName ++ ".app"]), ok = filelib:ensure_dir(Filename), ok = ec_file:write_term(Filename, get_bad_app_metadata(BadName, BadVsn)), State0 = proplists:get_value(state, Config), {ok, State1} = providers:new(rlx_prv_app_discover, State0), DiscoverProvider = providers:get_provider(app_discover, rlx_state:providers(State1)), {ok, State2} = providers:do(DiscoverProvider, State1), ?assertMatch([], [App || App <- rlx_state:available_apps(State2), BadName =:= rlx_app_info:name(App)]). shallow_app_discovery(Config) -> LibDir1 = proplists:get_value(lib1, Config), Apps1 = [(fun({Name, Vsn}) -> create_app(LibDir1, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app1_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], LibDir2 = proplists:get_value(lib2, Config), Apps2 = [(fun({Name, Vsn}) -> create_app(LibDir2, Name, Vsn) end)(App) || App <- [{rlx_test_utils:create_random_name("lib_app2_"), rlx_test_utils:create_random_vsn()} || _ <- lists:seq(1, 100)]], State0 = rlx_state:put(proplists:get_value(state, Config), default_libs, false), State1 = rlx_state:put(State0, enable_shallow_app_discovery, true), {ok, State2} = providers:new(rlx_prv_app_discover, State1), DiscoverProvider = providers:get_provider(app_discover, rlx_state:providers(State2)), {ok, State3} = providers:do(DiscoverProvider, State2), lists:foreach(fun(App) -> ?assertMatch(true, lists:member(App, rlx_state:available_apps(State3))) end, Apps1), lists:foreach(fun(App) -> ?assertMatch(true, lists:member(App, rlx_state:available_apps(State3))) end, Apps2), Length = erlang:length(Apps1) + erlang:length(Apps2), ?assertMatch(Length, erlang:length(rlx_state:available_apps(State3))). %%%=================================================================== %%% Helper functions %%%=================================================================== create_app(Dir, Name, Vsn) -> AppDir = filename:join([Dir, Name]), write_app_file(AppDir, Name, Vsn), write_beam_file(AppDir, Name), {ok, App} = rlx_app_info:new(erlang:list_to_atom(Name), Vsn, erlang:iolist_to_binary(AppDir), [kernel, stdlib], []), App. write_beam_file(Dir, Name) -> Beam = filename:join([Dir, "ebin", "not_a_real_beam" ++ Name ++ ".beam"]), ok = filelib:ensure_dir(Beam), ok = ec_file:write_term(Beam, testing_purposes_only). write_app_file(Dir, Name, Version) -> Filename = filename:join([Dir, "ebin", Name ++ ".app"]), ok = filelib:ensure_dir(Filename), ok = ec_file:write_term(Filename, get_app_metadata(Name, Version)). get_app_metadata(Name, Vsn) -> {application, erlang:list_to_atom(Name), [{description, ""}, {vsn, Vsn}, {modules, []}, {applications, [kernel, stdlib]}]}. get_bad_app_metadata(Name, Vsn) -> ["{application, ", Name, ", [{description, \"\"}, {vsn, \"", Vsn, "\"}, {modules, [missing], {applications, [kernel, stdlib]}]}."].