%%% This file is part of RefactorErl.
%%%
%%% RefactorErl is free software: you can redistribute it and/or modify
%%% it under the terms of the GNU Lesser General Public License as published
%%% by the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% RefactorErl is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%%% GNU Lesser General Public License for more details.
%%%
%%% You should have received a copy of the GNU Lesser General Public License
%%% along with RefactorErl.  If not, see <http://plc.inf.elte.hu/erlang/>.
%%%
%%% The Original Code is RefactorErl.
%%%
%%% The Initial Developer of the Original Code is Eötvös Loránd University.
%%% Portions created  by Eötvös Loránd University and ELTE-Soft Ltd.
%%% are Copyright 2007-2025 Eötvös Loránd University, ELTE-Soft Ltd.
%%% and Ericsson Hungary. All Rights Reserved.


%%% ============================================================================
%%% Module information

%%% @author Mate Tejfel <matej@inf.elte.hu>

-module(refqc_var_anal_test).
-vsn("$Rev: 17551 $ ").

-export([var_anal_test/1, prop_anal_var_n/2, test/1]).

-include_lib("referl_qc/include/prop_based_testing.hrl").
-include_lib("referl_qc/include/qc.hrl").

%% @doc Variable analyzer test. It checks the graph consistency for a list of
%% modules using the prop_anal_var_n function with parameter 100.
%%
%% @spec var_anal_test([atom()]) -> ok 
var_anal_test(Modules) ->
    case Modules of
        []         -> true; 
        [Mod|Mods] ->
            prop_anal_var_n(Mod, 100),
            var_anal_test(Mods)
    end.


%% Argument generator
generate_args(Module) ->
    VarNames = ?QCGEN:generate_varnames(rand:uniform(10)),
    QuerySeq = ?Query:seq([?Mod:find(Module),
                   ?Mod:locals(), ?Fun:definition(), ?Form:clauses(),
                   ?Clause:body()]),
    ExprNodes = ?Query:exec(QuerySeq),
    MatchExprNodes = [ExprNode || ExprNode <- ExprNodes, 
            ?Expr:kind(ExprNode) == match_expr],
    ChangeExprNodes = 
        case MatchExprNodes of
            [] -> []; 
            _  -> ?QCCOMMON:get_n(rand:uniform(length(MatchExprNodes)), 
                                                            MatchExprNodes)
        end,
    {MatchExprNodes, ChangeExprNodes, VarNames}.
    

%% @doc Variable analyzer test. It checks the graph consistency for a module,
%% checking the property prop_anal_var for one times (without QuickCheck).
%%
%% @spec test(atom()) -> boolean() 
test(Module) ->
    Args = generate_args(Module),
    case Args of
        {MatchExprNodes, ChangeExprNodes, VarNames} -> 
            replAll(ChangeExprNodes, VarNames),
            MatchVarNodes = [hd(?Query:exec(ExprNode, ?Expr:child(1))) 
                         || ExprNode <- MatchExprNodes],        
            ?GRAPHPROP:propVar(MatchVarNodes, Module);
        _  -> true
    end.  
 
%% @doc Variable analyzer test. It checks the graph consistency for a module,
%% checking the property prop_anal_var for N times using QuickCheck.
%%
%% @spec prop_anal_var_n(atom(), int()) -> ok 
prop_anal_var_n(Module,N) ->
    ?QuickCheck:quickcheck(
      ?QuickCheck:numtests(N, prop_anal_var(Module))).


prop_anal_var(Module) ->
    ?FORALL(Args, generate_args(Module),
        begin
            case Args of
                {MatchExprNodes, ChangeExprNodes, VarNames} -> 
                     replAll(ChangeExprNodes, VarNames),
                     MatchVarNodes = [hd(?Query:exec(ExprNode, ?Expr:child(1))) 
                         || ExprNode <- MatchExprNodes],
                     ?GRAPHPROP:propVar(MatchVarNodes, Module);
                _  -> true
            end        
        end).
    
replAll([], _) -> 
    ?ESG:finalize();

replAll([ExprNode | ExprNodes], [VarName | VarNames]) ->
    NewNode = ?Syn:create(#expr{type = variable}, [VarName]),
    ?Syn:replace(ExprNode, {sub, 1, 1}, [NewNode]),
    replAll(ExprNodes, VarNames ++ [VarName]).    
