From b4b8a2130348d8828a53303d7003e24abfc692f2 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Fri, 4 Oct 2013 10:57:07 +0200 Subject: Fix diameter_make:flatten/1 To set @avp_vendor_id, @codecs and @custom_types as required for imported avps. --- lib/diameter/src/compiler/diameter_dict_util.erl | 5 +- lib/diameter/src/compiler/diameter_make.erl | 96 ++++++++++++++++-- lib/diameter/test/diameter_compiler_SUITE.erl | 119 +++++++++++++++++++++-- 3 files changed, 203 insertions(+), 17 deletions(-) diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl index eef0cb26d4..3941f30e03 100644 --- a/lib/diameter/src/compiler/diameter_dict_util.erl +++ b/lib/diameter/src/compiler/diameter_dict_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -265,6 +265,9 @@ io(K, Id) io(vendor = K, {Id, Name}) -> [?NL, section(K) | [[?SP, tok(X)] || X <- [Id, Name]]]; +io(_, []) -> + []; + io(avp_types = K, Body) -> [?NL, ?NL, section(K), ?NL, [body(K,A) || A <- Body]]; diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl index 411d71ba3b..99034734c5 100644 --- a/lib/diameter/src/compiler/diameter_make.erl +++ b/lib/diameter/src/compiler/diameter_make.erl @@ -107,19 +107,97 @@ format([?VERSION | Dict]) -> -> parsed(). flatten([?VERSION = V | Dict]) -> - [V | lists:foldl(fun flatten/2, Dict, [[avp_types, import_avps], - [grouped, import_groups], - [enum, import_enums]])]. + [V | lists:foldl(fun flatten/2, + Dict, + [avp_vendor_id, + custom_types, + codecs, + [avp_types, import_avps], + [grouped, import_groups], + [enum, import_enums]])]. + +%% =========================================================================== + +%% flatten/2 flatten([_,_] = Keys, Dict) -> [Values, Imports] = [orddict:fetch(K, Dict) || K <- Keys], - Vs = lists:append([Values | [V || {_,V} <- Imports]]), - lists:foldl(fun store/2, + Vs = lists:append([Values | [V || {_Mod, V} <- Imports]]), + lists:foldl(fun({K,V},D) -> orddict:store(K,V,D) end, Dict, - lists:zip([inherits | Keys], [[], Vs, []])). - -store({Key, Value}, Dict) -> - orddict:store(Key, Value, Dict). + lists:zip([inherits | Keys], [[], Vs, []])); + +%% Inherited avp's setting the 'V' flag get their value either from +%% @avp_vendor_id in the inheriting dictionary or from @vendor in the +%% *inherited* (not inheriting) dictionary: add the latter to +%% @avp_vendor_id as required. +flatten(avp_vendor_id = Key, Dict) -> + Def = orddict:find(vendor, Dict), + ModD = imports(Dict), + Vids = orddict:fetch(Key, Dict), + Avps = lists:append([As || {_,As} <- Vids]), + orddict:store(Key, + dict:fold(fun(M, As, A) -> vid(M, As -- Avps, Def, A) end, + Vids, + ModD), + Dict); + +%% Import @codecs and @custom_types from inherited dictionaries as +%% required. +flatten(Key, Dict) -> + ImportAvps = orddict:fetch(import_avps, Dict), + ImportItems = [{M, As} + || {Mod, Avps} <- ImportAvps, + [_|D] <- [Mod:dict()], + {M,As0} <- orddict:fetch(Key, D), + F <- [fun(A) -> lists:keymember(A, 1, Avps) end], + [_|_] = As <- [lists:filter(F, As0)]], + orddict:store(Key, + lists:foldl(fun merge/2, + orddict:fetch(Key, Dict), + ImportItems), + Dict). + +%% merge/2 + +merge({Mod, _Avps} = T, Acc) -> + merge(lists:keyfind(Mod, 1, Acc), T, Acc). + +merge({Mod, Avps}, {Mod, As}, Acc) -> + lists:keyreplace(Mod, 1, Acc, {Mod, Avps ++ As}); +merge(false, T, Acc) -> + [T | Acc]. + +%% imports/1 +%% +%% Return a module() -> [AVP] dict of inherited AVP's setting the V flag. + +imports(Dict) -> + lists:foldl(fun imports/2, + dict:new(), + orddict:fetch(import_avps, Dict)). + +imports({Mod, Avps}, Dict) -> + dict:store(Mod, + [A || {A,_,_,Fs} <- Avps, lists:member($V, Fs)], + Dict). + +%% vid/4 + +vid(_, [], _, Acc) -> + Acc; +vid(Mod, Avps, Def, Acc) -> + v(Mod:vendor_id(), Avps, Def, Acc). + +v(Vid, _, {ok, {Vid, _}}, Acc) -> %% same id as inheriting dictionary's + Acc; +v(Vid, Avps, _, Acc) -> + case lists:keyfind(Vid, 1, Acc) of + {Vid, As} -> + lists:keyreplace(Vid, 1, Acc, {Vid, As ++ Avps}); + false -> + [{Vid, Avps} | Acc] + end. %% =========================================================================== diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl index 943cac1446..a37f52a2c5 100644 --- a/lib/diameter/test/diameter_compiler_SUITE.erl +++ b/lib/diameter/test/diameter_compiler_SUITE.erl @@ -31,10 +31,15 @@ %% testcases -export([format/1, format/2, replace/1, replace/2, - generate/1, generate/4]). + generate/1, generate/4, + flatten1/1, flatten1/3, + flatten2/1]). -export([dict/0]). %% fake dictionary module +%% dictionary callbacks for flatten2/1 +-export(['A1'/3, 'Unsigned32'/3]). + -define(base, "base_rfc3588.dia"). -define(util, diameter_util). -define(S, atom_to_list). @@ -335,7 +340,9 @@ suite() -> all() -> [format, replace, - generate]. + generate, + flatten1, + flatten2]. %% Error handling testcases will make an erroneous dictionary out of %% the base dictionary and check that the expected error results. @@ -361,11 +368,11 @@ format(Config) -> format(Mods, Bin) -> B = modify(Bin, Mods), - {ok, Dict} = make(B, []), - {ok, D} = make(diameter_make:format(Dict), []), + {ok, Dict} = parse(B, []), + {ok, D} = parse(diameter_make:format(Dict), []), {Dict, Dict} = {Dict, D}. -make(File, Opts) -> +parse(File, Opts) -> case diameter_make:codec(File, [parse, hrl, return | Opts]) of {ok, [Dict, _]} -> {ok, Dict}; @@ -387,7 +394,7 @@ replace(Config) -> replace({E, Mods}, Bin) -> B = modify(Bin, Mods), - case {E, make(B, [{include, here()}]), Mods} of + case {E, parse(B, [{include, here()}]), Mods} of {ok, {ok, Dict}, _} -> Dict; {_, {error, S}, _} -> @@ -412,7 +419,7 @@ generate(Config) -> generate(Mods, Bin, N, Mode) -> B = modify(Bin, Mods ++ [{"@name .*", "@name dict" ++ ?L(N)}]), - {ok, Dict} = make(B, []), + {ok, Dict} = parse(B, []), File = "dict" ++ integer_to_list(N), {_, ok} = {Dict, diameter_make:codec(Dict, [{name, File}, @@ -433,6 +440,104 @@ generate(parse, File, Dict) -> generate(hrl, _, _) -> ok. +%% =========================================================================== +%% flatten1/1 + +flatten1(_Config) -> + [Vsn | BaseD] = diameter_gen_base_rfc6733:dict(), + {ok, I} = parse("@inherits diameter_gen_base_rfc6733\n", []), + [Vsn | FlatD] = diameter_make:flatten(I), + [] = ?util:run([{?MODULE, [flatten1, K, BaseD, FlatD]} + || K <- [avp_types, grouped, enum]]). + +flatten1(Key, BaseD, FlatD) -> + Vs = orddict:fetch(Key, BaseD), + Vs = orddict:fetch(Key, FlatD). + +%% =========================================================================== +%% flatten2/1 + +flatten2(_Config) -> + Dict1 = + "@name diameter_test1\n" + "@prefix diameter_test1\n" + "@vendor 666 test\n" + "@avp_vendor_id 111 A1 A3\n" + "@avp_vendor_id 222 A4 A6\n" + "@custom_types " ++ ?S(?MODULE) ++ " A1 A4\n" + "@codecs " ++ ?S(?MODULE) ++ " A3 A6\n" + "@avp_types\n" + "A1 1001 Unsigned32 V\n" + "A2 1002 Unsigned32 V\n" + "A3 1003 Unsigned32 V\n" + "A4 1004 Unsigned32 V\n" + "A5 1005 Unsigned32 V\n" + "A6 1006 Unsigned32 V\n" + "@end ignored\n", + Dict2 = + "@name diameter_test2\n" + "@prefix diameter_test2\n" + "@vendor 777 test\n" + "@inherits diameter_test1 A1 A2 A3\n" + "@inherits diameter_gen_base_rfc6733\n" + "@avp_vendor_id 333 A1\n", + + {ok, [E1, {_,B1}]} + = diameter_make:codec(Dict1, [erl, beam, return]), + ct:pal("~s", [E1]), + {module, diameter_test1} + = code:load_binary(diameter_test1, "diameter_test1", B1), + + + {ok, [D2, E2, {_,B2}]} + = diameter_make:codec(Dict2, [parse, erl, beam, return]), + ct:pal("~s", [E2]), + {module, diameter_test2} + = code:load_binary(diameter_test2, "diameter_test2", B2), + + + Flat = lists:flatten(diameter_make:format(diameter_make:flatten(D2))), + ct:pal("~s", [Flat]), + {ok, [E3, {_,B3}]} + = diameter_make:codec(Flat, [erl, beam, return, + {name, "diameter_test3"}]), + ct:pal("~s", [E3]), + {module, diameter_test3} + = code:load_binary(diameter_test3, "diameter_test3", B3), + + {M1, M2, M3} = {diameter_test1, diameter_test2, diameter_test3}, + + [{1001, 111, M1, 'A1'}, %% @avp_vendor_id + {1002, 666, M1, 'A2'}, %% @vendor + {1003, 111, M1, 'A3'}, %% @avp_vendor_id + {1004, 222, M1, 'A4'}, %% @avp_vendor_id + {1005, 666, M1, 'A5'}, %% @vendor + {1006, 222, M1, 'A6'}, %% @avp_vendor_id + {1001, 333, M2, 'A1'}, %% M2 @avp_vendor_id + {1002, 666, M2, 'A2'}, %% M1 @vendor + {1003, 666, M2, 'A3'}, %% M1 @vendor + {1001, 333, M3, 'A1'}, %% (as for M2) + {1002, 666, M3, 'A2'}, %% " + {1003, 666, M3, 'A3'}] %% " + = [{Code, Vid, Mod, Name} + || Mod <- [M1, M2, M3], + Code <- lists:seq(1001, 1006), + Vid <- [666, 111, 222, 777, 333], + {Name, 'Unsigned32'} <- [Mod:avp_name(Code, Vid)]], + + [] = [{A,T,M,RC} || A <- ['A1', 'A3'], + T <- [encode, decode], + M <- [M2, M3], + Ref <- [make_ref()], + RC <- [M:avp(T, Ref, A)], + RC /= {T, Ref}]. + +'A1'(T, 'Unsigned32', Ref) -> + {T, Ref}. + +'Unsigned32'(T, 'A3', Ref) -> + {T, Ref}. + %% =========================================================================== modify(Bin, Mods) -> -- cgit v1.2.3