%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 92 -*- %% ex: ts=4 sw=4 et %%------------------------------------------------------------------- %% %% Copyright 2012 Opscode, Inc. 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 Merritt %% @doc %% Additional testing for depsolver %% @end %%------------------------------------------------------------------- -module(rlx_depsolver_tester). -export([run_data/1, run_log/1]). -include_lib("eunit/include/eunit.hrl"). -define(ADD_PKG, "^DepSelector\\sinst#\\s(\\d+)\\s-\\s" "Adding\\spackage\\sid\\s(\\d+)\\/(\\d+):\\smin\\s=\\s-1," "\\smax\\s=\\s(\\d+),\\scurrent\\sversion\\s0$"). -define(ADD_VC, "^DepSelector\\sinst#\\s(\\d+)\\s-\\sAdding\\sVC\\s" "for\\s(\\d+)\\s@\\s(\\d+)\\sdepPkg\\s(\\d+)\\s\\[\\s(\\d+)" "\\s(\\d+)\\s\\]$"). -define(ADD_GOAL, "^DepSelector\\sinst#\\s(\\d+)\\s-\\s" "Marking\\sPackage\\sRequired\\s(\\d+)$"). %%============================================================================ %% Public Api %%============================================================================ run_data(FileName) -> {ok, Device} = file:open(FileName, [read]), run_data_file(Device). run_log(FileName) -> {ok, Device} = file:open(FileName, [read]), run_log_file(Device). data1_test() -> ExpectedResult = versionify([ {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, {"dep_pkg1","0.0.2"}, {"dep_pkg2","0.0.5"}, {"app13","0.0.1"}, {"dep_pkg13","0.0.2"}, {"app6","0.0.1"} ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data1.txt"))). data2_test() -> ExpectedResult = versionify([ {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, {"dep_pkg2","0.0.5"}, {"dep_pkg1","0.0.2"}, {"dep_pkg5","0.0.3"}, {"app13","0.0.1"}, {"dep_pkg13","0.0.2"}, {"app6","0.0.1"}, {"app1","0.0.1"}, {"app4","0.0.7"}, {"app18","0.0.1"} ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data2.txt"))). data3_test() -> ExpectedResult = versionify([ {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, {"dep_pkg2","0.0.5"}, {"dep_pkg1","0.0.2"}, {"dep_pkg5","0.0.3"}, {"app13","0.0.1"}, {"dep_pkg13","0.0.2"}, {"app6","0.0.1"}, {"app1","0.0.1"}, {"app4","0.0.7"}, {"app18","0.0.1"}, {"app28","0.0.1"}, {"app38","0.0.1"}, {"app48","0.0.7"}, {"app58","0.0.1"}, {"app68","0.0.1"} ]), ?assertMatch({ok,ExpectedResult}, run_data(fix_rebar_brokenness("data3.txt"))). data4_test() -> ExpectedResult = versionify([ {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, {"dep_pkg2","0.0.5"}, {"dep_pkg1","0.0.2"}, {"dep_pkg5","0.0.3"}, {"app13","0.0.1"}, {"dep_pkg13","0.0.2"}, {"app6","0.0.1"}, {"app1","0.0.1"}, {"app4","0.0.7"}, {"app18","0.0.1"}, {"app28","0.0.1"}, {"app38","0.0.1"}, {"app48","0.0.7"}, {"app58","0.0.1"}, {"app68","0.0.1"}, {"app78","0.0.1"}, {"dep_pkg20","0.0.2"} ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data4.txt"))). data5_test() -> ExpectedResult = versionify([ {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, {"dep_pkg2","0.0.5"}, {"dep_pkg1","0.0.2"}, {"dep_pkg5","0.0.3"}, {"app13","0.0.1"}, {"dep_pkg13","0.0.2"}, {"app6","0.0.1"}, {"app1","0.0.1"}, {"app4","0.0.7"}, {"app18","0.0.1"}, {"app28","0.0.1"}, {"app38","0.0.1"}, {"app48","0.0.7"}, {"app58","0.0.1"}, {"app68","0.0.1"}, {"app78","0.0.1"}, {"dep_pkg20","0.0.2"}, {"dep_pkg22","0.0.2"}, {"dep_pkg14","0.0.2"} ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data5.txt"))). data6_test() -> ExpectedResult = versionify([ {"dep_pkg16","1.0.2"}, {"app9","0.0.1"}, {"dep_pkg7","0.1.2"}, {"dep_pkg2","0.0.5"}, {"dep_pkg1","0.0.2"}, {"dep_pkg5","0.0.3"}, {"app13","0.0.1"}, {"dep_pkg13","0.0.2"}, {"app6","0.0.1"}, {"app1","0.0.1"}, {"app4","0.0.7"}, {"app18","0.0.1"}, {"app28","0.0.1"}, {"app38","0.0.1"}, {"app48","0.0.7"}, {"app58","0.0.1"}, {"app68","0.0.1"}, {"app78","0.0.1"}, {"dep_pkg20","0.0.2"}, {"dep_pkg22","0.0.2"}, {"dep_pkg14","0.0.2"}, {"app88","0.0.1"}, {"app98","0.0.1"}, {"app108","0.0.1"} ]), ?assertMatch({ok, ExpectedResult}, run_data(fix_rebar_brokenness("data6.txt"))). log_07be9e47_test() -> Data = run_log(fix_rebar_brokenness("log-07be9e47-6f42-4a5d-b8b5-1d2eae1ad83b.txt")), ExpectedResult = versionify([ {"25","0"}, {"24","0"}, {"23","0"}, {"22","0"}, {"21","0"}, {"19","0"}, {"18","0"}, {"16","0"}, {"15","0"}, {"14","0"}, {"13","0"}, {"12","0"}, {"11","0"}, {"10","0"}, {"9","0"}, {"8","0"}, {"7","0"}, {"6","0"}, {"5","0"}, {"4","0"}, {"3","0"}, {"1","0"}, {"0","0"} ]), ?assertMatch({ok, ExpectedResult}, Data). log_183998c1_test() -> ?assertMatch({error, {unreachable_package,<<"9">>}}, run_log(fix_rebar_brokenness("log-183998c1-2ada-4214-b308-e480345c42f2.txt"))). log_311a15e7_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-311a15e7-3378-4c5b-beb7-86a1b9cf0ea9.txt")), ExpectedResult = lists:sort(versionify([ {"20","0"}, {"21","2"}, {"22","3"}, {"23","1"}, {"24","3"}, {"25","5"}, {"26","0"}, {"27","2"}, {"29","2"}, {"55","4"}, {"56","0"}, {"57","0"}, {"58","1"}, {"59","0"}, {"10","1"}, {"11","0"}, {"12","1"}, {"13","0"}, {"14","1"}, {"15","0"}, {"16","0"}, {"17","2"}, {"18","0"}, {"19","1"}, {"30","0"}, {"32","24"}, {"36","3"}, {"37","2"}, {"38","2"}, {"39","0"}, {"35","4"}, {"60","0"}, {"61","1"}, {"0","2"}, {"1","5"}, {"6","2"}, {"7","0"}, {"8","0"}, {"9","0"}, {"3","5"}, {"40","1"}, {"45", "22"} ])), ?assertMatch(ExpectedResult, lists:sort(Data)). log_382cfe5b_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-382cfe5b-0ac2-48b8-83d1-717cb4620990.txt")), ExpectedResult = lists:sort(versionify([ {"0","0"}, {"1","0"}, {"2","1"}, {"3","0"}, {"4","0"}, {"5","0"}, {"6","0"}, {"7","0"}, {"10","0"}, {"14","0"}, {"15","1"}, {"17","0"}, {"18","0"} ])), ?assertMatch(ExpectedResult, lists:sort(Data)). log_d3564ef6_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-d3564ef6-6437-41e7-90b6-dbdb849551a6_mod.txt")), ExpectedResult = lists:sort(versionify([ {"30","2"}, {"32","2"}, {"33","0"}, {"35","24"}, {"39","3"}, {"67","7"}, {"68","1"}, {"69","0"}, {"0","3"}, {"3","2"}, {"4","5"}, {"9","2"}, {"10","0"}, {"11","0"}, {"12","0"}, {"13","1"}, {"14","0"}, {"15","1"}, {"16","0"}, {"17","1"}, {"18","0"}, {"19","0"}, {"40","2"}, {"41","2"}, {"42","1"}, {"43","7"}, {"44","1"}, {"45","1"}, {"47","4"}, {"70","1"}, {"72","0"}, {"73","11"}, {"74","7"}, {"78","4"}, {"79","2"}, {"75","32"}, {"20","2"}, {"21","0"}, {"22","1"}, {"23","0"}, {"24","2"}, {"25","3"}, {"26","1"}, {"27","3"}, {"28","5"}, {"29","0"}, {"80","1"}, {"81","0"}, {"82","0"}, {"53","1"}, {"54","0"}, {"55","4"}, {"56","3"}, {"57","5"} ])), ?assertMatch(ExpectedResult, lists:sort(Data)). log_ea2d264b_test() -> {ok, Data} = run_log(fix_rebar_brokenness("log-ea2d264b-003e-4611-94ed-14efc7732083.txt")), ExpectedResult = lists:sort(versionify([ {"0","1"}, {"1","0"}, {"2","0"}, {"3","0"}, {"4","0"}, {"5","0"}, {"6","0"}, {"8","2"}, {"9","1"}, {"10","1"}, {"13","1"}, {"14","0"}, {"15","0"}, {"16","0"}, {"17","0"}, {"18","1"} ])), ?assertMatch(ExpectedResult, lists:sort(Data)). %%============================================================================ %% Internal Functions %%============================================================================ versionify(X) when erlang:is_list(X) -> lists:map(fun versionify/1, X); versionify({K, V}) -> {erlang:list_to_binary(K), rlx_depsolver:parse_version(V)}. fix_rebar_brokenness(Filename) -> Alt1 = filename:join(["./test", "data", Filename]), Alt2 = filename:join(["../test", "data", Filename]), case filelib:is_regular(Alt1) of true -> Alt1; false -> case filelib:is_regular(Alt2) of true -> Alt2; false -> erlang:throw(unable_to_find_data_files) end end. run_data_file(Device) -> Constraints = get_constraints(io:get_line(Device, "")), rlx_depsolver:solve(process_packages(read_packages(Device)), Constraints). goble_lines(_Device, eof, Acc) -> lists:reverse(Acc); goble_lines(_Device, {error, Err}, _Acc) -> erlang:throw(Err); goble_lines(Device, ValidVal, Acc) -> goble_lines(Device, io:get_line(Device, ""), [ValidVal | Acc]). goble_lines(Device) -> goble_lines(Device, io:get_line(Device, ""), []). run_log_file(Device) -> State0 = rlx_depsolver:new_graph(), {Goals, State2} = lists:foldl(fun(Line, Data) -> process_add_goal(Line, process_add_constraint(Line, process_add_package(Line, Data))) end, {[], State0}, goble_lines(Device)), rlx_depsolver:solve(State2, Goals). read_packages(Device) -> process_line(Device, io:get_line(Device, ""), []). process_line(Device, eof, Acc) -> file:close(Device), Acc; process_line(Device, [], Acc) -> process_line(Device, io:get_line(Device, ""), Acc); process_line(Device, "\n", Acc) -> process_line(Device, io:get_line(Device, ""), Acc); process_line(Device, [$\s | Rest], [{Pkg, Vsn, Deps} | Acc]) -> [DepPackage, Type, DepVsn] = rlx_string:lexemes(Rest, " \n"), Dep = case Type of "=" -> {DepPackage, DepVsn}; ">=" -> {DepPackage, DepVsn, gte} end, process_line(Device, io:get_line(Device, ""), [{Pkg, Vsn, [Dep | Deps]} | Acc]); process_line(Device, Pkg, Acc) -> [Package, Vsn] = rlx_string:lexemes(Pkg, " \n"), process_line(Device, io:get_line(Device, ""), [{Package, Vsn, []} | Acc]). process_packages(Pkgs) -> lists:foldl(fun({Pkg, Vsn, Constraints}, Dom0) -> rlx_depsolver:add_package_version(Dom0, Pkg, Vsn, Constraints) end, rlx_depsolver:new_graph(), Pkgs). get_constraints(ConLine) -> AppVsns = rlx_string:lexemes(ConLine, " \n"), lists:map(fun(AppCon) -> parse_app(AppCon, []) end, AppVsns). parse_app([$= | Rest], Acc) -> {lists:reverse(Acc), Rest}; parse_app([$>, $= | Rest], Acc) -> {lists:reverse(Acc), Rest, gte}; parse_app([Else | Rest], Acc) -> parse_app(Rest, [Else | Acc]); parse_app([], Acc) -> lists:reverse(Acc). process_add_package(Line, {Goals, State0}) -> case re:run(Line, ?ADD_PKG, [{capture, all, list}]) of {match, [_All, _InstNumber, PkgName, _PkgCount, VersionCount]} -> {Goals, lists:foldl(fun(PkgVsn, State1) -> rlx_depsolver:add_package_version(State1, PkgName, erlang:integer_to_list(PkgVsn), []) end, State0, lists:seq(0, erlang:list_to_integer(VersionCount)))}; _ -> {Goals, State0} end. process_add_constraint(Line, {Goals, State0}) -> case re:run(Line, ?ADD_VC, [{capture, all, list}]) of {match, [_All, _InstNumber, Pkg, Vsn, Dep, _Ignore, DepVsn]} -> {Goals, rlx_depsolver:add_package_version(State0, Pkg, Vsn, [{Dep, DepVsn}])}; _ -> {Goals, State0} end. process_add_goal(Line, {Goals, State0}) -> case re:run(Line, ?ADD_GOAL, [{capture, all, list}]) of {match,[_All, _InstNumber, NewGoal]} -> {[NewGoal | Goals], State0}; _ -> {Goals, State0} end.