1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1996-2012. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: -module(digraph_SUITE). 20: 21: %-define(STANDALONE,1). 22: 23: -ifdef(STANDALONE). 24: -define(line, put(line, ?LINE), ). 25: -else. 26: -include_lib("test_server/include/test_server.hrl"). 27: -endif. 28: 29: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 30: init_per_group/2,end_per_group/2]). 31: 32: -export([opts/1, degree/1, path/1, cycle/1, vertices/1, 33: edges/1, data/1, otp_3522/1, otp_3630/1, otp_8066/1]). 34: 35: -export([spawn_graph/2]). 36: 37: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 38: 39: suite() -> [{ct_hooks,[ts_install_cth]}]. 40: 41: all() -> 42: [opts, degree, path, cycle, {group, misc}, 43: {group, tickets}]. 44: 45: groups() -> 46: [{misc, [], [vertices, edges, data]}, 47: {tickets, [], [otp_3522, otp_3630, otp_8066]}]. 48: 49: init_per_suite(Config) -> 50: Config. 51: 52: end_per_suite(_Config) -> 53: ok. 54: 55: init_per_group(_GroupName, Config) -> 56: Config. 57: 58: end_per_group(_GroupName, Config) -> 59: Config. 60: 61: 62: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 63: 64: opts(doc) -> []; 65: opts(suite) -> []; 66: opts(Config) when is_list(Config) -> 67: %% OTP-5985: the 'public' option has been removed 68: ?line {'EXIT',{badarg,_}} = (catch digraph:new([public])), 69: ?line {P2,G2} = spawn_graph([private]), 70: ?line {'EXIT',{badarg,_}} = (catch digraph:add_vertex(G2, x)), 71: ?line kill_graph(P2), 72: ?line {P3,G3} = spawn_graph([protected]), 73: ?line {'EXIT',{badarg,_}} = (catch digraph:add_vertex(G3, x)), 74: ?line kill_graph(P3), 75: ?line Template = [{v1,[v2]}, {v2,[v3]}, {v3,[v4]}, {v4,[]}], 76: ?line G4 = build_graph([], Template), 77: ?line e = digraph:add_edge(G4, e, v4, v1, []), 78: ?line digraph:delete(G4), 79: ?line G5 = build_graph([cyclic], Template), 80: ?line e = digraph:add_edge(G5, e, v4, v1, []), 81: ?line digraph:delete(G5), 82: ?line G6 = build_graph([acyclic], Template), 83: ?line acyclic = info(G6, cyclicity), 84: ?line {error, {bad_edge,_}} = digraph:add_edge(G6, v4, v1), 85: ?line digraph:delete(G6), 86: ok. 87: 88: 89: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 90: 91: degree(doc) -> []; 92: degree(suite) -> []; 93: degree(Config) when is_list(Config) -> 94: ?line G = build_graph([], [{x1,[]}, {x2,[x1]}, {x3,[x1,x2]}, 95: {x4,[x1,x2,x3]}, {x5,[x1,x2,x3,x4]}]), 96: %% out degree 97: ?line 0 = digraph:out_degree(G, x1), 98: ?line 1 = digraph:out_degree(G, x2), 99: ?line 2 = digraph:out_degree(G, x3), 100: ?line 3 = digraph:out_degree(G, x4), 101: ?line 4 = digraph:out_degree(G, x5), 102: %% out neighbours 103: ?line [] = check(digraph:out_neighbours(G, x1), []), 104: ?line [] = check(digraph:out_neighbours(G, x2), [x1]), 105: ?line [] = check(digraph:out_neighbours(G, x3), [x1,x2]), 106: ?line [] = check(digraph:out_neighbours(G, x4), [x1,x2,x3]), 107: ?line [] = check(digraph:out_neighbours(G, x5), [x1,x2,x3,x4]), 108: 109: %% in degree 110: ?line 4 = digraph:in_degree(G, x1), 111: ?line 3 = digraph:in_degree(G, x2), 112: ?line 2 = digraph:in_degree(G, x3), 113: ?line 1 = digraph:in_degree(G, x4), 114: ?line 0 = digraph:in_degree(G, x5), 115: %% in neighbours 116: ?line [] = check(digraph:in_neighbours(G, x1), [x2,x3,x4,x5]), 117: ?line [] = check(digraph:in_neighbours(G, x2), [x3,x4,x5]), 118: ?line [] = check(digraph:in_neighbours(G, x3), [x4,x5]), 119: ?line [] = check(digraph:in_neighbours(G, x4), [x5]), 120: ?line [] = check(digraph:in_neighbours(G, x5), []), 121: digraph:delete(G), 122: ok. 123: 124: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 125: 126: path(doc) -> []; 127: path(suite) -> []; 128: path(Config) when is_list(Config) -> 129: ?line G = build_graph([], [{x1,[x2,x3]}, {x2,[x4]}, {x3,[x4]}, 130: {x4,[x5,x6]}, {x5,[x7]}, {x6,[x7]}]), 131: ?line Vi = case digraph:get_path(G, x1, x7) of 132: [x1,x2,x4,x5,x7] -> digraph:del_vertex(G, x5), x6; 133: [x1,x2,x4,x6,x7] -> digraph:del_vertex(G, x6), x5; 134: [x1,x3,x4,x5,x7] -> digraph:del_vertex(G, x5), x6; 135: [x1,x3,x4,x6,x7] -> digraph:del_vertex(G, x6), x5 136: end, 137: ?line Vj = case digraph:get_path(G, x1, x7) of 138: [x1,x2,x4,Vi,x7] -> digraph:del_vertex(G,x2), x3; 139: [x1,x3,x4,Vi,x7] -> digraph:del_vertex(G,x3), x2 140: end, 141: ?line [x1,Vj,x4,Vi,x7] = digraph:get_path(G, x1, x7), 142: ?line digraph:del_vertex(G, Vj), 143: ?line false = digraph:get_path(G, x1, x7), 144: ?line [] = check(digraph:vertices(G), [x1,x4,Vi,x7]), 145: digraph:delete(G), 146: ok. 147: 148: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 149: 150: cycle(doc) -> []; 151: cycle(suite) -> []; 152: cycle(Config) when is_list(Config) -> 153: ?line G = build_graph([], [{x1,[x2,x3]}, {x2,[x4]}, {x3,[x4]}, 154: {x4,[x5,x6]}, {x5,[x7]}, {x6,[x7,x8]}, 155: {x8,[x3,x8]}]), 156: ?line false = digraph:get_cycle(G, x1), 157: ?line false = digraph:get_cycle(G, x2), 158: ?line false = digraph:get_cycle(G, x5), 159: ?line false = digraph:get_cycle(G, x7), 160: ?line [x3,x4,x6,x8,x3] = digraph:get_cycle(G, x3), 161: ?line [x4,x6,x8,x3,x4] = digraph:get_cycle(G, x4), 162: ?line [x6,x8,x3,x4,x6] = digraph:get_cycle(G, x6), 163: ?line [x8,x3,x4,x6,x8] = digraph:get_cycle(G, x8), 164: ?line digraph:del_vertex(G, x4), 165: ?line [x8] = digraph:get_cycle(G, x8), 166: digraph:delete(G), 167: ok. 168: 169: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 170: 171: 172: 173: vertices(doc) -> []; 174: vertices(suite) -> []; 175: vertices(Config) when is_list(Config) -> 176: ?line G = build_graph([], [{x,[]}, {y,[]}]), 177: ?line [] = check(digraph:vertices(G), [x,y]), 178: ?line digraph:del_vertices(G, [x,y]), 179: ?line [] = digraph:vertices(G), 180: ?line digraph:delete(G), 181: ok. 182: 183: edges(doc) -> []; 184: edges(suite) -> []; 185: edges(Config) when is_list(Config) -> 186: ?line G = build_graph([], [{x, [{exy,y},{exx,x}]}, 187: {y, [{eyx,x}]} 188: ]), 189: ?line [] = check(digraph:edges(G), [exy, eyx, exx]), 190: ?line [] = check(digraph:out_edges(G, x), [exy,exx]), 191: ?line [] = check(digraph:in_edges(G, x), [eyx,exx]), 192: ?line [] = check(digraph:out_edges(G, y), [eyx]), 193: ?line [] = check(digraph:in_edges(G, y), [exy]), 194: ?line true = digraph:del_edges(G, [exy, eyx, does_not_exist]), 195: ?line [exx] = digraph:edges(G), 196: ?line [] = check(digraph:out_edges(G, x), [exx]), 197: ?line [] = check(digraph:in_edges(G, x), [exx]), 198: ?line [] = check(digraph:out_edges(G, y), []), 199: ?line [] = check(digraph:in_edges(G, y), []), 200: ?line digraph:del_vertices(G, [x,y]), 201: ?line [] = digraph:edges(G), 202: ?line [] = digraph:vertices(G), 203: ?line digraph:delete(G), 204: ok. 205: 206: data(doc) -> []; 207: data(suite) -> []; 208: data(Config) when is_list(Config) -> 209: ?line G = build_graph([], [{x, [{exy, y}]}, {y, []}]), 210: 211: ?line {x,[]} = digraph:vertex(G, x), 212: ?line {y,[]} = digraph:vertex(G, y), 213: ?line {exy,x,y,[]} = digraph:edge(G, exy), 214: 215: ?line digraph:add_edge(G, exy, x, y, {data,x,y}), 216: ?line E = digraph:add_edge(G, x, y, {data,y,x}), 217: ?line digraph:add_vertex(G, x, {any}), 218: ?line digraph:add_vertex(G, y, '_'), 219: 220: ?line {x,{any}} = digraph:vertex(G, x), 221: ?line {y,'_'} = digraph:vertex(G, y), 222: ?line {exy,x,y,{data,x,y}} = digraph:edge(G, exy), 223: ?line {E,x,y,{data,y,x}} = digraph:edge(G, E), 224: ?line true = digraph:del_edge(G, E), 225: ?line false = digraph:edge(G, E), 226: ?line true = sane(G), 227: ?line digraph:delete(G), 228: ok. 229: 230: 231: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 232: 233: 234: 235: otp_3522(doc) -> []; 236: otp_3522(suite) -> []; 237: otp_3522(Config) when is_list(Config) -> 238: ?line G1 = build_graph([acyclic], [{x, []}]), 239: ?line {error, {bad_edge,_}} = digraph:add_edge(G1, x, x), 240: ?line true = digraph:delete(G1), 241: 242: ?line G = digraph:new(), 243: ?line 0 = digraph:no_vertices(G), 244: ?line 0 = digraph:no_edges(G), 245: ?line V1 = digraph:add_vertex(G), 246: ?line '$vid' = digraph:add_vertex(G, '$vid'), 247: ?line V2 = digraph:add_vertex(G), 248: ?line '$eid' = digraph:add_edge(G, '$eid', V1, V2, []), 249: ?line E = digraph:add_edge(G, V1, V2), 250: ?line 3 = digraph:no_vertices(G), 251: ?line 2 = digraph:no_edges(G), 252: ?line cyclic = info(G, cyclicity), 253: ?line protected = info(G, protection), 254: 255: ?line [] = check(digraph:in_edges(G, V2), ['$eid', E]), 256: ?line [] = check(digraph:out_edges(G, V1), ['$eid', E]), 257: ?line [] = check(digraph:vertices(G), [V1,V2,'$vid']), 258: ?line [] = check(digraph:edges(G), [E, '$eid']), 259: ?line true = sane(G), 260: ?line true = digraph:delete(G), 261: ok. 262: 263: otp_3630(doc) -> []; 264: otp_3630(suite) -> []; 265: otp_3630(Config) when is_list(Config) -> 266: ?line G = build_graph([], [{x, [{exy,y},{exx,x}]}, 267: {y, [{eyy,y},{eyx,x}]} 268: ]), 269: ?line [x,y] = digraph:get_path(G, x, y), 270: ?line [y,x] = digraph:get_path(G, y, x), 271: 272: ?line [x,x] = digraph:get_short_path(G, x, x), 273: ?line [y,y] = digraph:get_short_path(G, y, y), 274: ?line true = digraph:delete(G), 275: 276: ?line G1 = build_graph([], [{1, [{12,2},{13,3},{11,1}]}, 277: {2, [{23,3}]}, 278: {3, [{34,4},{35,5}]}, 279: {4, [{45,5}]}, 280: {5, [{56,6},{57,7}]}, 281: {6, [{67,7}]}, 282: {7, [{71,1}]} 283: ]), 284: 285: ?line [1,3,5,7] = digraph:get_short_path(G1, 1, 7), 286: ?line [3,5,7,1,3] = digraph:get_short_cycle(G1, 3), 287: ?line [1,1] = digraph:get_short_cycle(G1, 1), 288: ?line true = digraph:delete(G1), 289: 290: F = 0.0, I = round(F), 291: ?line G2 = digraph:new([acyclic]), 292: ?line digraph:add_vertex(G2, F), 293: ?line digraph:add_vertex(G2, I), 294: ?line E = digraph:add_edge(G2, F, I), 295: ?line true = not is_tuple(E), 296: ?line true = sane(G2), 297: ?line true = digraph:delete(G2), 298: 299: ok. 300: 301: otp_8066(doc) -> []; 302: otp_8066(suite) -> []; 303: otp_8066(Config) when is_list(Config) -> 304: fun() -> 305: D = digraph:new(), 306: V1 = digraph:add_vertex(D), 307: V2 = digraph:add_vertex(D), 308: _ = digraph:add_edge(D, V1, V2), 309: ?line [V1, V2] = digraph:get_path(D, V1, V2), 310: ?line true = sane(D), 311: ?line true = digraph:del_path(D, V1, V2), 312: ?line true = sane(D), 313: ?line false = digraph:get_path(D, V1, V2), 314: ?line true = digraph:del_path(D, V1, V2), 315: ?line true = digraph:delete(D) 316: end(), 317: 318: fun() -> 319: D = digraph:new(), 320: V1 = digraph:add_vertex(D), 321: V2 = digraph:add_vertex(D), 322: _ = digraph:add_edge(D, V1, V2), 323: _ = digraph:add_edge(D, V1, V2), 324: _ = digraph:add_edge(D, V1, V1), 325: _ = digraph:add_edge(D, V2, V2), 326: ?line [V1, V2] = digraph:get_path(D, V1, V2), 327: ?line true = sane(D), 328: ?line true = digraph:del_path(D, V1, V2), 329: ?line false = digraph:get_short_path(D, V2, V1), 330: 331: ?line true = sane(D), 332: ?line false = digraph:get_path(D, V1, V2), 333: ?line true = digraph:del_path(D, V1, V2), 334: ?line true = digraph:delete(D) 335: end(), 336: 337: fun() -> 338: G = digraph:new(), 339: W1 = digraph:add_vertex(G), 340: W2 = digraph:add_vertex(G), 341: W3 = digraph:add_vertex(G), 342: W4 = digraph:add_vertex(G), 343: _ = digraph:add_edge(G,['$e'|0], W1, W2, {}), 344: ?line {error,{bad_vertex, bv}} = 345: digraph:add_edge(G, edge, bv, W1, {}), 346: ?line {error,{bad_vertex, bv}} = 347: digraph:add_edge(G, edge, W1, bv, {}), 348: ?line false = digraph:get_short_cycle(G, W1), 349: ?line {error, {bad_edge,_}} = 350: digraph:add_edge(G,['$e'|0], W3, W4, {}), 351: ?line true = sane(G), 352: ?line true = digraph:delete(G) 353: end(), 354: ok. 355: 356: 357: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 358: 359: sane(G) -> 360: sane1(G), 361: erase(sane) =:= undefined. 362: 363: sane1(G) -> 364: %% etab: {E, V1, V2, Label} 365: %% ntab: {{out,V},E} eller {{in,V},E} 366: %% vtab: {V,Label} 367: 368: Es = digraph:edges(G), 369: Vs = digraph:vertices(G), 370: VEs = lists:flatmap(fun(V) -> digraph:edges(G, V) end, Vs), 371: case lists:sort(Es++Es) =:= lists:sort(VEs) of 372: true -> ok; 373: false -> 374: io:format("Bad edges~n", []), put(sane, no) 375: end, 376: 377: lists:foreach( 378: fun(E) -> 379: Edge = {E, V1, V2, _L} = digraph:edge(G, E), 380: case {digraph:vertex(G, V1), digraph:vertex(G, V2)} of 381: {{V1, _}, {V2, _}} -> ok; 382: _ -> io:format("Missing vertex ~p~n", [Edge]), put(sane, no) 383: end, 384: In = digraph:in_edges(G, V2), 385: case lists:member(E, In) of 386: true -> ok; 387: false -> 388: io:format("Missing in-neighbour ~p~n", [Edge]), 389: put(sane, no) 390: end, 391: Out = digraph:out_edges(G, V1), 392: case lists:member(E, Out) of 393: true -> ok; 394: false -> 395: io:format("Missing out-neighbour ~p~n", [Edge]), 396: put(sane, no) 397: end 398: end, Es), 399: 400: lists:foreach( 401: fun(V) -> 402: InEs = digraph:in_edges(G, V), 403: %% *All* in-edoges of V 404: lists:foreach( 405: fun(E) -> 406: case digraph:edge(G, E) of 407: {E, _, V, _} -> ok; 408: _ -> 409: io:format("Bad in-edge ~p: ~p~n", [V, E]), 410: put(sane, no) 411: end 412: end, InEs), 413: OutEs = digraph:out_edges(G, V), 414: lists:foreach( 415: fun(E) -> 416: case digraph:edge(G, E) of 417: {E, V, _, _} -> ok; 418: _ -> 419: io:format("Bad out-edge ~p: ~p~n", [V, E]), 420: put(sane, no) 421: end 422: end, OutEs) 423: end, Vs), 424: 425: InEs = lists:flatmap(fun(V) -> digraph:in_edges(G, V) end, Vs), 426: OutEs = lists:flatmap(fun(V) -> digraph:out_edges(G, V) end, Vs), 427: lists:foreach( 428: fun(E) -> 429: case digraph:edge(G, E) of 430: {E, _, _, _} -> ok; 431: _ -> 432: io:format("Unknown edge (neighbour) ~p~n", [E]), 433: put(sane, no) 434: end 435: end, InEs++OutEs), 436: 437: N_in = length(InEs), 438: N_out = length(OutEs), 439: N_edges = digraph:no_edges(G), 440: if 441: N_in =/= N_out -> 442: io:format("Number of in- and out-edges differs~n", []), 443: put(sane, no); 444: N_in+N_out =/= N_edges+N_edges -> 445: io:format("Invalid number of edges (~p+~p =/= 2*~p)~n", 446: [N_in, N_out, N_edges]), 447: put(sane, no); 448: true -> ok 449: end, 450: Edges = [digraph:edge(G, E) || E <- Es], 451: EVs = lists:usort([V || {_, V, _, _} <- Edges] ++ 452: [V || {_, _, V, _} <- Edges]), 453: lists:foreach( 454: fun(V) -> 455: case digraph:vertex(G, V) of 456: {_, _} -> ok; 457: false -> 458: io:format("Unknown vertex in edge: ~p~n", [V]), 459: put(sane, no) 460: end 461: end, EVs), 462: 463: %% sink_vertices and source_vertices were introduced in 2001. They 464: %% are not documented. 465: 466: %% sink: a vertex with no outgoing edges 467: SinkVs = [V || V <- Vs, digraph:out_edges(G, V) =:= [] ], 468: case lists:sort(SinkVs) =:= lists:sort(digraph:sink_vertices(G)) of 469: true -> ok; 470: false -> 471: io:format("Bad sinks~n"), put(sane, no) 472: end, 473: %% sink: a vertex with no incoming edges 474: SourceVs = [V || V <- Vs, digraph:in_edges(G, V) =:= [] ], 475: case lists:sort(SourceVs) =:= lists:sort(digraph:source_vertices(G)) of 476: true -> ok; 477: false -> 478: io:format("Bad sources~n"), put(sane, no) 479: end, 480: 481: true. 482: 483: build_graph(Opts, Gs) -> 484: G = digraph:new(Opts), 485: build_g(G, Gs). 486: 487: build_g(G, [{V,Ns} | Gs]) -> 488: digraph:add_vertex(G, V), 489: build_ns(G, V, Ns), 490: build_g(G, Gs); 491: build_g(G, []) -> 492: true = sane(G), 493: G. 494: 495: build_ns(G, V, [{E,W} | Ns]) -> 496: digraph:add_vertex(G, W), 497: digraph:add_edge(G, E, V, W, []), 498: build_ns(G, V, Ns); 499: build_ns(G, V, [W | Ns]) -> 500: digraph:add_vertex(G, W), 501: digraph:add_edge(G, V, W), 502: build_ns(G, V, Ns); 503: build_ns(_G, _V, []) -> 504: true. 505: 506: %% Spawn a process that create a graph return {Pid, Graph} 507: 508: spawn_graph(Opts) -> 509: Pid = spawn(?MODULE, spawn_graph, [self(),Opts]), 510: receive 511: {Pid, G} -> {Pid,G} 512: end. 513: 514: %% Create a graph and wait for die message 515: spawn_graph(Starter, Opts) -> 516: G = digraph:new(Opts), 517: Starter ! {self(), G}, 518: receive 519: die -> true 520: end. 521: 522: info(G, What) -> 523: case lists:keysearch(What, 1, digraph:info(G)) of 524: {value, {What, Value}} -> Value; 525: false -> [] 526: end. 527: 528: %% Kill process created by spawn_graph 529: kill_graph(Pid) -> 530: Pid ! die. 531: 532: check(R0, E0) -> 533: R = lists:sort(R0), 534: E = lists:sort(E0), 535: case R of 536: E -> 537: []; 538: _ -> 539: (R -- E) ++ (E -- R) 540: end.