aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2013-10-04 10:57:07 +0200
committerAnders Svensson <[email protected]>2013-12-01 16:38:52 +0100
commitb4b8a2130348d8828a53303d7003e24abfc692f2 (patch)
tree955fbb5ba98a40bc938811dbd039e8d9ddfb330d /lib/diameter
parent350e7e93696c4a2f553a64232b5a52f256b5c0e7 (diff)
downloadotp-b4b8a2130348d8828a53303d7003e24abfc692f2.tar.gz
otp-b4b8a2130348d8828a53303d7003e24abfc692f2.tar.bz2
otp-b4b8a2130348d8828a53303d7003e24abfc692f2.zip
Fix diameter_make:flatten/1
To set @avp_vendor_id, @codecs and @custom_types as required for imported avps.
Diffstat (limited to 'lib/diameter')
-rw-r--r--lib/diameter/src/compiler/diameter_dict_util.erl5
-rw-r--r--lib/diameter/src/compiler/diameter_make.erl96
-rw-r--r--lib/diameter/test/diameter_compiler_SUITE.erl119
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},
@@ -434,6 +441,104 @@ 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) ->
lists:foldl(fun re/2, Bin, Mods).