%% -*- erlang-indent-level: 4; indent-tabs-mode: nil; fill-column: 92 -*-
%% ex: ts=4 sx=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 <[email protected]>
%% @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([{"app6","0.0.1"},
{"dep_pkg13","0.0.2"},
{"app13","0.0.1"},
{"dep_pkg2","0.0.5"},
{"dep_pkg1","0.0.2"},
{"dep_pkg7","0.1.2"},
{"app9","0.0.1"}]),
?assertMatch({ok, ExpectedResult},
run_data(fix_rebar_brokenness("data1.txt"))).
data2_test() ->
ExpectedResult = versionify([{"app18","0.0.1"},
{"app4","0.0.7"},
{"app1","0.0.1"},
{"app6","0.0.1"},
{"dep_pkg13","0.0.2"},
{"app13","0.0.1"},
{"dep_pkg5","0.0.3"},
{"dep_pkg1","0.0.2"},
{"dep_pkg2","0.0.5"},
{"dep_pkg7","0.1.2"},
{"app9","0.0.1"},
{"dep_pkg16","1.0.2"}]),
?assertMatch({ok, ExpectedResult},
run_data(fix_rebar_brokenness("data2.txt"))).
data3_test() ->
ExpectedResult = versionify([{"app68","0.0.1"},
{"app58","0.0.1"},
{"app48","0.0.7"},
{"app38","0.0.1"},
{"app28","0.0.1"},
{"app18","0.0.1"},
{"app4","0.0.7"},
{"app1","0.0.1"},
{"app6","0.0.1"},
{"dep_pkg13","0.0.2"},
{"app13","0.0.1"},
{"dep_pkg5","0.0.3"},
{"dep_pkg1","0.0.2"},
{"dep_pkg2","0.0.5"},
{"dep_pkg7","0.1.2"},
{"app9","0.0.1"},
{"dep_pkg16","1.0.2"}]),
?assertMatch({ok,ExpectedResult}, run_data(fix_rebar_brokenness("data3.txt"))).
data4_test() ->
ExpectedResult = versionify([{"dep_pkg20","0.0.2"},
{"app78","0.0.1"},
{"app68","0.0.1"},
{"app58","0.0.1"},
{"app48","0.0.7"},
{"app38","0.0.1"},
{"app28","0.0.1"},
{"app18","0.0.1"},
{"app4","0.0.7"},
{"app1","0.0.1"},
{"app6","0.0.1"},
{"dep_pkg13","0.0.2"},
{"app13","0.0.1"},
{"dep_pkg5","0.0.3"},
{"dep_pkg1","0.0.2"},
{"dep_pkg2","0.0.5"},
{"dep_pkg7","0.1.2"},
{"app9","0.0.1"},
{"dep_pkg16","1.0.2"}]),
?assertMatch({ok, ExpectedResult},
run_data(fix_rebar_brokenness("data4.txt"))).
data5_test() ->
ExpectedResult = versionify([{"dep_pkg14","0.0.2"},
{"dep_pkg22","0.0.2"},
{"dep_pkg20","0.0.2"},
{"app78","0.0.1"},
{"app68","0.0.1"},
{"app58","0.0.1"},
{"app48","0.0.7"},
{"app38","0.0.1"},
{"app28","0.0.1"},
{"app18","0.0.1"},
{"app4","0.0.7"},
{"app1","0.0.1"},
{"app6","0.0.1"},
{"dep_pkg13","0.0.2"},
{"app13","0.0.1"},
{"dep_pkg5","0.0.3"},
{"dep_pkg1","0.0.2"},
{"dep_pkg2","0.0.5"},
{"dep_pkg7","0.1.2"},
{"app9","0.0.1"},
{"dep_pkg16","1.0.2"}]),
?assertMatch({ok, ExpectedResult},
run_data(fix_rebar_brokenness("data5.txt"))).
data6_test() ->
ExpectedResult = versionify([{"app108","0.0.1"},
{"app98","0.0.1"},
{"app88","0.0.1"},
{"dep_pkg14","0.0.2"},
{"dep_pkg22","0.0.2"},
{"dep_pkg20","0.0.2"},
{"app78","0.0.1"},
{"app68","0.0.1"},
{"app58","0.0.1"},
{"app48","0.0.7"},
{"app38","0.0.1"},
{"app28","0.0.1"},
{"app18","0.0.1"},
{"app4","0.0.7"},
{"app1","0.0.1"},
{"app6","0.0.1"},
{"dep_pkg13","0.0.2"},
{"app13","0.0.1"},
{"dep_pkg5","0.0.3"},
{"dep_pkg1","0.0.2"},
{"dep_pkg2","0.0.5"},
{"dep_pkg7","0.1.2"},
{"app9","0.0.1"},
{"dep_pkg16","1.0.2"}]),
?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([{"0","0"},
{"1","0"},
{"3","0"},
{"4","0"},
{"5","0"},
{"6","0"},
{"7","0"},
{"8","0"},
{"9","0"},
{"10","0"},
{"11","0"},
{"12","0"},
{"13","0"},
{"14","0"},
{"15","0"},
{"16","0"},
{"18","0"},
{"19","0"},
{"21","0"},
{"22","0"},
{"23","0"},
{"24","0"},
{"25","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([{"45", "22"},
{"40","1"},
{"3","5"},
{"9","0"},
{"8","0"},
{"7","0"},
{"6","2"},
{"1","5"},
{"0","2"},
{"61","1"},
{"60","0"},
{"35","4"},
{"39","0"},
{"38","2"},
{"37","2"},
{"36","3"},
{"32","24"},
{"30","0"},
{"19","1"},
{"18","0"},
{"17","2"},
{"16","0"},
{"15","0"},
{"14","1"},
{"13","0"},
{"12","1"},
{"11","0"},
{"10","1"},
{"59","0"},
{"58","1"},
{"57","0"},
{"56","0"},
{"55","4"},
{"29","2"},
{"27","2"},
{"26","0"},
{"25","5"},
{"24","3"},
{"23","1"},
{"22","3"},
{"21","2"},
{"20","0"}])),
?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([{"18","0"},
{"17","0"},
{"15","1"},
{"14","0"},
{"10","0"},
{"7","0"},
{"6","0"},
{"5","0"},
{"4","0"},
{"3","0"},
{"2","1"},
{"1","0"},
{"0","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([{"57","5"},
{"56","3"},
{"55","4"},
{"54","0"},
{"53","1"},
{"82","0"},
{"81","0"},
{"80","1"},
{"29","0"},
{"28","5"},
{"27","3"},
{"26","1"},
{"25","3"},
{"24","2"},
{"23","0"},
{"22","1"},
{"21","0"},
{"20","2"},
{"75","32"},
{"79","2"},
{"78","4"},
{"74","7"},
{"73","11"},
{"72","0"},
{"70","1"},
{"47","4"},
{"45","1"},
{"44","1"},
{"43","7"},
{"42","1"},
{"41","2"},
{"40","2"},
{"19","0"},
{"18","0"},
{"17","1"},
{"16","0"},
{"15","1"},
{"14","0"},
{"13","1"},
{"12","0"},
{"11","0"},
{"10","0"},
{"9","2"},
{"4","5"},
{"3","2"},
{"0","3"},
{"69","0"},
{"68","1"},
{"67","7"},
{"39","3"},
{"35","24"},
{"33","0"},
{"32","2"},
{"30","2"}])),
?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([{"18","1"},
{"17","0"},
{"16","0"},
{"15","0"},
{"14","0"},
{"13","1"},
{"10","1"},
{"9","1"},
{"8","2"},
{"6","0"},
{"5","0"},
{"4","0"},
{"3","0"},
{"2","0"},
{"1","0"},
{"0","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.