123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295openOUnitopenDependencies_matrix_codemoduleE=Entity_codemoduleG=Graph_codemoduleDM=Dependencies_matrix_codemoduleDMBuild=Dependencies_matrix_build(*****************************************************************************)(* Helpers *)(*****************************************************************************)(*****************************************************************************)(* Data *)(*****************************************************************************)letbuild_g_and_dm()=letg=G.create()ing|>G.add_node(".",E.Dir);g|>G.add_node("foo.ml",E.File);g|>G.add_node("a",E.Dir);g|>G.add_node("a/x.ml",E.File);g|>G.add_node("a/y.ml",E.File);g|>G.add_node("bar.ml",E.File);g|>G.add_edge((".",E.Dir),("foo.ml",E.File))G.Has;g|>G.add_edge((".",E.Dir),("bar.ml",E.File))G.Has;g|>G.add_edge((".",E.Dir),("a",E.Dir))G.Has;g|>G.add_edge(("a",E.Dir),("a/x.ml",E.File))G.Has;g|>G.add_edge(("a",E.Dir),("a/y.ml",E.File))G.Has;g|>G.add_edge(("a/x.ml",E.File),("foo.ml",E.File))G.Use;g|>G.add_edge(("a/y.ml",E.File),("foo.ml",E.File))G.Use;g|>G.add_edge(("bar.ml",E.File),("foo.ml",E.File))G.Use;g|>G.add_edge(("a/y.ml",E.File),("a/x.ml",E.File))G.Use;g|>G.add_edge(("bar.ml",E.File),("a/y.ml",E.File))G.Use;letdm={matrix=[|[|0;0;0;0|];[|1;0;0;0|];[|1;2;0;0|];[|1;0;3;0|];|];name_to_i=Common.hash_of_list[("foo.ml",E.File),0;("a/x.ml",E.File),1;("a/y.ml",E.File),2;("bar.ml",E.File),3;];i_to_name=[|("foo.ml",E.File);("a/x.ml",E.File);("a/y.ml",E.File);("bar.ml",E.File);|];config=Node((".",E.Dir),[Node(("foo.ml",E.File),[]);Node(("a",E.Dir),[Node(("a/x.ml",E.File),[]);Node(("a/y.ml",E.File),[]);]);Node(("bar.ml",E.File),[]);]);}ing,dm(*****************************************************************************)(* Unit tests *)(*****************************************************************************)letunittest~graph_of_string="graph_code">:::[(*---------------------------------------------------------------------------*)(* The graph *)(*---------------------------------------------------------------------------*)"graph">:::["scc">::(fun()->letg=G.create()inlet(-->)f1f2=letf1=f1,E.Functioninletf2=f2,E.Functioninifnot(G.has_nodef1g)thenG.add_nodef1g;ifnot(G.has_nodef2g)thenG.add_nodef2g;G.add_edge(f1,f2)G.Usegin(* foo -> bar <-> bar_mutual
* \
* -> bar_bis
*)"foo"-->"bar";"bar"-->"bar_mutual";"bar_mutual"-->"bar";"bar"-->"bar_bis";let(scc,_hscc)=G.strongly_connected_components_use_graphginassert_equal~msg:"it should find the right strongly connected components"[|[("bar_bis",E.Function)];[("bar_mutual",E.Function);("bar",E.Function)];[("foo",E.Function)]|]scc;letnumbering=G.top_down_numberingginletxs=Common.hash_to_listnumbering|>Common.sort_by_val_lowfirstinassert_equal~msg:"it should find the right ordering of nodes"[("foo",E.Function),0;("bar",E.Function),1;("bar_mutual",E.Function),1;("bar_bis",E.Function),2;]xs;letnumbering=G.bottom_up_numberingginletxs=Common.hash_to_listnumbering|>Common.sort_by_val_lowfirstinassert_equal~msg:"it should find the right ordering of nodes"[("bar_bis",E.Function),0;("bar",E.Function),1;("bar_mutual",E.Function),1;("foo",E.Function),2;]xs;);"adjust graph">::(fun()->let(g,_dm)=build_g_and_dm()inletadjust=[("a","EXTRA_DIR")]inGraph_code.adjust_graphgadjust[];letgopti=Graph_code_opti.convertginletconfig=DM.basic_configginlet_dm=DMBuild.buildconfigNonegoptiin());"create fake dotdotdot entries">::(fun()->let(g,_dm)=build_g_and_dm()inletgopti=Graph_code_opti.convertginCommon.save_excursionDMBuild.threshold_pack2(fun()->letconfig=DM.basic_config_optigoptiinletdm,gopti=DMBuild.buildconfigNonegoptiinletconfig2=DM.expand_node_opti("./...",E.Dir)dm.configgoptiinletdm,gopti=DMBuild.buildconfig2Nonegoptiin(* pr2_gen dm; *)let_xs=DM.explain_cell_list_use_edges(1,0)dmgoptiin(* pr2_gen xs *)()));(*
"uses and users of file XXX" >:: (fun () ->
let g = G.create () in
let nodeinfo f =
{ G.
props = [];
pos = { Parse_info.
str = ""; charpos = -1; line = 1; column = 0;
file = fst f ^ ".php";
};
}
in
let (-->) f1 f2 =
let f1 = f1, E.Function in
let f2 = f2, E.Function in
if not (G.has_node f1 g)
then begin
G.add_node f1 g;
G.add_nodeinfo f1 (nodeinfo f1) g;
end;
if not (G.has_node f2 g)
then begin
G.add_node f2 g;
G.add_nodeinfo f2 (nodeinfo f2) g;
end;
G.add_edge (f1, f2) G.Use g
in
(* foo.php -> bar.php <-> bar_mutial.php
* \
* -> bar_bis.php
*)
"foo" --> "bar";
"bar" --> "bar_mutual";
"bar_mutual" --> "bar";
"bar" --> "bar_bis";
let uses_of_file, users_of_file =
Graph_code_analysis.build_uses_and_users_of_file g in
let uses = List.assoc "bar.php" uses_of_file in
let users = List.assoc "bar.php" users_of_file in
assert_equal
~msg:"it should find all uses"
["bar_bis.php"; "bar_mutual.php"]
uses;
assert_equal
~msg:"it should find all users"
["bar_mutual.php"; "foo.php"]
users;
);
*)"class analysis">::(fun()->letfile_content="
class A {
public function foo() { }
}
class B extends A {
public function foo() { }
}
class C {
public function foo() { }
}
"inletg=graph_of_stringfile_contentinletdag=Graph_code_class_analysis.class_hierarchyginletnode=("A",E.Class)inletchildren=Graphe.succnodedaginassert_equal~msg:"it should find the direct children of a class"["B"](children|>List.mapfst);letdag=Graph_code_class_analysis.class_hierarchyginlethmethods=Graph_code_class_analysis.toplevel_methodsgdaginletxs=Hashtbl.find_allhmethods"foo"inassert_equal~msg:"it should find the toplevel methods"["C.foo";"A.foo"](xs|>List.mapfst);letnode=("A.foo",E.Method)inletmethods=Graph_code_class_analysis.dispatched_methodsgdagnodeinassert_equal~msg:"it should find the dispatched methods"["B.foo"](methods|>List.mapfst););];(*---------------------------------------------------------------------------*)(* The matrix *)(*---------------------------------------------------------------------------*)"dm">:::["dead columns">::(fun()->let(_,dm)=build_g_and_dm()inassert_equalfalse(DM.is_dead_column0dm);assert_equaltrue(DM.is_dead_column3dm);());"internal helpers">::(fun()->let(_,dm)=build_g_and_dm()inletarr=DM.parents_of_indexesdminassert_equalarr[|[(".",E.Dir)];[(".",E.Dir);("a",E.Dir);];[(".",E.Dir);("a",E.Dir);];[(".",E.Dir)];|];assert_equal~msg:"It should not find distance between foo.ml and a/x.ml"(DM.distance_entity(0,1)arr)0;assert_equal~msg:"It should find distance between a/x.ml and foo.ml"(DM.distance_entity(1,0)arr)1;assert_equal~msg:"It should not find distance between a/x.ml a/y.ml"(DM.distance_entity(1,2)arr)0;assert_equalfalse(DM.is_internal_helper0dm);assert_equaltrue(DM.is_internal_helper1dm);assert_equalfalse(DM.is_internal_helper2dm););"explain cell">::(fun()->let(g,dm)=build_g_and_dm()inletgopti=Graph_code_opti.convertginletxs=DM.explain_cell_list_use_edges(2,1)dmgoptiinassert_equalxs[("a/y.ml",E.File),("a/x.ml",E.File);];);]]