aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/src/asn1ct_check.erl16
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl9
-rw-r--r--lib/asn1/src/asn1ct_gen.erl8
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl6
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl10
-rw-r--r--lib/asn1/src/asn1ct_gen_per_rt2ct.erl6
-rw-r--r--lib/asn1/src/asn1rtt_ber.erl2
-rw-r--r--lib/asn1/src/asn1rtt_per.erl4
-rw-r--r--lib/asn1/src/asn1rtt_real_common.erl2
-rw-r--r--lib/asn1/test/Makefile1
-rw-r--r--lib/asn1/test/asn1_SUITE.erl270
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Def.py31
-rw-r--r--lib/asn1/test/asn1_SUITE_data/NullTest.asn114
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Opt.py31
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SeqOf.py45
-rw-r--r--lib/asn1/test/asn1_SUITE_data/SetOf.py42
-rw-r--r--lib/asn1/test/testTCAP.erl2
-rw-r--r--lib/asn1/test/testWSParamClass.erl17
-rw-r--r--lib/common_test/src/ct_logs.erl9
-rw-r--r--lib/common_test/src/ct_run.erl71
-rw-r--r--lib/common_test/src/ct_testspec.erl15
-rw-r--r--lib/common_test/src/ct_util.erl23
-rw-r--r--lib/common_test/src/ct_util.hrl1
-rw-r--r--lib/common_test/test/ct_test_support.erl9
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE.erl33
-rw-r--r--lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl171
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml9
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl35
-rw-r--r--lib/dialyzer/src/dialyzer_callgraph.erl5
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl25
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl7
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl16
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl9
-rw-r--r--lib/dialyzer/test/small_SUITE_data/results/types_arity2
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/types_arity.erl20
-rw-r--r--lib/diameter/doc/src/diameter.xml45
-rw-r--r--lib/diameter/doc/src/diameter_app.xml48
-rw-r--r--lib/diameter/include/diameter.hrl4
-rw-r--r--lib/diameter/src/Makefile16
-rw-r--r--lib/diameter/src/base/diameter.appup.src18
-rw-r--r--lib/diameter/src/base/diameter.erl3
-rw-r--r--lib/diameter/src/base/diameter_config.erl34
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl622
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl6
-rw-r--r--lib/diameter/src/compiler/diameter_nowarn.erl41
-rw-r--r--lib/diameter/src/modules.mk1
-rw-r--r--lib/diameter/test/.gitignore1
-rw-r--r--lib/diameter/test/Makefile23
-rw-r--r--lib/diameter/test/coverspec.sed33
-rw-r--r--lib/diameter/test/diameter_3xxx_SUITE.erl509
-rw-r--r--lib/diameter/test/diameter_app_SUITE.erl3
-rw-r--r--lib/diameter/test/diameter_ct.erl19
-rw-r--r--lib/diameter/test/diameter_failover_SUITE.erl6
-rw-r--r--lib/diameter/test/diameter_length_SUITE.erl43
-rw-r--r--lib/diameter/test/diameter_relay_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_tls_SUITE.erl2
-rw-r--r--lib/diameter/test/diameter_traffic_SUITE.erl34
-rw-r--r--lib/diameter/test/modules.mk29
-rw-r--r--lib/diameter/test/testspec3
-rw-r--r--lib/diameter/vsn.mk2
-rw-r--r--lib/edoc/src/edoc.erl81
-rw-r--r--lib/edoc/src/edoc_doclet.erl6
-rw-r--r--lib/edoc/src/edoc_lib.erl30
-rw-r--r--lib/edoc/src/edoc_macros.erl4
-rw-r--r--lib/edoc/src/edoc_parser.yrl2
-rw-r--r--lib/edoc/src/edoc_report.erl8
-rw-r--r--lib/edoc/src/edoc_run.erl6
-rw-r--r--lib/edoc/src/edoc_tags.erl4
-rw-r--r--lib/edoc/src/edoc_wiki.erl2
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl2
-rw-r--r--lib/hipe/cerl/erl_types.erl48
-rw-r--r--lib/kernel/src/auth.erl2
-rw-r--r--lib/kernel/src/file_server.erl2
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl25
-rw-r--r--lib/kernel/test/code_SUITE.erl8
-rw-r--r--lib/kernel/test/file_SUITE.erl76
-rw-r--r--lib/kernel/test/file_name_SUITE.erl46
-rw-r--r--lib/parsetools/src/leex.erl18
-rw-r--r--lib/parsetools/src/yecc.erl14
-rw-r--r--lib/ssh/doc/src/ssh.xml3
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl.erl111
-rw-r--r--lib/ssl/src/ssl_alert.erl2
-rw-r--r--lib/ssl/src/ssl_connection.erl58
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl75
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl30
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl14
-rw-r--r--lib/ssl/test/ssl_test_lib.erl2
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl2
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/src/c.erl2
-rw-r--r--lib/stdlib/src/qlc.erl2
-rw-r--r--lib/stdlib/test/ets_SUITE.erl116
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl2
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl32
-rw-r--r--lib/syntax_tools/src/igor.erl24
-rw-r--r--lib/test_server/src/test_server_ctrl.erl31
-rw-r--r--lib/test_server/src/ts_run.erl16
98 files changed, 2115 insertions, 1318 deletions
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index dd77085c39..452862fcee 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -1263,13 +1263,13 @@ check_object_list(S,ClassRef,[ObjOrSet|Objs],Acc) ->
check_object_list(S,ClassRef,Objs,[{{no_mod,no_name},Def}|Acc]);
{'ObjectSetFromObjects',Os,FieldName} when is_tuple(Os) ->
NewSet =
- check_ObjectSetFromObjects(S,element(size(Os),Os),
+ check_ObjectSetFromObjects(S, element(tuple_size(Os), Os),
FieldName,[]),
check_object_list(S,ClassRef,Objs,NewSet++Acc);
{{'ObjectSetFromObjects',Os,FieldName},InterSection}
when is_tuple(Os) ->
NewSet =
- check_ObjectSetFromObjects(S, element(size(Os),Os),
+ check_ObjectSetFromObjects(S, element(tuple_size(Os), Os),
FieldName,InterSection),
check_object_list(S,ClassRef,Objs,NewSet++Acc);
Other ->
@@ -1570,7 +1570,7 @@ gen_incl_set(S,Fields,#typedef{typespec=#type{def=Eref}})
gen_incl_set(S,Fields,CDef);
gen_incl_set(S,Fields,ClassDef) ->
case catch get_unique_fieldname(S,ClassDef) of
- Tuple when is_tuple(Tuple), size(Tuple) =:= 3 ->
+ Tuple when tuple_size(Tuple) =:= 3 ->
false;
_ ->
gen_incl_set1(S,Fields,
@@ -1589,7 +1589,7 @@ gen_incl_set1(_,['EXTENSIONMARK'],_) ->
gen_incl_set1(_,['EXTENSIONMARK'|_],_) ->
true;
gen_incl_set1(S,[Object|Rest],CFields)->
- Fields = element(size(Object),Object),
+ Fields = element(tuple_size(Object), Object),
case gen_incl1(S,Fields,CFields) of
true ->
true;
@@ -3028,7 +3028,7 @@ is_record_normalized(S,Name,V = #'Externalvaluereference'{},NumComps) ->
_ -> false
end;
is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) ->
- (size(Value) =:= (NumComps + 1)) andalso (element(1,Value)=:=Name);
+ (tuple_size(Value) =:= (NumComps + 1)) andalso (element(1, Value) =:= Name);
is_record_normalized(_,_,_,_) ->
false.
@@ -3720,7 +3720,7 @@ maybe_open_type(S,ClassSpec=#objectclass{fields=Fs},
{typefieldreference,_} ->
case {catch get_unique_fieldname(S,#classdef{typespec=ClassSpec}),
asn1ct_gen:get_constraint(Constr,componentrelation)}of
- {Tuple,_} when is_tuple(Tuple), size(Tuple) =:= 3 ->
+ {Tuple,_} when tuple_size(Tuple) =:= 3 ->
OCFT#'ObjectClassFieldType'{fieldname=FieldNames,
type='ASN1_OPEN_TYPE'};
{_,no} ->
@@ -4167,7 +4167,7 @@ check_constraint(S,Ext) when is_record(Ext,'Externaltypereference') ->
check_constraint(S,{'SizeConstraint',{Lb,Ub}})
- when is_list(Lb);is_tuple(Lb),size(Lb)==2 ->
+ when is_list(Lb); tuple_size(Lb) =:= 2 ->
NewLb = range_check(resolv_tuple_or_list(S,Lb)),
NewUb = range_check(resolv_tuple_or_list(S,Ub)),
{'SizeConstraint',{NewLb,NewUb}};
@@ -5217,7 +5217,7 @@ imported1(_Name,[]) ->
check_integer(_S,[],_C) ->
[];
check_integer(S,NamedNumberList,_C) ->
- case [X||X<-NamedNumberList,is_tuple(X),size(X)=:=2] of
+ case [X || X <- NamedNumberList, tuple_size(X) =:= 2] of
NamedNumberList ->
%% An already checked integer with NamedNumberList
NamedNumberList;
diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index e82212f0d8..341a04761b 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -528,14 +528,7 @@ gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when is_record(D,type) ->
Atom when is_atom(Atom) -> Atom;
_ -> TypeNameSuffix
end,
-%% fix me
- ObjFun =
- case D#type.tablecinf of
- [{objfun,_}|_R] ->
- ", ObjFun";
- _ ->
- []
- end,
+ ObjFun = false,
gen_dec_line(Erules,TypeName,ContName,[],Cont,mandatory,ObjFun),
%% gen_dec_line_sof(Erules,Typename,ContName,Cont,ObjFun),
emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]).
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index ebc52df1d9..76c4182160 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -657,9 +657,13 @@ gen_check_sof(Name,SOF,Type) ->
end,
emit({" ",{asis,NewName},"(DVs,Vs).",nl,nl}).
+gen_check_sequence(Name, []) ->
+ emit([{asis,ensure_atom(Name)},"(_,_) ->",nl,
+ " throw(badval).",nl,nl]);
gen_check_sequence(Name,Components) ->
emit([{asis,ensure_atom(Name)},"(DefaultValue,Value) ->",nl]),
gen_check_sequence(Name,Components,1).
+
gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
InnerType = get_inner(Type#type.def),
NthDefV = ["element(",Num+1,",DefaultValue)"],
@@ -671,9 +675,7 @@ gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
_ ->
emit({",",nl}),
gen_check_sequence(Name,Cs,Num+1)
- end;
-gen_check_sequence(_,[],_) ->
- ok.
+ end.
gen_check_choice(Name,CList=[#'ComponentType'{}|_Cs]) ->
emit([{asis,ensure_atom(Name)},"({Id,DefaultValue},{Id,Value}) ->",nl]),
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index f3a2486565..de0adef2b2 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -1162,7 +1162,7 @@ gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
emit({indent(3),"fun(_, Val, _RestPrimFieldName) ->",nl}),
emit({indent(6),"Len = case Val of",nl,indent(9),
- "Bin when is_binary(Bin) -> size(Bin);",nl,indent(9),
+ "Bin when is_binary(Bin) -> byte_size(Bin);",nl,indent(9),
"_ -> length(Val)",nl,indent(6),"end,"}),
emit({indent(6),"{Val,Len}",nl}),
emit({indent(3),"end.",nl,nl}),
@@ -1270,7 +1270,7 @@ gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
%% treatment.
emit([";",nl,indent(9),{asis,Name}," ->",nl]),
emit([indent(12),"Len = case Val of",nl,
- indent(15),"Bin when is_binary(Bin) -> size(Bin);",nl,
+ indent(15),"Bin when is_binary(Bin) -> byte_size(Bin);",nl,
indent(15),"_ -> length(Val)",nl,indent(12),"end,",nl,
indent(12),"{Val,Len}"]),
{Acc,0}
@@ -1449,7 +1449,7 @@ gen_inlined_dec_funs(Fields,[{typefield,Name,Prop}|Rest],
nl,indent(6),"case Type of",nl,
indent(9),{asis,Name}," ->",nl,
indent(12),"Len = case Bytes of",nl,
- indent(15),"B when is_binary(B) -> size(B);",nl,
+ indent(15),"B when is_binary(B) -> byte_size(B);",nl,
indent(15),"_ -> length(Bytes)",nl,
indent(12),"end,",nl,
indent(12),"{Bytes,[],Len}"]),
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 0d6620667f..fac233532b 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -1174,12 +1174,12 @@ gen_dec_imm_1('UTF8String', _Constraint, Aligned) ->
asn1ct_imm:per_dec_restricted_string(Aligned);
gen_dec_imm_1('REAL', _Constraint, Aligned) ->
asn1ct_imm:per_dec_real(Aligned);
-gen_dec_imm_1(#'ObjectClassFieldType'{}=TypeName, Constraint, Aligned) ->
+gen_dec_imm_1(#'ObjectClassFieldType'{}=TypeName, _Constraint, Aligned) ->
case asn1ct_gen:get_inner(TypeName) of
- {fixedtypevaluefield,_,InnerType} ->
- gen_dec_imm_1(InnerType, Constraint, Aligned);
- T ->
- gen_dec_imm_1(T, Constraint, Aligned)
+ {fixedtypevaluefield,_,#type{def=InnerType,constraint=C}} ->
+ gen_dec_imm_1(InnerType, C, Aligned);
+ #type{def=T,constraint=C} ->
+ gen_dec_imm_1(T, C, Aligned)
end.
gen_dec_bit_string(F, Imm) ->
diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
index 5a409295fb..81d8cdcae6 100644
--- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
+++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
@@ -610,9 +610,9 @@ gen_encode_objectfields(Erules,ClassName,[{typefield,Name,OptOrMand}|Rest],
emit([" if",nl,
" is_list(Val) ->",nl,
" NewVal = list_to_binary(Val),",nl,
- " [20,size(NewVal),NewVal];",nl,
+ " [20,byte_size(NewVal),NewVal];",nl,
" is_binary(Val) ->",nl,
- " [20,size(Val),Val]",nl,
+ " [20,byte_size(Val),Val]",nl,
" end"]),
[];
{false,{'DEFAULT',DefaultType}} ->
@@ -989,7 +989,7 @@ gen_objset_enc(_Erule,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
emit({indent(9),"is_list(Val) -> list_to_binary(Val);",nl}),
emit({indent(9),"true -> Val",nl}),
emit({indent(6),"end,",nl}),
- emit({indent(6),"Size = size(BinVal),",nl}),
+ emit({indent(6),"Size = byte_size(BinVal),",nl}),
emit({indent(6),"if",nl}),
emit({indent(9),"Size < 256 ->",nl}),
emit({indent(12),"[20,Size,BinVal];",nl}),
diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl
index 88292aca99..5fbf116747 100644
--- a/lib/asn1/src/asn1rtt_ber.erl
+++ b/lib/asn1/src/asn1rtt_ber.erl
@@ -868,7 +868,7 @@ remove_unused_then_dotag(TagIn,Unused,BinBits) ->
encode_tags(TagIn, <<0>>, 1);
0 ->
Bin = <<Unused,BinBits/binary>>,
- encode_tags(TagIn,Bin,size(Bin));
+ encode_tags(TagIn, Bin, byte_size(Bin));
Num ->
N = byte_size(BinBits)-1,
<<BBits:N/binary,LastByte>> = BinBits,
diff --git a/lib/asn1/src/asn1rtt_per.erl b/lib/asn1/src/asn1rtt_per.erl
index d02f4f548e..84ff809912 100644
--- a/lib/asn1/src/asn1rtt_per.erl
+++ b/lib/asn1/src/asn1rtt_per.erl
@@ -613,11 +613,11 @@ bit_string_trailing_zeros1(BitList,Lb,Ub) ->
encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList)
when is_integer(C),C=<16 ->
range_check(C, bit_size(BinBits) - Unused),
- [45,C,size(BinBits),BinBits];
+ [45,C,byte_size(BinBits),BinBits];
encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList)
when is_integer(C), C =< 255 ->
range_check(C, bit_size(BinBits) - Unused),
- [2,45,C,size(BinBits),BinBits];
+ [2,45,C,byte_size(BinBits),BinBits];
encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList)
when is_integer(C), C =< 65535 ->
range_check(C, bit_size(BinBits) - Unused),
diff --git a/lib/asn1/src/asn1rtt_real_common.erl b/lib/asn1/src/asn1rtt_real_common.erl
index 540f0d60a5..d1668f68b2 100644
--- a/lib/asn1/src/asn1rtt_real_common.erl
+++ b/lib/asn1/src/asn1rtt_real_common.erl
@@ -88,7 +88,7 @@ encode_real(_C, {Mantissa, Base, Exponent}) when Base =:= 2 ->
end,
%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
SFactor = 0,
- OctExpLen = size(OctExp),
+ OctExpLen = byte_size(OctExp),
if OctExpLen > 255 ->
exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
true -> true %% make real assert later..
diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile
index 1fa495d8f1..10f8e2833b 100644
--- a/lib/asn1/test/Makefile
+++ b/lib/asn1/test/Makefile
@@ -109,6 +109,7 @@ MODULES= \
test_modified_x420 \
testX420 \
test_x691 \
+ testWSParamClass \
asn1_test_lib \
asn1_app_test \
asn1_appup_test \
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index be9b82cddf..62418e554e 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -86,6 +86,7 @@ groups() ->
testInvokeMod,
per,
ber_other,
+ der,
h323test,
per_GeneralString]},
testChoPrim,
@@ -166,13 +167,13 @@ groups() ->
testINSTANCE_OF,
testTCAP,
test_ParamTypeInfObj,
- test_WS_ParamClass,
test_Defed_ObjectIdentifier,
testSelectionType,
testSSLspecs,
testNortel,
- % Uses 'PKCS7'
- {group, [], [test_modified_x420,
+ % Uses 'PKCS7', 'InformationFramework'
+ {group, [], [test_WS_ParamClass,
+ test_modified_x420,
testX420]},
testTcapsystem,
testNBAPsystem,
@@ -200,8 +201,6 @@ parallel(Options) ->
%%------------------------------------------------------------------------------
init_per_suite(Config) ->
- PrivDir = ?config(priv_dir, Config),
- true = code:add_patha(PrivDir),
Config.
end_per_suite(_Config) ->
@@ -214,7 +213,7 @@ end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(Func, Config) ->
- CaseDir = filename:join([?config(priv_dir, Config), ?MODULE, Func]),
+ CaseDir = filename:join(?config(priv_dir, Config), Func),
ok = filelib:ensure_dir(filename:join([CaseDir, dummy_file])),
true = code:add_patha(CaseDir),
@@ -351,7 +350,7 @@ testPrimStrings(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
[compact_bit_string,Rule|Opts]),
testPrimStrings:bit_string(Rule),
- ?only_ber(testPrimStrings:more_strings(Rule)).
+ testPrimStrings:more_strings(Rule).
testPrimStrings_cases(Rule) ->
testPrimStrings:bit_string(Rule),
@@ -368,10 +367,10 @@ testPrimExternal(Config, Rule, Opts) ->
asn1_test_lib:compile_all(["External", "PrimExternal"], Config,
[Rule|Opts]),
testPrimExternal:external(Rule),
- ?only_ber(asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
- [Rule|Opts])),
- ?only_ber(testPrimStrings_cases(Rule)),
- ?only_ber(testPrimStrings:more_strings(Rule)).
+ asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config,
+ [Rule|Opts]),
+ testPrimStrings_cases(Rule),
+ testPrimStrings:more_strings(Rule).
testChoPrim(Config) -> test(Config, fun testChoPrim/3).
testChoPrim(Config, Rule, Opts) ->
@@ -634,9 +633,10 @@ c_syntax(Config) ->
"SeqBadComma"]].
c_string(Config) ->
- test(Config, fun c_string/3, [per, ber]).
+ test(Config, fun c_string/3).
c_string(Config, Rule, Opts) ->
- asn1_test_lib:compile("String", Config, [Rule|Opts]).
+ asn1_test_lib:compile("String", Config, [Rule|Opts]),
+ asn1ct:test('String').
c_implicit_before_choice(Config) ->
test(Config, fun c_implicit_before_choice/3, [ber]).
@@ -688,6 +688,8 @@ ber_other(Config) ->
ber_other(Config, Rule, Opts) ->
[module_test(M, Config, Rule, Opts) || M <- ber_modules()].
+der(Config) ->
+ asn1_test_lib:compile_all(ber_modules(), Config, [der]).
module_test(M, Config, Rule, Opts) ->
asn1_test_lib:compile(M, Config, [Rule|Opts]),
@@ -740,13 +742,9 @@ value_test(Config, Rule, Opts) ->
'ObjIdValues':'mobileDomainId'()).
value_bad_enum_test(Config) ->
- case ?MODULE of
- asn1_SUITE ->
- {error, _} = asn1ct:compile(?config(data_dir, Config)
- ++ "BadEnumValue1",
- [{outdir, ?config(case_dir, Config)}]);
- _ -> {skip, "Runs in asn1_SUITE only"}
- end.
+ {error, _} = asn1ct:compile(?config(data_dir, Config) ++
+ "BadEnumValue1",
+ [{outdir, ?config(case_dir, Config)}]).
constructed(Config) ->
test(Config, fun constructed/3, [ber]).
@@ -861,18 +859,13 @@ testInvokeMod(Config, Rule, Opts) ->
{ok, _Result2} = 'PrimStrings':encode('Bs1', [1, 0, 1, 0]).
testExport(Config) ->
- case ?MODULE of
- asn1_SUITE ->
- {error, {asn1, _Reason}} =
- asn1ct:compile(filename:join(?config(data_dir, Config),
- "IllegalExport"),
- [{outdir, ?config(case_dir, Config)}]);
- _ ->
- {skip, "Runs in asn1_SUITE only"}
- end.
+ {error, {asn1, _Reason}} =
+ asn1ct:compile(filename:join(?config(data_dir, Config),
+ "IllegalExport"),
+ [{outdir, ?config(case_dir, Config)}]).
testImport(Config) ->
- test(Config, fun testImport/3, [ber]).
+ test(Config, fun testImport/3).
testImport(Config, Rule, Opts) ->
{error, _} = asn1ct:compile(filename:join(?config(data_dir, Config),
"ImportsFrom"),
@@ -910,18 +903,14 @@ testOpenTypeImplicitTag(Config, Rule, Opts) ->
testOpenTypeImplicitTag:main(Rule).
duplicate_tags(Config) ->
- case ?MODULE of
- asn1_SUITE ->
- DataDir = ?config(data_dir, Config),
- CaseDir = ?config(case_dir, Config),
- {error, {asn1, [{error, {type, _, _, 'SeqOpt1Imp', {asn1, {duplicates_of_the_tags, _}}}}]}} =
- asn1ct:compile(filename:join(DataDir, "SeqOptional2"),
- [abs, {outdir, CaseDir}]);
- _ ->
- {skip, "Runs in asn1_SUITE only"}
- end.
+ DataDir = ?config(data_dir, Config),
+ CaseDir = ?config(case_dir, Config),
+ {error, {asn1, [{error, {type, _, _, 'SeqOpt1Imp',
+ {asn1, {duplicates_of_the_tags, _}}}}]}} =
+ asn1ct:compile(filename:join(DataDir, "SeqOptional2"),
+ [abs, {outdir, CaseDir}]).
-rtUI(Config) -> test(Config, fun rtUI/3, [per,ber]).
+rtUI(Config) -> test(Config, fun rtUI/3).
rtUI(Config, Rule, Opts) ->
asn1_test_lib:compile("Prim", Config, [Rule|Opts]),
{ok, _} = asn1rt:info('Prim').
@@ -937,7 +926,7 @@ testINSTANCE_OF(Config, Rule, Opts) ->
testINSTANCE_OF:main(Rule).
testTCAP(Config) ->
- test(Config, fun testTCAP/3, [ber]).
+ test(Config, fun testTCAP/3).
testTCAP(Config, Rule, Opts) ->
testTCAP:compile(Config, [Rule|Opts]),
testTCAP:test(Rule, Config),
@@ -988,11 +977,16 @@ test_driver_load(Config, Rule, Opts) ->
test_ParamTypeInfObj(Config) ->
asn1_test_lib:compile("IN-CS-1-Datatypes", Config, [ber]).
-test_WS_ParamClass(Config) ->
- asn1_test_lib:compile("InformationFramework", Config, [ber]).
+test_WS_ParamClass(Config) -> test(Config, fun test_WS_ParamClass/3).
+test_WS_ParamClass(Config, Rule, Opts) ->
+ asn1_test_lib:compile("InformationFramework", Config, [Rule|Opts]),
+ ?only_ber(testWSParamClass:main(Rule)),
+ ok.
test_Defed_ObjectIdentifier(Config) ->
- asn1_test_lib:compile("UsefulDefinitions", Config, [ber]).
+ test(Config, fun test_Defed_ObjectIdentifier/3).
+test_Defed_ObjectIdentifier(Config, Rule, Opts) ->
+ asn1_test_lib:compile("UsefulDefinitions", Config, [Rule|Opts]).
testSelectionType(Config) -> test(Config, fun testSelectionType/3).
testSelectionType(Config, Rule, Opts) ->
@@ -1020,7 +1014,7 @@ test_undecoded_rest(Config, Rule, Opts) ->
test_undecoded_rest:test(undec_rest, Config).
testTcapsystem(Config) ->
- test(Config, fun testTcapsystem/3, [ber]).
+ test(Config, fun testTcapsystem/3).
testTcapsystem(Config, Rule, Opts) ->
testTcapsystem:compile(Config, [Rule|Opts]).
@@ -1123,6 +1117,7 @@ test_modules() ->
"Int",
"MAP-commonDataTypes",
"Null",
+ "NullTest",
"Octetstr",
"One",
"P-Record",
@@ -1264,189 +1259,6 @@ smp(Config) ->
{skipped,"No smp support"}
end.
-per_performance(Config) ->
- PrivDir = proplists:get_value(priv_dir, Config),
- NifDir = filename:join(PrivDir,"nif"),
- ErlDir = filename:join(PrivDir,"erl"),
- file:make_dir(NifDir),file:make_dir(ErlDir),
-
- Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ok = testNBAPsystem:compile([{priv_dir,NifDir}|Config], [per]),
- ok = testNBAPsystem:compile([{priv_dir,ErlDir}|Config], [per]),
-
- Modules = ['NBAP-CommonDataTypes',
- 'NBAP-Constants',
- 'NBAP-Containers',
- 'NBAP-IEs',
- 'NBAP-PDU-Contents',
- 'NBAP-PDU-Discriptions'],
-
-
- PreNif = fun() ->
- code:add_patha(NifDir),
- lists:foreach(fun(M) ->
- code:purge(M),
- code:load_file(M)
- end,Modules)
- end,
-
- PreErl = fun() ->
- code:add_patha(ErlDir),
- lists:foreach(fun(M) ->
- code:purge(M),
- code:load_file(M)
- end,Modules)
- end,
-
- Func = fun() ->
- element(1,timer:tc(
- asn1_wrapper,encode,['NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- Msg]))
- end,
-
- nif_vs_erlang_performance({{{PreNif,Func},{PreErl,Func}},100000,32}).
-
-ber_performance(Config) ->
-
- Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ok = testNBAPsystem:compile(Config, [ber]),
-
-
- BerFun = fun() ->
- {ok,B} = asn1_wrapper:encode('NBAP-PDU-Discriptions',
- 'NBAP-PDU', Msg),
- asn1_wrapper:decode(
- 'NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- B)
- end,
- nif_vs_erlang_performance({BerFun,100000,32}).
-
-cert_pem_performance(Config) when is_list(Config) ->
- cert_pem_performance({100000, 32});
-cert_pem_performance({N,S}) ->
- nif_vs_erlang_performance({fun pem_performance:cert_pem/0,N,S}).
-
-dsa_pem_performance(Config) when is_list(Config) ->
- dsa_pem_performance({100000, 32});
-dsa_pem_performance({N,S}) ->
- nif_vs_erlang_performance({fun pem_performance:dsa_pem/0,N,S}).
-
-
-nif_vs_erlang_performance({{TC1,TC2},N,Sched}) ->
- random:seed({123,456,789}),
- io:format("Running a ~p sample with ~p max procs...~n~n",[N,Sched]),
-
- {True,False} = exec(TC1,TC2,Sched,N+1),
-
- io:format("~ndone!~n"),
-
- io:format("~n"),TStats = print_stats(strip(True,N div 20)),
- io:format("~n"),FStats = print_stats(strip(False,N div 20)),
- Str = io_lib:format("~nNifs are ~.3f% faster than erlang!~n",
- [(element(2,FStats) - element(2,TStats)) /
- element(2,FStats) * 100]),
- io:format(Str),
- {comment, lists:flatten(Str)};
-nif_vs_erlang_performance({T,N,Sched}) ->
- PTC1 = fun() ->
- application:set_env(asn1, nif_loadable, true)
- end,
- PTC2 = fun() ->
- application:set_env(asn1, nif_loadable, false)
- end,
- TC = fun() ->
- element(1,timer:tc(T))
- end,
- nif_vs_erlang_performance({{{PTC1,TC},{PTC2,TC}},N,Sched}).
-
-
-print_stats(Data) ->
- Length = length(Data),
- Mean = lists:sum(Data) / Length,
- Variance = lists:foldl(fun(N,Acc) -> math:pow(N - Mean, 2)+Acc end, 0, Data),
- StdDev = math:sqrt(Variance / Length),
- Median = lists:nth(round(Length/2),Data),
- Min = lists:min(Data),
- Max = lists:max(Data),
- if Length < 20 ->
- io:format("Data: ~w~n",[Data]);
- true ->
- ok
- end,
- io:format("Length: ~p~nMean: ~p~nStdDev: ~p~nMedian: ~p~nMin: ~p~nMax: ~p~n",
- [Length,Mean,StdDev,Median,Min,Max]),
- {Length,Mean,StdDev,Median,Min,Max}.
-
-collect(Acc) ->
- receive
- {Tag,Val} ->
- Prev = proplists:get_value(Tag,Acc,[]),
- collect(lists:keystore(Tag,1,Acc,{Tag,[Val|Prev]}))
- after 100 ->
- Acc
- end.
-
-exec(One,Two,Max,N) ->
- exec(One,Two,Max,N,{[],[]}).
-exec(_,_,_,1,{D1,D2}) ->
- {lists:flatten(D1),lists:flatten(D2)};
-exec({PreOne,One} = O,{PreTwo,Two} = T,MaxProcs, N, {D1,D2}) ->
- Num = random:uniform(round(N/2)),
- if Num rem 3 == 0 ->
- timer:sleep(Num rem 1000);
- true ->
- ok
- end,
- Procs = random:uniform(MaxProcs),
- io:format("\tBatch: ~p items in ~p processes, ~p left~n",[Num,Procs,N-Num]),
- if Num rem 2 == 1 ->
- erlang:garbage_collect(),
- PreOne(),
- MoreOne = pexec(One, Num, Procs, []),
- erlang:garbage_collect(),
- PreTwo(),
- MoreTwo = pexec(Two, Num, Procs, []);
- true ->
- erlang:garbage_collect(),
- PreTwo(),
- MoreTwo = pexec(Two, Num, Procs, []),
- erlang:garbage_collect(),
- PreOne(),
- MoreOne = pexec(One, Num, Procs, [])
- end,
- exec(O,T,MaxProcs,N-Num,{[MoreOne|D1],
- [MoreTwo|D2]}).
-
-pexec(_Fun, _, 0, []) ->
- [];
-pexec(Fun, _, 0, [{Ref,Pid}|Rest]) ->
- receive
- {data,D} ->
- [D|pexec(Fun,0,0,[{Ref,Pid}|Rest])];
- {'DOWN', Ref, process, Pid, normal} ->
- pexec(Fun, 0,0,Rest)
- end;
-pexec(Fun, 0, 1, AccProcs) ->
- pexec(Fun, 0, 0, AccProcs);
-pexec(Fun, N, 1, AccProcs) ->
- [Fun()|pexec(Fun, N - 1, 1, AccProcs)];
-pexec(Fun, N, Procs, AccProcs) ->
- S = self(),
- Pid = spawn(fun() ->
- S ! {data,pexec(Fun,N,1,[])}
- end),
- Ref = erlang:monitor(process, Pid),
- pexec(Fun, N, Procs - 1, [{Ref,Pid}|AccProcs]).
-
-strip(Data,Num) ->
- {_,R} = lists:split(Num,lists:sort(Data)),
- element(2,lists:split(Num,lists:reverse(R))).
-
-faster(A,B) ->
- (B - A)/B * 100.
-
enc_dec(1, Msg, N) ->
worker_loop(N, Msg);
enc_dec(NumOfProcs,Msg, N) ->
diff --git a/lib/asn1/test/asn1_SUITE_data/Def.py b/lib/asn1/test/asn1_SUITE_data/Def.py
deleted file mode 100644
index ff08ed6386..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/Def.py
+++ /dev/null
@@ -1,31 +0,0 @@
-Def DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-Def1 ::= SEQUENCE
-{
- bool0 [0] BOOLEAN,
- bool1 [1] BOOLEAN DEFAULT false,
- bool2 [2] BOOLEAN DEFAULT false,
- bool3 [3] BOOLEAN DEFAULT false
-}
-
-
-Def2 ::= SEQUENCE
-{
- bool10 [10] BOOLEAN,
- bool11 [11] BOOLEAN DEFAULT false,
- bool12 [12] BOOLEAN DEFAULT false,
- bool13 [13] BOOLEAN
-}
-
-
-Def3 ::= SEQUENCE
-{
- bool30 [30] BOOLEAN DEFAULT false,
- bool31 [31] BOOLEAN DEFAULT false,
- bool32 [32] BOOLEAN DEFAULT false,
- bool33 [33] BOOLEAN DEFAULT false
-}
-
-END
diff --git a/lib/asn1/test/asn1_SUITE_data/NullTest.asn1 b/lib/asn1/test/asn1_SUITE_data/NullTest.asn1
new file mode 100644
index 0000000000..041b20a4c1
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/NullTest.asn1
@@ -0,0 +1,14 @@
+NullTest DEFINITIONS ::=
+BEGIN
+
+NullTestData ::= SEQUENCE {
+ body NullBody,
+ tail INTEGER
+}
+
+NullBody ::= CHOICE {
+ null [0] NULL,
+ notNull [1] INTEGER
+}
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/Opt.py b/lib/asn1/test/asn1_SUITE_data/Opt.py
deleted file mode 100644
index 48c2a09b64..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/Opt.py
+++ /dev/null
@@ -1,31 +0,0 @@
-Opt DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-Opt1 ::= SEQUENCE
-{
- bool0 [0] BOOLEAN,
- bool1 [1] BOOLEAN OPTIONAL,
- bool2 [2] BOOLEAN OPTIONAL,
- bool3 [3] BOOLEAN OPTIONAL
-}
-
-
-Opt2 ::= SEQUENCE
-{
- bool10 [10] BOOLEAN,
- bool11 [11] BOOLEAN OPTIONAL,
- bool12 [12] BOOLEAN OPTIONAL,
- bool13 [13] BOOLEAN
-}
-
-
-Opt3 ::= SEQUENCE
-{
- bool30 [30] BOOLEAN OPTIONAL,
- bool31 [31] BOOLEAN OPTIONAL,
- bool32 [32] BOOLEAN OPTIONAL,
- bool33 [33] BOOLEAN OPTIONAL
-}
-
-END
diff --git a/lib/asn1/test/asn1_SUITE_data/SeqOf.py b/lib/asn1/test/asn1_SUITE_data/SeqOf.py
deleted file mode 100644
index c941418934..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/SeqOf.py
+++ /dev/null
@@ -1,45 +0,0 @@
-SeqOf DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-
-Seq1 ::= SEQUENCE
-{
- bool1 BOOLEAN,
- int1 INTEGER,
- seq1 SEQUENCE OF SeqIn DEFAULT {}
-}
-
-Seq2 ::= SEQUENCE
-{
- seq2 SEQUENCE OF SeqIn DEFAULT {},
- bool2 BOOLEAN,
- int2 INTEGER
-}
-
-Seq3 ::= SEQUENCE
-{
- bool3 BOOLEAN,
- seq3 SEQUENCE OF SeqIn DEFAULT {},
- int3 INTEGER
-}
-
-Seq4 ::= SEQUENCE
-{
- seq41 [41] SEQUENCE OF SeqIn DEFAULT {},
- seq42 [42] SEQUENCE OF SeqIn DEFAULT {},
- seq43 [43] SEQUENCE OF SeqIn DEFAULT {}
-}
-
-
-
-SeqIn ::= SEQUENCE
-{
- boolIn BOOLEAN,
- intIn INTEGER
-}
-
-
-
-
-END
diff --git a/lib/asn1/test/asn1_SUITE_data/SetOf.py b/lib/asn1/test/asn1_SUITE_data/SetOf.py
deleted file mode 100644
index 4e2ea16fcc..0000000000
--- a/lib/asn1/test/asn1_SUITE_data/SetOf.py
+++ /dev/null
@@ -1,42 +0,0 @@
-SetOf DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
-
-Set1 ::= SET
-{
- bool1 BOOLEAN,
- int1 INTEGER,
- set1 SET OF SetIn DEFAULT {}
-}
-
-Set2 ::= SET
-{
- set2 SET OF SetIn DEFAULT {},
- bool2 BOOLEAN,
- int2 INTEGER
-}
-
-Set3 ::= SET
-{
- bool3 BOOLEAN,
- set3 SET OF SetIn DEFAULT {},
- int3 INTEGER
-}
-
-Set4 ::= SET
-{
- set41 [41] SET OF SetIn DEFAULT {},
- set42 [42] SET OF SetIn DEFAULT {},
- set43 [43] SET OF SetIn DEFAULT {}
-}
-
-
-
-SetIn ::= SET
-{
- boolIn BOOLEAN,
- intIn INTEGER
-}
-
-END
diff --git a/lib/asn1/test/testTCAP.erl b/lib/asn1/test/testTCAP.erl
index b723995e40..354b6c5ea4 100644
--- a/lib/asn1/test/testTCAP.erl
+++ b/lib/asn1/test/testTCAP.erl
@@ -37,7 +37,7 @@ compile_asn1config(Config, Options) ->
asn1_test_lib:compile_all(Files, Config, Options),
asn1_test_lib:compile_erlang("TCAPPackage_msg", Config, []).
-test(ber=Erule,_Config) ->
+test(Erule,_Config) ->
% ?line OutDir = ?config(priv_dir,Config),
%% testing OTP-4798, open type encoded with indefinite length
?line {ok,_Res} = asn1_wrapper:decode('TCAPMessages-simple','MessageType', val_OTP_4798(Erule)),
diff --git a/lib/asn1/test/testWSParamClass.erl b/lib/asn1/test/testWSParamClass.erl
new file mode 100644
index 0000000000..ae67ca8b81
--- /dev/null
+++ b/lib/asn1/test/testWSParamClass.erl
@@ -0,0 +1,17 @@
+-module(testWSParamClass).
+-export([main/1]).
+
+main(_) ->
+ IF = 'InformationFramework',
+ roundtrip({'Attribute',IF:'id-at-objectClass'(),
+ [IF:'id-at-objectClass'()],
+ asn1_NOVALUE}),
+ roundtrip({'Attribute',IF:'id-at-objectClass'(),
+ [],[]}),
+ ok.
+
+roundtrip(Data) ->
+ IF = 'InformationFramework',
+ {ok,Enc} = asn1_wrapper:encode(IF, 'Attribute', Data),
+ {ok,Data} = IF:decode('Attribute', Enc),
+ ok.
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index 5924930072..0b204a681a 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -397,9 +397,9 @@ tc_print(Category,Format,Args) ->
%%% <p>This function is called by <code>ct</code> when printing
%%% stuff from a testcase on the user console.</p>
tc_print(Category,Importance,Format,Args) ->
- VLvl = case ct_util:get_testdata({verbosity,Category}) of
+ VLvl = case ct_util:get_verbosity(Category) of
undefined ->
- ct_util:get_testdata({verbosity,'$unspecified'});
+ ct_util:get_verbosity('$unspecified');
{error,bad_invocation} ->
?MAX_VERBOSITY;
Val ->
@@ -1475,8 +1475,9 @@ count_cases(Dir) ->
write_summary(SumFile, Summary),
Summary
end;
- {error, _Reason} ->
- io:format("\nFailed to read ~p (skipped)\n", [LogFile]),
+ {error, Reason} ->
+ io:format("\nFailed to read ~p: ~p (skipped)\n",
+ [LogFile,Reason]),
error
end
end.
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index c0bdbb2a09..49f00429ae 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -2291,8 +2291,12 @@ add_jobs([{TestDir,all,_}|Tests], Skip, Opts, CleanUp) ->
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, CleanUp)
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, CleanUp);
+ _ ->
+ CleanUp
+ end
end;
add_jobs([{TestDir,[Suite],all}|Tests], Skip,
Opts, CleanUp) when is_atom(Suite) ->
@@ -2305,8 +2309,12 @@ add_jobs([{TestDir,Suites,all}|Tests], Skip,
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, CleanUp)
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, CleanUp);
+ _ ->
+ CleanUp
+ end
end;
add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) ->
case maybe_interpret(Suite, all, Opts) of
@@ -2318,8 +2326,12 @@ add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) ->
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2358,8 +2370,12 @@ add_jobs([{TestDir,Suite,Confs}|Tests], Skip, Opts, CleanUp) when
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2384,8 +2400,12 @@ add_jobs([{TestDir,Suite,Cases}|Tests],
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2401,8 +2421,12 @@ add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opts, CleanUp) when is_atom(Case) -
{'EXIT',_} ->
CleanUp;
_ ->
- wait_for_idle(),
- add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ case wait_for_idle() of
+ ok ->
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp]);
+ _ ->
+ CleanUp
+ end
end;
Error ->
Error
@@ -2412,7 +2436,13 @@ add_jobs([], _, _, CleanUp) ->
wait_for_idle() ->
ct_util:update_last_run_index(),
- Notify = fun(Me) -> Me ! idle end,
+ Notify = fun(Me,IdleState) -> Me ! {idle,IdleState},
+ receive
+ {Me,proceed} -> ok
+ after
+ 30000 -> ok
+ end
+ end,
case catch test_server_ctrl:idle_notify(Notify) of
{'EXIT',_} ->
error;
@@ -2420,11 +2450,14 @@ wait_for_idle() ->
%% so we don't hang forever if test_server dies
Ref = erlang:monitor(process, TSPid),
Result = receive
- idle -> ok;
+ {idle,abort} -> aborted;
+ {idle,_} -> ok;
{'DOWN', Ref, _, _, _} -> error
end,
erlang:demonitor(Ref, [flush]),
ct_util:update_last_run_index(),
+ %% let test_server_ctrl proceed (and possibly shut down) now
+ TSPid ! {self(),proceed},
Result
end.
@@ -2921,11 +2954,11 @@ opts2args(EnvStartOpts) ->
[{event_handler_init,[atom_to_list(EH),ArgStr]}];
({event_handler,{EHs,Arg}}) when is_list(EHs) ->
ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
- Strs = lists:map(fun(EH) ->
- [atom_to_list(EH),
- ArgStr,"and"]
- end, EHs),
- [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
+ Strs = lists:flatmap(fun(EH) ->
+ [atom_to_list(EH),
+ ArgStr,"and"]
+ end, EHs),
+ [_LastAnd | StrsR] = lists:reverse(Strs),
[{event_handler_init,lists:reverse(StrsR)}];
({logopts,LOs}) when is_list(LOs) ->
[{logopts,[atom_to_list(LO) || LO <- LOs]}];
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index e341391a91..71b03c0ea6 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1020,17 +1020,6 @@ add_tests([],Spec) -> % done
%% check if it's a CT term that has bad format or if the user seems to
%% have added something of his/her own, which we'll let pass if relaxed
%% mode is enabled.
-check_term(Atom) when is_atom(Atom) ->
- Valid = valid_terms(),
- case lists:member(Atom,Valid) of
- true ->
- valid;
- false -> % ignore
- case get(relaxed) of
- true -> invalid;
- false -> throw({error,{undefined_term_in_spec,Atom}})
- end
- end;
check_term(Term) when is_tuple(Term) ->
Size = size(Term),
[Name|_] = tuple_to_list(Term),
@@ -1059,9 +1048,7 @@ check_term(Term) when is_tuple(Term) ->
throw({error,{undefined_term_in_spec,Term}})
end
end
- end;
-check_term(Other) ->
- throw({error,{undefined_term_in_spec,Other}}).
+ end.
%% specific data handling before saving in testspec record, e.g.
%% converting relative paths to absolute for directories and files
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 0f2b2081d9..2e7e731595 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -39,7 +39,8 @@
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
delete_testdata/0, delete_testdata/1,
set_testdata/1, get_testdata/1, get_testdata/2,
- set_testdata_async/1, update_testdata/2, update_testdata/3]).
+ set_testdata_async/1, update_testdata/2, update_testdata/3,
+ set_verbosity/1, get_verbosity/1]).
-export([override_silence_all_connections/0, override_silence_connections/1,
get_overridden_silenced_connections/0,
@@ -128,6 +129,10 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
create_table(?conn_table,#conn.handle),
create_table(?board_table,2),
create_table(?suite_table,#suite_data.key),
+
+ create_table(?verbosity_table,1),
+ [ets:insert(?verbosity_table,{Cat,Lvl}) || {Cat,Lvl} <- Verbosity],
+
{ok,StartDir} = file:get_cwd(),
case file:set_cwd(LogDir) of
ok -> ok;
@@ -202,7 +207,7 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
self() ! {{stop,{self(),{user_error,CTHReason}}},
{Parent,make_ref()}}
end,
- loop(Mode, [{{verbosity,Cat},Lvl} || {Cat,Lvl} <- Verbosity], StartDir).
+ loop(Mode, [], StartDir).
create_table(TableName,KeyPos) ->
create_table(TableName,set,KeyPos).
@@ -278,6 +283,19 @@ reset_cwd() ->
get_start_dir() ->
call(get_start_dir).
+%% handle verbosity outside ct_util_server (let the client read
+%% the verbosity table) to avoid possible deadlock situations
+set_verbosity(Elem = {_Category,_Level}) ->
+ ets:insert(?verbosity_table, Elem),
+ ok.
+get_verbosity(Category) ->
+ case ets:lookup(?verbosity_table, Category) of
+ [{Category,Level}] ->
+ Level;
+ _ ->
+ undefined
+ end.
+
loop(Mode,TestData,StartDir) ->
receive
{update_last_run_index,From} ->
@@ -377,6 +395,7 @@ loop(Mode,TestData,StartDir) ->
ets:delete(?conn_table),
ets:delete(?board_table),
ets:delete(?suite_table),
+ ets:delete(?verbosity_table),
ct_logs:close(Info, StartDir),
ct_event:stop(),
ct_config:stop(),
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index c9c6514fa4..7c2e31f40c 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -21,6 +21,7 @@
-define(conn_table,ct_connections).
-define(board_table,ct_boards).
-define(suite_table,ct_suite_data).
+-define(verbosity_table,ct_verbosity_table).
-record(conn, {handle,
targetref,
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 7c33fd404d..5e109e98e9 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -312,8 +312,10 @@ wait_for_ct_stop(Retries, CTNode) ->
undefined ->
true;
Pid ->
+ Info = (catch process_info(Pid)),
test_server:format(0, "Waiting for CT (~p) to finish (~p)...",
[Pid,Retries]),
+ test_server:format(0, "Process info for ~p:~n~p", [Info]),
timer:sleep(5000),
wait_for_ct_stop(Retries-1, CTNode)
end.
@@ -328,12 +330,17 @@ handle_event(EH, Event) ->
start_event_receiver(Config) ->
CTNode = proplists:get_value(ct_node, Config),
- spawn_link(CTNode, fun() -> er() end).
+ Level = proplists:get_value(trace_level, Config),
+ ER = spawn_link(CTNode, fun() -> er() end),
+ test_server:format(Level, "~nEvent receiver ~w started!~n", [ER]),
+ ER.
get_events(_, Config) ->
CTNode = proplists:get_value(ct_node, Config),
+ Level = proplists:get_value(trace_level, Config),
{event_receiver,CTNode} ! {self(),get_events},
Events = receive {event_receiver,Evs} -> Evs end,
+ test_server:format(Level, "Stopping event receiver!~n", []),
{event_receiver,CTNode} ! stop,
Events.
diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl
index 349319de94..ff4c05ce3a 100644
--- a/lib/common_test/test/ct_verbosity_SUITE.erl
+++ b/lib/common_test/test/ct_verbosity_SUITE.erl
@@ -44,8 +44,11 @@
%% there will be clashes with logging processes etc).
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- Config1 = ct_test_support:init_per_suite(Config),
- Config1.
+ DataDir = ?config(data_dir, Config),
+ EvH = filename:join(DataDir,"simple_evh.erl"),
+ ct:pal("Compiling ~s: ~p", [EvH,compile:file(EvH,[{outdir,DataDir},
+ debug_info])]),
+ ct_test_support:init_per_suite([{path_dirs,[DataDir]} | Config]).
end_per_suite(Config) ->
ct_test_support:end_per_suite(Config).
@@ -56,7 +59,8 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{timetrap,{minutes,2}},
+ {ct_hooks,[ts_install_cth]}].
all() ->
[
@@ -67,7 +71,8 @@ all() ->
change_default,
combine_categories,
testspec_only,
- merge_with_testspec
+ merge_with_testspec,
+ possible_deadlock
].
%%--------------------------------------------------------------------
@@ -173,6 +178,17 @@ merge_with_testspec(Config) ->
ok = execute(TC, Opts, ERPid, Config).
%%%-----------------------------------------------------------------
+%%%
+possible_deadlock(Config) ->
+ TC = possible_deadlock,
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, "io_test_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},{label,TC},
+ {event_handler,[simple_evh]}], Config),
+ ok = execute(TC, Opts, ERPid, Config).
+
+
+%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -180,7 +196,14 @@ setup(Test, Config) ->
Opts0 = ct_test_support:get_opts(Config),
Level = ?config(trace_level, Config),
EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
- Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ Opts =
+ case proplists:get_value(event_handler, Test) of
+ undefined ->
+ Opts0 ++ [{event_handler,{?eh,EvHArgs}} | Test];
+ EvHs ->
+ Opts0 ++ [{event_handler,{[?eh|EvHs],EvHArgs}} |
+ proplists:delete(event_handler, Test)]
+ end,
ERPid = ct_test_support:start_event_receiver(Config),
{Opts,ERPid}.
diff --git a/lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl b/lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl
new file mode 100644
index 0000000000..b677e601fb
--- /dev/null
+++ b/lib/common_test/test/ct_verbosity_SUITE_data/simple_evh.erl
@@ -0,0 +1,171 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2012. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Framework Event Handler
+%%%
+%%% <p>This module implements an event handler that CT uses to
+%%% handle status and progress notifications during test runs.
+%%% The notifications are handled locally (per node) and passed
+%%% on to ct_master when CT runs in distributed mode. This
+%%% module may be used as a template for other event handlers
+%%% that can be plugged in to handle local logging and reporting.</p>
+-module(simple_evh).
+
+-behaviour(gen_event).
+
+%% gen_event callbacks
+-export([init/1, handle_event/2, handle_call/2,
+ handle_info/2, terminate/2, code_change/3]).
+
+-include_lib("common_test/include/ct_event.hrl").
+-include_lib("common_test/src/ct_util.hrl").
+
+%%====================================================================
+%% gen_event callbacks
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State}
+%% Description: Whenever a new event handler is added to an event manager,
+%% this function is called to initialize the event handler.
+%%--------------------------------------------------------------------
+init(_) ->
+ io:format("Event handler ~w started!~n", [?MODULE]),
+ {ok,[]}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_event(Event, State) -> {ok, State} |
+%% {swap_handler, Args1, State1, Mod2, Args2} |
+%% remove_handler
+%% Description:Whenever an event manager receives an event sent using
+%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
+%% each installed event handler to handle the event.
+%%--------------------------------------------------------------------
+handle_event(Event = #event{name = test_stats},State) ->
+ %% this could cause a deadlock
+ ct:pal("~p: ~p~n", [Event#event.name,Event#event.data]),
+ {ok,State};
+handle_event(_Event,State) ->
+ {ok,State}.
+
+%%============================== EVENTS ==============================
+%%
+%% Name = test_start
+%% Data = {StartTime,LogDir}
+%%
+%% Name = start_info
+%% Data = {Tests,Suites,Cases}
+%% Tests = Suites = Cases = integer()
+%%
+%% Name = test_done
+%% Data = EndTime
+%%
+%% Name = start_make
+%% Data = Dir
+%%
+%% Name = finished_make
+%% Data = Dir
+%%
+%% Name = tc_start
+%% Data = {Suite,CaseOrGroup}
+%% CaseOrGroup = atom() | {Conf,GroupName,GroupProperties}
+%% Conf = init_per_group | end_per_group
+%% GroupName = atom()
+%% GroupProperties = list()
+%%
+%% Name = tc_done
+%% Data = {Suite,CaseOrGroup,Result}
+%% CaseOrGroup = atom() | {Conf,GroupName,GroupProperties}
+%% Conf = init_per_group | end_per_group
+%% GroupName = atom()
+%% GroupProperties = list()
+%% Result = ok | {skipped,Reason} | {failed,Reason}
+%%
+%% Name = tc_user_skip
+%% Data = {Suite,Case,Comment}
+%% Comment = string()
+%%
+%% Name = tc_auto_skip
+%% Data = {Suite,Case,Comment}
+%% Comment = string()
+%%
+%% Name = test_stats
+%% Data = {Ok,Failed,Skipped}
+%% Ok = Failed = integer()
+%% Skipped = {UserSkipped,AutoSkipped}
+%% UserSkipped = AutoSkipped = integer()
+%%
+%% Name = start_logging
+%% Data = CtRunDir
+%%
+%% Name = stop_logging
+%% Data = []
+%%
+%% Name = start_write_file
+%% Data = FullNameFile
+%%
+%% Name = finished_write_file
+%% Data = FullNameFile
+%%
+%% Name =
+%% Data =
+%%
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_call(Request, State) -> {ok, Reply, State} |
+%% {swap_handler, Reply, Args1, State1,
+%% Mod2, Args2} |
+%% {remove_handler, Reply}
+%% Description: Whenever an event manager receives a request sent using
+%% gen_event:call/3,4, this function is called for the specified event
+%% handler to handle the request.
+%%--------------------------------------------------------------------
+handle_call(_Req, State) ->
+ Reply = ok,
+ {ok, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% Function:
+%% handle_info(Info, State) -> {ok, State} |
+%% {swap_handler, Args1, State1, Mod2, Args2} |
+%% remove_handler
+%% Description: This function is called for each installed event handler when
+%% an event manager receives any other message than an event or a synchronous
+%% request (or a system message).
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description:Whenever an event handler is deleted from an event manager,
+%% this function is called. It should be the opposite of Module:init/1 and
+%% do any necessary cleaning up.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 5e0c9b51e3..0ac96e8ac9 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -158,6 +158,9 @@
<item>Make Dialyzer a bit more quiet.</item>
<tag><c><![CDATA[--verbose]]></c></tag>
<item>Make Dialyzer a bit more verbose.</item>
+ <tag><c><![CDATA[--statistics]]></c></tag>
+ <item>Prints information about the progress of execution (analysis phases,
+ time spent in each and size of the relative input).</item>
<tag><c><![CDATA[--build_plt]]></c></tag>
<item>The analysis starts from an empty plt and creates a new one from
the files specified with <c><![CDATA[-c]]></c> and
@@ -228,6 +231,9 @@
match.</item>
<tag><c><![CDATA[-Wno_opaque]]></c></tag>
<item>Suppress warnings for violations of opaqueness of data types.</item>
+ <tag><c><![CDATA[-Wno_behaviours]]></c>***</tag>
+ <item>Suppress warnings about behaviour callbacks which drift from the
+ published recommended interfaces.</item>
<tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
<item>Include warnings for function calls which ignore a structured return
value or do not match against one of many possible return
@@ -237,9 +243,6 @@
exception.</item>
<tag><c><![CDATA[-Wrace_conditions]]></c>***</tag>
<item>Include warnings for possible race conditions.</item>
- <tag><c><![CDATA[-Wbehaviours]]></c>***</tag>
- <item>Include warnings about behaviour callbacks which drift from the
- published recommended interfaces.</item>
<tag><c><![CDATA[-Wunderspecs]]></c>***</tag>
<item>Warn about underspecified functions
(the -spec is strictly more allowing than the success typing).</item>
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 36aef2a37f..bf0d08cf8f 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -25,8 +25,6 @@
%%%
%%% Created : 28 Oct 2009 by Stavros Aronis <[email protected]>
%%%-------------------------------------------------------------------
-%%% NOTE: This module is currently experimental -- do NOT rely on it!
-%%%-------------------------------------------------------------------
-module(dialyzer_behaviours).
@@ -127,15 +125,12 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest],
erl_types:t_to_string(CbReturnType, Records)]}|Acc00]
end
end,
- Acc02 =
- case erl_types:any_none(
- erl_types:t_inf_lists(ArgTypes, CbArgTypes)) of
- false -> Acc01;
- true ->
- find_mismatching_args(type, ArgTypes, CbArgTypes, Behaviour,
- Function, Arity, Records, 1, Acc01)
- end,
- Acc02
+ case erl_types:any_none(erl_types:t_inf_lists(ArgTypes, CbArgTypes)) of
+ false -> Acc01;
+ true ->
+ find_mismatching_args(type, ArgTypes, CbArgTypes, Behaviour,
+ Function, Arity, Records, 1, Acc01)
+ end
end,
Acc2 =
case dialyzer_codeserver:lookup_mfa_contract(CbMFA, Codeserver) of
@@ -157,16 +152,14 @@ check_all_callbacks(Module, Behaviour, [Cb|Rest],
erl_types:t_to_string(ExtraType, Records),
erl_types:t_to_string(CbReturnType, Records)]}|Acc10]
end,
- Acc12 =
- case erl_types:any_none(
- erl_types:t_inf_lists(SpecArgTypes, CbArgTypes)) of
- false -> Acc11;
- true ->
- find_mismatching_args({spec, File, Line}, SpecArgTypes,
- CbArgTypes, Behaviour, Function,
- Arity, Records, 1, Acc11)
- end,
- Acc12
+ case erl_types:any_none(
+ erl_types:t_inf_lists(SpecArgTypes, CbArgTypes)) of
+ false -> Acc11;
+ true ->
+ find_mismatching_args({spec, File, Line}, SpecArgTypes,
+ CbArgTypes, Behaviour, Function,
+ Arity, Records, 1, Acc11)
+ end
end,
NewAcc = Acc2,
check_all_callbacks(Module, Behaviour, Rest, State, NewAcc).
diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl
index 64e0ee88af..9e9226fa5a 100644
--- a/lib/dialyzer/src/dialyzer_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_callgraph.erl
@@ -798,10 +798,7 @@ condensation(G) ->
fun({V1, V2}) ->
I1 = ets:lookup_element(V2I, V1, 2),
I2 = ets:lookup_element(V2I, V2, 2),
- case I1 =:= I2 of
- true -> true;
- false -> ets:insert(I2I, {I1, I2})
- end
+ I1 =:= I2 orelse ets:insert(I2I, {I1, I2})
end,
lists:foreach(Fun1, digraph:edges(G)),
Fun3 =
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 6732d96b98..967aa989fb 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -488,6 +488,7 @@ expand_dependent_modules_1([Mod|Mods], Included, ModDeps) ->
expand_dependent_modules_1([], Included, _ModDeps) ->
Included.
+-define(MIN_PARALLELISM, 7).
-define(MIN_FILES_FOR_NATIVE_COMPILE, 20).
-spec hipe_compile([file:filename()], #options{}) -> 'ok'.
@@ -501,11 +502,14 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
case erlang:system_info(hipe_architecture) of
undefined -> ok;
_ ->
- Mods = [lists, dict, gb_sets, gb_trees, ordsets, sets,
+ Mods = [lists, dict, digraph, digraph_utils, ets,
+ gb_sets, gb_trees, ordsets, sets, sofs,
cerl, cerl_trees, erl_types, erl_bif_types,
- dialyzer_analysis_callgraph, dialyzer_codeserver,
- dialyzer_dataflow, dialyzer_dep, dialyzer_plt,
- dialyzer_succ_typings, dialyzer_typesig],
+ dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
+ dialyzer_codeserver, dialyzer_contracts,
+ dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
+ dialyzer_plt, dialyzer_succ_typings, dialyzer_typesig,
+ dialyzer_typesig, dialyzer_worker],
report_native_comp(Options),
{T1, _} = statistics(wall_clock),
native_compile(Mods),
@@ -515,12 +519,12 @@ hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
end.
native_compile(Mods) ->
- case erlang:system_info(schedulers) of
- %% N when N > 1 ->
- %% Parent = self(),
- %% Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods],
- %% lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
- _ -> % 1 ->
+ case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
+ true ->
+ Parent = self(),
+ Pids = [spawn(fun () -> Parent ! {self(), hc(M)} end) || M <- Mods],
+ lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
+ false ->
lists:foreach(fun (Mod) -> hc(Mod) end, Mods)
end.
@@ -529,6 +533,7 @@ hc(Mod) ->
case code:is_module_native(Mod) of
true -> ok;
false ->
+ %% io:format(" ~s", [Mod]),
{ok, Mod} = hipe:c(Mod),
ok
end.
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 9989118671..5109bf968a 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -100,10 +100,7 @@ ets_dict_to_dict(Table) ->
ets:foldl(Fold, dict:new(), Table).
ets_set_is_element(Key, Table) ->
- case ets:lookup(Table, Key) of
- [] -> false;
- _ -> true
- end.
+ ets:lookup(Table, Key) =/= [].
ets_set_insert_set(Set, Table) ->
ets_set_insert_list(sets:to_list(Set), Table).
@@ -116,7 +113,7 @@ ets_set_to_set(Table) ->
ets:foldl(Fold, sets:new(), Table).
ets_read_concurrent_table(Name) ->
- ets:new(Name,[{read_concurrency, true}]).
+ ets:new(Name, [{read_concurrency, true}]).
%%--------------------------------------------------------------------
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 0df003a035..40d8936afa 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -1543,12 +1543,11 @@ get_bif_constr({erlang, '==', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
mk_constraint(Arg1, sub, ArgV1),
mk_constraint(Arg2, sub, ArgV2)]);
get_bif_constr({erlang, element, 2} = _BIF, Dst, Args,
- #state{cs = Constrs} = State) ->
+ #state{cs = Constrs, opaques = Opaques}) ->
GenType = erl_bif_types:type(erlang, element, 2),
case t_is_none(GenType) of
true -> ?debug("Bif: ~w failed\n", [_BIF]), throw(error);
false ->
- Opaques = State#state.opaques,
Fun = fun(Map) ->
[I, T] = ATs = lookup_type_list(Args, Map),
ATs2 = case lists:member(T, Opaques) of
@@ -2582,19 +2581,8 @@ enter_type(Key, Val, Map) when is_integer(Key) ->
end
end;
enter_type(Key, Val, Map) ->
- ?debug("Entering ~s :: ~s\n", [format_type(Key), format_type(Val)]),
KeyName = t_var_name(Key),
- case t_is_any(Val) of
- true ->
- erase_type(KeyName, Map);
- false ->
- LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT),
- case dict:find(KeyName, Map) of
- {ok, LimitedVal} -> Map;
- {ok, _} -> map_store(KeyName, LimitedVal, Map);
- error -> map_store(KeyName, LimitedVal, Map)
- end
- end.
+ enter_type(KeyName, Val, Map).
enter_type_lists([Key|KeyTail], [Val|ValTail], Map) ->
Map1 = enter_type(Key, Val, Map),
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 8046b48838..dc8e825199 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -219,15 +219,18 @@ get_record_and_type_info([], _Module, Records, RecDict) ->
end.
add_new_type(TypeOrOpaque, Name, TypeForm, ArgForms, Module, RecDict) ->
- case erl_types:type_is_defined(TypeOrOpaque, Name, RecDict) of
+ Arity = length(ArgForms),
+ case erl_types:type_is_defined(TypeOrOpaque, Name, Arity, RecDict) of
true ->
- throw({error, flat_format("Type ~s already defined\n", [Name])});
+ Msg = flat_format("Type ~s/~w already defined\n", [Name, Arity]),
+ throw({error, Msg});
false ->
ArgTypes = [erl_types:t_from_form(X) || X <- ArgForms],
case lists:all(fun erl_types:t_is_var/1, ArgTypes) of
true ->
ArgNames = [erl_types:t_var_name(X) || X <- ArgTypes],
- dict:store({TypeOrOpaque, Name}, {Module, TypeForm, ArgNames}, RecDict);
+ dict:store({TypeOrOpaque, Name, Arity},
+ {Module, TypeForm, ArgNames}, RecDict);
false ->
throw({error, flat_format("Type declaration for ~w does not "
"have variables as parameters", [Name])})
diff --git a/lib/dialyzer/test/small_SUITE_data/results/types_arity b/lib/dialyzer/test/small_SUITE_data/results/types_arity
new file mode 100644
index 0000000000..02641bd167
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/results/types_arity
@@ -0,0 +1,2 @@
+
+types_arity.erl:16: Invalid type specification for function types_arity:test2/0. The success typing is () -> {'node','a','nil','nil'}
diff --git a/lib/dialyzer/test/small_SUITE_data/src/types_arity.erl b/lib/dialyzer/test/small_SUITE_data/src/types_arity.erl
new file mode 100644
index 0000000000..4ddc986ea8
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/types_arity.erl
@@ -0,0 +1,20 @@
+-module(types_arity).
+
+-export([ test1/0
+ , test2/0
+ , test3/0
+ ]).
+
+-export_type([tree/0, tree/1]).
+
+-type tree(T) :: 'nil' | {'node', T, tree(T), tree(T)}.
+-type tree() :: tree(integer()).
+
+-spec test1() -> tree().
+test1() -> {node, 7, nil, nil}.
+
+-spec test2() -> tree().
+test2() -> {node, a, nil, nil}.
+
+-spec test3() -> tree(atom()).
+test3() -> {node, a, nil, nil}.
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index ba9225da8b..379e9f0738 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -16,7 +16,7 @@
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -188,7 +188,7 @@ Defaults to the value of the <c>alias</c> option if unspecified.</p>
<item>
<p>
Specifies whether or not the &app_pick_peer;
-application callback can modify the application state,
+application callback can modify the application state.
Defaults to <c>false</c> if unspecified.</p>
<note>
@@ -206,11 +206,13 @@ probably avoid it.</p>
<item>
<p>
Determines the manner in which incoming answer messages containing
-decode errors are handled.
+decode errors are handled.</p>
+
+<p>
If <c>callback</c> then errors result in a &app_handle_answer;
callback in the same fashion as for &app_handle_request;, with
errors communicated in the <c>errors</c> field of the
-<c>#diameter_packet{}</c> record passed to the callback.
+<c>#diameter_packet{}</c> passed to the callback.
If <c>report</c> then an answer containing errors is discarded
without a callback and a warning report is written to the log.
If <c>discard</c> then an answer containing errors is silently
@@ -224,6 +226,39 @@ question is as if a callback had taken place and returned
Defaults to <c>report</c> if unspecified.</p>
</item>
+<tag><c>{request_errors, answer_3xxx|answer|callback}</c></tag>
+<item>
+<p>
+Determines the manner in which incoming requests are handled when an
+error other than 3007, DIAMETER_APPLICATION_UNSUPPORTED (which cannot
+be associated with an application callback module), is detected.</p>
+
+<p>
+If <c>answer_3xxx</c> then requests are answered without a
+&app_handle_request; callback taking place.
+If <c>answer</c> then even 5xxx errors are answered without a
+callback unless the connection in question has configured the RFC 3588
+common dictionary as noted below.
+If <c>callback</c> then a &app_handle_request; callback always takes
+place and the return value determines the answer sent to the peer.</p>
+
+<p>
+Defaults to <c>answer_3xxx</c> if unspecified.</p>
+
+<note>
+<p>
+Answers sent by diameter set the E-bit in the Diameter Header.
+Since RFC 3588 allowed only 3xxx result codes in an
+<c>answer-message</c>, <c>answer</c> has the same semantics as
+<c>answer_3xxx</c> if the peer connection in question has configured
+the RFC 3588 common dictionary, <c>diameter_gen_base_rfc3588</c>.
+RFC 6733 allows both 3xxx and 5xxx result codes in an
+<c>answer-message</c> so a connection configured with the RFC 6733
+common dictionary, <c>diameter_gen_base_rfc6733</c>, does
+distinguish between <c>answer_3xxx</c> and <c>answer</c>.</p>
+</note>
+</item>
+
</taglist>
<marker id="call_opt"/>
@@ -534,7 +569,7 @@ Pkt = #diameter_packet{}
The RFC 3539 watchdog state machine has
transitioned into (<c>up</c>) or out of (<c>down</c>) the OKAY
state.
-If a <c>#diameter_packet{}</c> record is present in an <c>up</c> event
+If a <c>#diameter_packet{}</c> is present in an <c>up</c> event
then there has been a capabilties exchange on a newly established
transport connection and the record contains the received CER or CEA.
Otherwise a connection has reestablished without the loss or
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index f4db625c71..d0f1b22ebd 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -13,7 +13,7 @@
<header>
<copyright>
-<year>2011</year><year>2012</year>
+<year>2011</year><year>2013</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -475,6 +475,7 @@ not selected.</p>
| discard
| {eval|eval_packet, Action, PostF}</v>
<v>Reply = {reply, &packet; | &message;}
+ | {answer_message, 3000..3999|5000..5999}
| {protocol_error, 3000..3999}</v>
<v>Opt = &mod_call_opt;</v>
<v>PostF = &mod_evaluable;</v>
@@ -509,14 +510,15 @@ Otherwise it contains the record representing the request as outlined
in &dict;.</p>
<p>
-The <c>errors</c> field specifies any Result-Code's identifying errors
-that were encountered in decoding the request.
-In this case diameter will set both Result-Code and
-Failed-AVP AVP's in a returned
-answer &message; before sending it to the peer:
-the returned &message; need only set any other required AVP's.
-Note that the errors detected by diameter are all of the 5xxx series
-(Permanent Failures).
+The <c>errors</c> field specifies any results codes identifying errors
+found while decoding the request.
+This is used to set Result-Code and/or Failed-AVP in a returned
+answer unless the callback returns a <c>#diameter_packet{}</c>
+whose <c>errors</c> field is set to either a non-empty list of its
+own, in which case this list is used instead, or the atom <c>false</c>
+to disable any setting of Result-Code and Failed-AVP.
+Note that the errors detected by diameter are of the 3xxx
+and 5xxx series, Protocol Errors and Permanent Failures respectively.
The <c>errors</c> list is empty if the request has been received in
the relay application.</p>
@@ -544,24 +546,25 @@ preserved in the outgoing answer, appropriate values otherwise
being set by diameter.</p>
</item>
-<tag><c>{protocol_error, 3000..3999}</c></tag>
+<tag><c>{answer_message, 3000..3999|5000..5999}</c></tag>
<item>
<p>
Send an answer message to the peer containing the specified
-protocol error.
+Result-Code.
Equivalent to</p>
<pre>
{reply, ['answer-message' | Avps]
</pre>
<p>
where <c>Avps</c> sets the Origin-Host, Origin-Realm, the specified
-Result-Code and (if the request sent one) Session-Id AVP's.</p>
+Result-Code and (if the request contained one) Session-Id AVP's.</p>
<p>
-Note that &the_rfc; mandates that only answers with a 3xxx series
-Result-Code (protocol errors) may set the E bit.
-Returning a non-3xxx value in a <c>protocol_error</c> tuple
-will cause the request process in question to fail.</p>
+Returning a value other than 3xxx or 5xxx will cause the request
+process in question to fail, as will returning a 5xxx value if the
+peer connection in question has been configured with the RFC 3588
+common dictionary <c>diameter_gen_base_rfc3588</c>.
+(Since RFC 3588 only allows 3xxx values in an answer-message.)</p>
</item>
<tag><c>{relay, Opts}</c></tag>
@@ -614,11 +617,20 @@ containing the encoded binary.
The return value is ignored.</p>
</item>
+<tag><c>{protocol_error, 3000..3999}</c></tag>
+<item>
+<p>
+Equivalent to <c>{answer_message, 3000..3999}</c>.</p>
+</item>
+
</taglist>
+<note>
<p>
-Note that protocol errors detected by diameter will result in an
-answer message without <c>handle_request/3</c> being invoked.</p>
+Requests containing errors may be answered by diameter, without a
+callback taking place, depending on the value of the
+&mod_application_opt; <c>request_errors</c>.</p>
+</note>
</desc>
</func>
diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl
index 5ee898c3dd..79c4dce541 100644
--- a/lib/diameter/include/diameter.hrl
+++ b/lib/diameter/include/diameter.hrl
@@ -143,6 +143,6 @@
init_state, %% option 'state', initial callback state
id, %% 32-bit unsigned application identifier = Dict:id()
mutable = false, %% boolean(), do traffic callbacks modify state?
- options = [{answer_errors, report}]}). %% | callback | discard
-
+ options = [{answer_errors, report}, %% | callback | discard
+ {request_errors, answer_3xxx}]}). %% | callback | answer
-endif. %% -ifdef(diameter_hrl).
diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile
index 0e448c8912..df10c33268 100644
--- a/lib/diameter/src/Makefile
+++ b/lib/diameter/src/Makefile
@@ -109,7 +109,8 @@ ERL_COMPILE_FLAGS += \
+warn_unused_vars \
-pa $(ABS_EBIN) \
-I $(INCDIR) \
- -I gen
+ -I gen \
+ $(STRICT_FLAGS)
# -pa is to be able to include_lib from the include directory: the
# path must contain the application name.
@@ -124,6 +125,13 @@ gen/diameter_gen_%.erl gen/diameter_gen_%.hrl: dict/%.dia
opt: $(TARGET_FILES)
+# Compile with -Werror during development. Don't do this in the 'opt'
+# target so that new warnings don't break the build. It's also
+# convenient to have both targets when weeding out warnings isn't the
+# priority. (Or when they're intentional, when debugging.)
+strict:
+ $(MAKE) opt STRICT_FLAGS=-Werror
+
# Build unofficial patches with some degree of traceability. Refuse to
# build if there are diffs from HEAD since that defeats the purpose.
patch:
@@ -251,13 +259,13 @@ release_docs_spec:
# Dependencies
# ----------------------------------------------------
-gen/diameter_gen_base_accounting.erl gen/diameter_gen_relay.erl \
-gen/diameter_gen_base_accounting.hrl gen/diameter_gen_relay.hrl: \
+gen/diameter_gen_base_accounting.erl gen/diameter_gen_base_accounting.hrl: \
$(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR)
gen/diameter_gen_acct_rfc6733.erl gen/diameter_gen_acct_rfc6733.hrl: \
$(EBIN)/diameter_gen_base_rfc6733.$(EMULATOR)
+gen/diameter_gen_relay.erl gen/diameter_gen_relay.hrl \
gen/diameter_gen_base_rfc3588.erl gen/diameter_gen_base_rfc3588.hrl \
gen/diameter_gen_base_rfc6733.erl gen/diameter_gen_base_rfc6733.hrl: \
$(COMPILER_MODULES:%=$(EBIN)/%.$(EMULATOR))
@@ -281,7 +289,7 @@ depend.mk: depend.sed $(MODULES:%=%.erl) Makefile
.PHONY: debug opt release_docs_spec release_spec
.PHONY: $(TARGET_DIRS:%/=%) $(TARGET_DIRS:%/=release_src_%)
.PHONY: $(EXAMPLE_DIRS:%/=release_examples_%)
-.PHONY: plt dialyze patch
+.PHONY: plt dialyze patch strict
# Keep intermediate files.
.SECONDARY: $(DICT_ERLS) $(DICT_HRLS) gen/$(DICT_YRL:%=%.erl)
diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src
index f6d772b534..2ce89579ff 100644
--- a/lib/diameter/src/base/diameter.appup.src
+++ b/lib/diameter/src/base/diameter.appup.src
@@ -20,14 +20,15 @@
{"%VSN%",
[
- {"0.9", [{restart_application, diameter}]},
- {"0.10", [{restart_application, diameter}]},
- {"1.0", [{restart_application, diameter}]},
- {"1.1", [{restart_application, diameter}]},
- {"1.2", [{restart_application, diameter}]},
+ {"0.9", [{restart_application, diameter}]}, %% R14B03
+ {"0.10", [{restart_application, diameter}]}, %% R14B04
+ {"1.0", [{restart_application, diameter}]}, %% R15B
+ {"1.1", [{restart_application, diameter}]}, %% R15B01
+ {"1.2", [{restart_application, diameter}]}, %% R15B02
{"1.2.1", [{restart_application, diameter}]},
- {"1.3", [{restart_application, diameter}]},
- {"1.3.1", [{restart_application, diameter}]}
+ {"1.3", [{restart_application, diameter}]}, %% R15B03
+ {"1.3.1", [{restart_application, diameter}]},
+ {"1.4", [{restart_application, diameter}]} %% R16A
],
[
{"0.9", [{restart_application, diameter}]},
@@ -37,6 +38,7 @@
{"1.2", [{restart_application, diameter}]},
{"1.2.1", [{restart_application, diameter}]},
{"1.3", [{restart_application, diameter}]},
- {"1.3.1", [{restart_application, diameter}]}
+ {"1.3.1", [{restart_application, diameter}]},
+ {"1.4", [{restart_application, diameter}]}
]
}.
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index f563d244f6..c67fba5f89 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -306,7 +306,8 @@ call(SvcName, App, Message) ->
| {module, app_module()}
| {state, any()}
| {call_mutates_state, boolean()}
- | {answer_errors, callback|report|discard}.
+ | {answer_errors, callback|report|discard}
+ | {request_errors, answer_3xxx|answer|callback}.
-type app_alias()
:: any().
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 1486071573..9f73815756 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -670,15 +670,17 @@ app_acc({application, Opts}, Acc) ->
[Dict, Mod] = get_opt([dictionary, module], Opts),
Alias = get_opt(alias, Opts, Dict),
ModS = get_opt(state, Opts, Alias),
- M = get_opt(call_mutates_state, Opts, false),
- A = get_opt(answer_errors, Opts, report),
+ M = get_opt(call_mutates_state, Opts, false, [true]),
+ A = get_opt(answer_errors, Opts, report, [callback, discard]),
+ P = get_opt(request_errors, Opts, answer_3xxx, [answer, callback]),
[#diameter_app{alias = Alias,
dictionary = Dict,
id = cb(Dict, id),
module = init_mod(Mod),
init_state = ModS,
- mutable = init_mutable(M),
- options = [{answer_errors, init_answers(A)}]}
+ mutable = M,
+ options = [{answer_errors, A},
+ {request_errors, P}]}
| Acc];
app_acc(_, Acc) ->
Acc.
@@ -707,20 +709,16 @@ init_cb(List) ->
V <- [proplists:get_value(F, List, D)]],
#diameter_callback{} = list_to_tuple([diameter_callback | Values]).
-init_mutable(M)
- when M == true;
- M == false ->
- M;
-init_mutable(M) ->
- ?THROW({call_mutates_state, M}).
-
-init_answers(A)
- when callback == A;
- report == A;
- discard == A ->
- A;
-init_answers(A) ->
- ?THROW({answer_errors, A}).
+%% Retreive and validate.
+get_opt(Key, List, Def, Other) ->
+ init_opt(Key, get_opt(Key, List, Def), [Def|Other]).
+
+init_opt(_, V, [V|_]) ->
+ V;
+init_opt(Name, V, [_|Vals]) ->
+ init_opt(Name, V, Vals);
+init_opt(Name, V, []) ->
+ ?THROW({Name, V}).
%% Get a single value at the specified key.
get_opt(Keys, List)
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 0de3825943..f527f7c754 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -20,7 +20,7 @@
%%
%% Implements the handling of incoming and outgoing Diameter messages
%% except CER/CEA, DWR/DWA and DPR/DPA. That is, the messages that a
-%% diameter client sends of receives.
+%% diameter client sends and receives.
%%
-module(diameter_traffic).
@@ -38,7 +38,7 @@
failover/1,
pending/1]).
-%% Other callbacks.
+%% towards ?MODULE
-export([send/1]). %% send from remote node
-include_lib("diameter/include/diameter.hrl").
@@ -187,37 +187,42 @@ recv_request(TPid,
Dict0,
#recvdata{peerT = PeerT, apps = Apps}
= RecvData) ->
- recv_request(diameter_service:find_incoming_app(PeerT, TPid, Id, Apps),
- TPid,
- Pkt,
- Dict0,
- RecvData).
-
-%% recv_request/5
-
-recv_request({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
- TPid,
- Pkt,
- Dict0,
- RecvData) ->
- recv_R(App,
+ send_A(recv_R(diameter_service:find_incoming_app(PeerT, TPid, Id, Apps),
+ TPid,
+ Pkt,
+ Dict0,
+ RecvData),
TPid,
- Caps,
Dict0,
- RecvData,
- diameter_codec:decode(Id, Dict, Pkt));
+ RecvData).
+
+%% recv_R/5
+
+recv_R({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
+ TPid,
+ Pkt0,
+ Dict0,
+ RecvData) ->
+ Pkt = errors(Id, diameter_codec:decode(Id, Dict, Pkt0)),
+ {Caps, Pkt, App, recv_R(App, TPid, Dict0, Caps, RecvData, Pkt)};
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
%% DIAMETER_APPLICATION_UNSUPPORTED 3007
%% A request was sent for an application that is not supported.
-recv_request(#diameter_caps{} = Caps, TPid, Pkt, Dict0, _) ->
- As = collect_avps(Pkt),
- protocol_error(3007, TPid, Caps, Dict0, Pkt#diameter_packet{avps = As});
+recv_R(#diameter_caps{}
+ = Caps,
+ _TPid,
+ #diameter_packet{errors = Es}
+ = Pkt,
+ _Dict0,
+ _RecvData) ->
+ {Caps, Pkt#diameter_packet{avps = collect_avps(Pkt),
+ errors = [3007 | Es]}};
-recv_request(false, _, _, _, _) -> %% transport has gone down
- ok.
+recv_R(false = No, _, _, _, _) -> %% transport has gone down
+ No.
collect_avps(Pkt) ->
case diameter_codec:collect_avps(Pkt) of
@@ -229,98 +234,51 @@ collect_avps(Pkt) ->
%% recv_R/6
-%% Wrong number of bits somewhere in the message: reply.
-%%
-%% DIAMETER_INVALID_AVP_BITS 3009
-%% A request was received that included an AVP whose flag bits are
-%% set to an unrecognized value, or that is inconsistent with the
-%% AVP's definition.
-%%
-recv_R(_App,
- TPid,
- Caps,
- Dict0,
- _RecvData,
- #diameter_packet{errors = [Bs | _]} = Pkt)
- when is_bitstring(Bs) ->
- protocol_error(3009, TPid, Caps, Dict0, Pkt);
-
-%% Either we support this application but don't recognize the command
-%% or we're a relay and the command isn't proxiable.
-%%
-%% DIAMETER_COMMAND_UNSUPPORTED 3001
-%% The Request contained a Command-Code that the receiver did not
-%% recognize or support. This MUST be used when a Diameter node
-%% receives an experimental command that it does not understand.
-%%
-recv_R(#diameter_app{id = Id},
- TPid,
- Caps,
+%% Answer errors ourselves ...
+recv_R(#diameter_app{options = [_, {request_errors, E} | _]},
+ _TPid,
Dict0,
+ _Caps,
_RecvData,
- #diameter_packet{header = #diameter_header{is_proxiable = P},
- msg = M}
- = Pkt)
- when ?APP_ID_RELAY /= Id, undefined == M;
- ?APP_ID_RELAY == Id, not P ->
- protocol_error(3001, TPid, Caps, Dict0, Pkt);
-
-%% Error bit was set on a request.
-%%
-%% DIAMETER_INVALID_HDR_BITS 3008
-%% A request was received whose bits in the Diameter header were
-%% either set to an invalid combination, or to a value that is
-%% inconsistent with the command code's definition.
-%%
-recv_R(_App,
+ #diameter_packet{errors = [RC|_]}) %% a detected 3xxx is hd
+ when E == answer, (Dict0 /= ?BASE orelse 3 == RC div 1000);
+ E == answer_3xxx, 3 == RC div 1000 ->
+ {{answer_message, rc(RC)}, [], []};
+
+%% ... or make a handle_request callback. Note that
+%% Pkt#diameter_packet.msg = undefined in the 3001 case.
+recv_R(App,
TPid,
+ _Dict0,
Caps,
- Dict0,
- _RecvData,
- #diameter_packet{header = #diameter_header{is_error = true}}
- = Pkt) ->
- protocol_error(3008, TPid, Caps, Dict0, Pkt);
-
-%% A message in a locally supported application or a proxiable message
-%% in the relay application. Don't distinguish between the two since
-%% each application has its own callback config. That is, the user can
-%% easily distinguish between the two cases.
-recv_R(App, TPid, Caps, Dict0, RecvData, Pkt) ->
- request_cb(App, TPid, Caps, Dict0, RecvData, examine(Pkt)).
-
-%% Note that there may still be errors but these aren't protocol
-%% (3xxx) errors that lead to an answer-message.
-
-request_cb(App,
- TPid,
- Caps,
- Dict0,
- #recvdata{service_name = SvcName}
- = RecvData,
- Pkt) ->
+ #recvdata{service_name = SvcName},
+ Pkt) ->
request_cb(cb(App, handle_request, [Pkt, SvcName, {TPid, Caps}]),
App,
- TPid,
- Caps,
- Dict0,
- RecvData,
[],
- Pkt).
+ []).
+
+rc({N,_}) ->
+ N;
+rc(N) ->
+ N.
-%% examine/1
+%% errors/1
%%
-%% Look for errors in a decoded message. It's odd/unfortunate that
-%% 501[15] aren't protocol errors.
+%% Look for additional errors in a decoded message, prepending the
+%% errors field with the first detected error. It's odd/unfortunate
+%% that 501[15] aren't protocol errors. With RFC 3588 this means that
+%% a handle_request callback has to formulate the answer. With RFC
+%% 6733 it's acceptable for 5xxx to be sent in an answer-message.
%% DIAMETER_INVALID_MESSAGE_LENGTH 5015
-%%
%% This error is returned when a request is received with an invalid
%% message length.
-examine(#diameter_packet{header = #diameter_header{length = Len},
- bin = Bin,
- errors = Es}
- = Pkt)
+errors(_, #diameter_packet{header = #diameter_header{length = Len},
+ bin = Bin,
+ errors = Es}
+ = Pkt)
when Len < 20;
0 /= Len rem 4;
8*Len /= bit_size(Bin) ->
@@ -330,57 +288,75 @@ examine(#diameter_packet{header = #diameter_header{length = Len},
%% This error is returned when a request was received, whose version
%% number is unsupported.
-examine(#diameter_packet{header = #diameter_header{version = V},
- errors = Es}
- = Pkt)
+errors(_, #diameter_packet{header = #diameter_header{version = V},
+ errors = Es}
+ = Pkt)
when V /= ?DIAMETER_VERSION ->
Pkt#diameter_packet{errors = [5011 | Es]};
-examine(Pkt) ->
+%% DIAMETER_INVALID_AVP_BITS 3009
+%% A request was received that included an AVP whose flag bits are
+%% set to an unrecognized value, or that is inconsistent with the
+%% AVP's definition.
+
+errors(_, #diameter_packet{errors = [Bs | Es]} = Pkt)
+ when is_bitstring(Bs) ->
+ Pkt#diameter_packet{errors = [3009 | Es]};
+
+%% DIAMETER_COMMAND_UNSUPPORTED 3001
+%% The Request contained a Command-Code that the receiver did not
+%% recognize or support. This MUST be used when a Diameter node
+%% receives an experimental command that it does not understand.
+
+errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P},
+ msg = M,
+ errors = Es}
+ = Pkt)
+ when ?APP_ID_RELAY /= Id, undefined == M; %% don't know the command
+ ?APP_ID_RELAY == Id, not P -> %% command isn't proxiable
+ Pkt#diameter_packet{errors = [3001 | Es]};
+
+%% DIAMETER_INVALID_HDR_BITS 3008
+%% A request was received whose bits in the Diameter header were
+%% either set to an invalid combination, or to a value that is
+%% inconsistent with the command code's definition.
+
+errors(_, #diameter_packet{header = #diameter_header{is_request = true,
+ is_error = true},
+ errors = Es}
+ = Pkt) ->
+ Pkt#diameter_packet{errors = [3008 | Es]};
+
+%% Green.
+errors(_, Pkt) ->
Pkt.
-%% request_cb/8
+%% request_cb/4
%% A reply may be an answer-message, constructed either here or by
%% the handle_request callback. The header from the incoming request
%% is passed into the encode so that it can retrieve the relevant
%% command code in this case. It will also then ignore Dict and use
%% the base encoder.
-request_cb({reply, Ans},
- #diameter_app{dictionary = Dict},
- TPid,
- _Caps,
- Dict0,
- _RecvData,
- Fs,
- Pkt) ->
- reply(Ans, dict(Dict, Dict0, Ans), TPid, Fs, Pkt);
+request_cb({reply, _Ans} = T, _App, EvalPktFs, EvalFs) ->
+ {T, EvalPktFs, EvalFs};
%% An 3xxx result code, for which the E-bit is set in the header.
-request_cb({protocol_error, RC},
- _App,
- TPid,
- Caps,
- Dict0,
- _RecvData,
- Fs,
- Pkt)
- when 3000 =< RC, RC < 4000 ->
- protocol_error(RC, TPid, Caps, Dict0, Fs, Pkt);
+request_cb({protocol_error, RC}, _App, EvalPktFs, EvalFs)
+ when 3 == RC div 1000 ->
+ {{answer_message, RC}, EvalPktFs, EvalFs};
+
+request_cb({answer_message, RC} = T, _App, EvalPktFs, EvalFs)
+ when 3 == RC div 1000;
+ 5 == RC div 1000 ->
+ {T, EvalPktFs, EvalFs};
%% RFC 3588 says we must reply 3001 to anything unrecognized or
%% unsupported. 'noreply' is undocumented (and inappropriately named)
%% backwards compatibility for this, protocol_error the documented
%% alternative.
-request_cb(noreply,
- _App,
- TPid,
- Caps,
- Dict0,
- _RecvData,
- Fs,
- Pkt) ->
- protocol_error(3001, TPid, Caps, Dict0, Fs, Pkt);
+request_cb(noreply, _App, EvalPktFs, EvalFs) ->
+ {{answer_message, 3001}, EvalPktFs, EvalFs};
%% Relay a request to another peer. This is equivalent to doing an
%% explicit call/4 with the message in question except that (1) a loop
@@ -397,29 +373,77 @@ request_cb(noreply,
%% want to distinguish between the cases in the callback return value
%% then 'resend' is a neutral alternative.
%%
-request_cb({A, Opts},
- #diameter_app{id = Id}
- = App,
- TPid,
- Caps,
- Dict0,
- RecvData,
- Fs,
- Pkt)
+request_cb({A, Opts}, #diameter_app{id = Id}, EvalPktFs, EvalFs)
when A == relay, Id == ?APP_ID_RELAY;
A == proxy, Id /= ?APP_ID_RELAY;
A == resend ->
- resend(Opts, App, TPid, Caps, Dict0, RecvData, Fs, Pkt);
+ {{call, Opts}, EvalPktFs, EvalFs};
-request_cb(discard, _, _, _, _, _, _, _) ->
- ok;
+request_cb(discard = No, _, _, _) ->
+ No;
+
+request_cb({eval_packet, RC, F}, App, Fs, EvalFs) ->
+ request_cb(RC, App, [F|Fs], EvalFs);
+
+request_cb({eval, RC, F}, App, EvalPktFs, Fs) ->
+ request_cb(RC, App, EvalPktFs, [F|Fs]);
+
+request_cb(T, App, _, _) ->
+ ?ERROR({invalid_return, T, handle_request, App}).
+
+%% send_A/4
-request_cb({eval_packet, RC, F}, App, TPid, Caps, Dict0, RecvData, Fs, Pkt) ->
- request_cb(RC, App, TPid, Caps, Dict0, RecvData, [F|Fs], Pkt);
+send_A({Caps, Pkt}, TPid, Dict0, _RecvData) -> %% unsupported application
+ #diameter_packet{errors = [RC|_]} = Pkt,
+ send_A(answer_message(RC, Caps, Dict0, Pkt),
+ TPid,
+ Dict0,
+ Pkt,
+ [],
+ []);
+
+send_A({Caps, Pkt, App, {T, EvalPktFs, EvalFs}}, TPid, Dict0, RecvData) ->
+ send_A(answer(T, Caps, Pkt, App, Dict0, RecvData),
+ TPid,
+ Dict0,
+ Pkt,
+ EvalPktFs,
+ EvalFs);
+
+send_A(_, _, _, _) ->
+ ok.
+
+%% send_A/6
+
+send_A(T, TPid, Dict0, ReqPkt, EvalPktFs, EvalFs) ->
+ reply(T, TPid, Dict0, EvalPktFs, ReqPkt),
+ lists:foreach(fun diameter_lib:eval/1, EvalFs).
+
+%% answer/6
+
+answer({reply, Ans}, _Caps, _Pkt, App, Dict0, _RecvData) ->
+ {dict(App#diameter_app.dictionary, Dict0, Ans), Ans};
+
+answer({call, Opts}, Caps, Pkt, App, Dict0, RecvData) ->
+ #diameter_caps{origin_host = {OH,_}}
+ = Caps,
+ #diameter_packet{avps = Avps}
+ = Pkt,
+ {Code, _Flags, Vid} = Dict0:avp_header('Route-Record'),
+ resend(is_loop(Code, Vid, OH, Dict0, Avps),
+ Opts,
+ Caps,
+ Pkt,
+ App,
+ Dict0,
+ RecvData);
-request_cb({eval, RC, F}, App, TPid, Caps, Dict0, RecvData, Fs, Pkt) ->
- request_cb(RC, App, TPid, Caps, Dict0, RecvData, Fs, Pkt),
- diameter_lib:eval(F).
+%% RFC 3588 only allows 3xxx errors in an answer-message. RFC 6733
+%% added the possibility of setting 5xxx.
+answer({answer_message, RC} = T, Caps, Pkt, App, Dict0, _RecvData) ->
+ Dict0 /= ?BASE orelse 3 == RC div 1000
+ orelse ?ERROR({invalid_return, T, handle_request, App}),
+ answer_message(RC, Caps, Dict0, Pkt).
%% dict/3
@@ -436,65 +460,31 @@ dict(Dict, Dict0, [Msg]) ->
dict(Dict, Dict0, #diameter_packet{msg = Msg}) ->
dict(Dict, Dict0, Msg);
-dict(_Dict, Dict0, ['answer-message' | _]) ->
- Dict0;
+dict(Dict, Dict0, Msg) ->
+ choose(is_answer_message(Msg, Dict0), Dict0, Dict).
-dict(Dict, Dict0, Rec) ->
+is_answer_message([Name | _], _) ->
+ Name == 'answer-message';
+
+is_answer_message(Rec, Dict) ->
try
- 'answer-message' = Dict0:rec2msg(element(1,Rec)),
- Dict0
+ 'answer-message' == Dict:rec2msg(element(1,Rec))
catch
- error:_ -> Dict
+ error:_ -> false
end.
-%% protocol_error/6
-
-protocol_error(RC, TPid, Caps, Dict0, Fs, Pkt) ->
- #diameter_caps{origin_host = {OH,_},
- origin_realm = {OR,_}}
- = Caps,
- #diameter_packet{avps = Avps, errors = Es}
- = Pkt,
+%% answer_message/4
+answer_message(RC,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}},
+ Dict0,
+ #diameter_packet{avps = Avps}
+ = Pkt) ->
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Dict0, Avps),
- Dict0,
- TPid,
- Fs,
- Pkt#diameter_packet{errors = [RC | Es]}).
-%% Note that reply/5 may set the result code once more. It's set in
-%% answer_message/3 in case reply/5 doesn't.
-
-%% protocol_error/5
-
-protocol_error(RC, TPid, Caps, Dict0, Pkt) ->
- protocol_error(RC, TPid, Caps, Dict0, [], Pkt).
+ {Dict0, answer_message(OH, OR, RC, Dict0, Avps)}.
%% resend/7
-%%
-%% Resend a message as a relay or proxy agent.
-
-resend(Opts,
- #diameter_app{}
- = App,
- TPid,
- #diameter_caps{origin_host = {OH,_}}
- = Caps,
- Dict0,
- RecvData,
- Fs,
- #diameter_packet{avps = Avps}
- = Pkt) ->
- {Code, _Flags, Vid} = Dict0:avp_header('Route-Record'),
- resend(is_loop(Code, Vid, OH, Dict0, Avps),
- Opts,
- App,
- TPid,
- Caps,
- Dict0,
- RecvData,
- Fs,
- Pkt).
%% DIAMETER_LOOP_DETECTED 3005
%% An agent detected a loop while trying to get the message to the
@@ -502,8 +492,8 @@ resend(Opts,
%% if one is available, but the peer reporting the error has
%% identified a configuration problem.
-resend(true, _Opts, _App, TPid, Caps, Dict0, _RecvData, Fs, Pkt) ->
- protocol_error(3005, TPid, Caps, Dict0, Fs, Pkt);
+resend(true, _Opts, Caps, Pkt, _App, Dict0, _RecvData) ->
+ answer_message(3005, Caps, Dict0, Pkt);
%% 6.1.8. Relaying and Proxying Requests
%%
@@ -513,22 +503,20 @@ resend(true, _Opts, _App, TPid, Caps, Dict0, _RecvData, Fs, Pkt) ->
resend(false,
Opts,
- App,
- TPid,
#diameter_caps{origin_host = {_,OH}}
= Caps,
- Dict0,
- #recvdata{service_name = SvcName,
- sequence = Mask},
- Fs,
#diameter_packet{header = Hdr0,
avps = Avps}
- = Pkt) ->
+ = Pkt,
+ App,
+ Dict0,
+ #recvdata{service_name = SvcName,
+ sequence = Mask}) ->
Route = #diameter_avp{data = {Dict0, 'Route-Record', OH}},
Seq = diameter_session:sequence(Mask),
Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq},
Msg = [Hdr, Route | Avps],
- resend(send_request(SvcName, App, Msg, Opts), TPid, Caps, Dict0, Fs, Pkt).
+ resend(send_request(SvcName, App, Msg, Opts), Caps, Dict0, Pkt).
%% The incoming request is relayed with the addition of a
%% Route-Record. Note the requirement on the return from call/4 below,
%% which places a requirement on the value returned by the
@@ -545,28 +533,24 @@ resend(false,
%% RFC 6.3 says that a relay agent does not modify Origin-Host but
%% says nothing about a proxy. Assume it should behave the same way.
-%% resend/6
+%% resend/4
%%
%% Relay a reply to a relayed request.
%% Answer from the peer: reset the hop by hop identifier and send.
resend(#diameter_packet{bin = B}
= Pkt,
- TPid,
_Caps,
_Dict0,
- Fs,
#diameter_packet{header = #diameter_header{hop_by_hop_id = Id},
transport_data = TD}) ->
- P = Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
- transport_data = TD},
- eval_packet(P, Fs),
- send(TPid, P);
+ Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B),
+ transport_data = TD};
%% TODO: counters
%% Or not: DIAMETER_UNABLE_TO_DELIVER.
-resend(_, TPid, Caps, Dict0, Fs, Pkt) ->
- protocol_error(3002, TPid, Caps, Dict0, Fs, Pkt).
+resend(_, Caps, Dict0, Pkt) ->
+ answer_message(3002, Caps, Dict0, Pkt).
%% is_loop/5
%%
@@ -590,37 +574,110 @@ is_loop(Code, Vid, OH, Dict0, Avps) ->
is_loop(Code, Vid, Dict0:avp(encode, OH, 'Route-Record'), Dict0, Avps).
%% reply/5
+
+%% Local answer ...
+reply({Dict, Ans}, TPid, Dict0, Fs, ReqPkt) ->
+ reply(Ans, Dict, TPid, Dict0, Fs, ReqPkt);
+
+%% ... or relayed.
+reply(#diameter_packet{} = Pkt, TPid, _Dict0, Fs, _ReqPkt) ->
+ eval_packet(Pkt, Fs),
+ send(TPid, Pkt).
+
+%% reply/6
%%
%% Send a locally originating reply.
%% Skip the setting of Result-Code and Failed-AVP's below. This is
-%% currently undocumented.
-reply([Msg], Dict, TPid, Fs, Pkt)
+%% undocumented and shouldn't be relied on.
+reply([Msg], Dict, TPid, Dict0, Fs, ReqPkt)
when is_list(Msg);
is_tuple(Msg) ->
- reply(Msg, Dict, TPid, Fs, Pkt#diameter_packet{errors = []});
+ reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt#diameter_packet{errors = []});
%% No errors or a diameter_header/avp list.
-reply(Msg, Dict, TPid, Fs, #diameter_packet{errors = Es} = ReqPkt)
- when [] == Es;
- is_record(hd(Msg), diameter_header) ->
- Pkt = encode(Dict, make_answer_packet(Msg, ReqPkt), Fs),
- incr(send, Pkt, Dict, TPid), %% count result codes in sent answers
- send(TPid, Pkt);
-
-%% Or not: set Result-Code and Failed-AVP AVP's.
-reply(Msg, Dict, TPid, Fs, #diameter_packet{errors = [H|_] = Es} = Pkt) ->
- reply(rc(Msg, rc(H), [A || {_,A} <- Es], Dict),
+reply(Msg, Dict, TPid, Dict0, Fs, ReqPkt) ->
+ Pkt = encode(Dict, reset(make_answer_packet(Msg, ReqPkt), Dict), Fs),
+ incr(send, Pkt, Dict, TPid, Dict0), %% count outgoing result codes
+ send(TPid, Pkt).
+
+%% reset/2
+
+%% Header/avps list: send as is.
+reset(#diameter_packet{msg = [#diameter_header{} | _]} = Pkt, _) ->
+ Pkt;
+
+%% No errors to set or errors explicitly ignored.
+reset(#diameter_packet{errors = Es} = Pkt, _)
+ when Es == [];
+ Es == false ->
+ Pkt;
+
+%% Otherwise possibly set Result-Code and/or Failed-AVP.
+reset(#diameter_packet{msg = Msg, errors = Es} = Pkt, Dict) ->
+ Pkt#diameter_packet{msg = reset(Msg, Dict, Es)}.
+
+%% reset/3
+
+reset(Msg, Dict, Es)
+ when is_list(Es) ->
+ {E3, E5, Fs} = partition(Es),
+ FailedAVP = failed_avp(Msg, lists:reverse(Fs), Dict),
+ reset(set(Msg, FailedAVP, Dict),
Dict,
- TPid,
- Fs,
- Pkt#diameter_packet{errors = []}).
+ choose(is_answer_message(Msg, Dict), E3, E5));
+
+reset(Msg, Dict, N)
+ when is_integer(N) ->
+ ResultCode = rc(Msg, {'Result-Code', N}, Dict),
+ set(Msg, ResultCode, Dict);
+
+reset(Msg, _, _) ->
+ Msg.
+
+partition(Es) ->
+ lists:foldl(fun pacc/2, {false, false, []}, Es).
+
+%% Note that the errors list can contain not only integer() and
+%% {integer(), #diameter_avp{}} but also #diameter_avp{}. The latter
+%% isn't something that's returned by decode but can be set in a reply
+%% for encode.
+
+pacc({RC, #diameter_avp{} = A}, {E3, E5, Acc})
+ when is_integer(RC) ->
+ pacc(RC, {E3, E5, [A|Acc]});
+
+pacc(#diameter_avp{} = A, {E3, E5, Acc}) ->
+ {E3, E5, [A|Acc]};
+
+pacc(RC, {false, E5, Acc})
+ when 3 == RC div 1000 ->
+ {RC, E5, Acc};
+
+pacc(RC, {E3, false, Acc})
+ when 5 == RC div 1000 ->
+ {E3, RC, Acc};
+
+pacc(_, Acc) ->
+ Acc.
eval_packet(Pkt, Fs) ->
lists:foreach(fun(F) -> diameter_lib:eval([F,Pkt]) end, Fs).
%% make_answer_packet/2
+%% Use decode errors to set Result-Code and/or Failed-AVP unless the
+%% the errors field has been explicitly set. Unfortunately, the
+%% default value is the empty list rather than 'undefined' so use the
+%% atom 'false' for "set nothing". (This is historical and changing
+%% the default value would require modules including diameter.hrl to
+%% be recompiled.)
+make_answer_packet(#diameter_packet{errors = []}
+ = Pkt,
+ #diameter_packet{errors = [_|_] = Es}
+ = ReqPkt) ->
+ make_answer_packet(Pkt#diameter_packet{errors = Es}, ReqPkt);
+
%% A reply message clears the R and T flags and retains the P flag.
%% The E flag will be set at encode. 6.2 of 3588 requires the same P
%% flag on an answer as on the request. A #diameter_packet{} returned
@@ -628,6 +685,7 @@ eval_packet(Pkt, Fs) ->
%% own header values.
make_answer_packet(#diameter_packet{header = Hdr,
msg = Msg,
+ errors = Es,
transport_data = TD},
#diameter_packet{header = ReqHdr}) ->
Hdr0 = ReqHdr#diameter_header{version = ?DIAMETER_VERSION,
@@ -636,6 +694,7 @@ make_answer_packet(#diameter_packet{header = Hdr,
is_retransmitted = false},
#diameter_packet{header = fold_record(Hdr0, Hdr),
msg = Msg,
+ errors = Es,
transport_data = TD};
%% Binaries and header/avp lists are sent as-is.
@@ -652,25 +711,6 @@ make_answer_packet([#diameter_header{} | _] = Msg,
make_answer_packet(Msg, #diameter_packet{transport_data = TD} = Pkt) ->
make_answer_packet(#diameter_packet{msg = Msg, transport_data = TD}, Pkt).
-%% rc/1
-
-rc({RC, _}) ->
- RC;
-rc(RC) ->
- RC.
-
-%% rc/4
-
-rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, DictT) ->
- Pkt#diameter_packet{msg = rc(Rec, RC, Failed, DictT)};
-
-rc(Rec, RC, Failed, DictT)
- when is_integer(RC) ->
- set(Rec,
- lists:append([rc(Rec, {'Result-Code', RC}, DictT),
- failed_avp(Rec, Failed, DictT)]),
- DictT).
-
%% Reply as name and tuple list ...
set([_|_] = Ans, Avps, _) ->
Ans ++ Avps; %% Values nearer tail take precedence.
@@ -796,9 +836,9 @@ fa(Rec, FailedAvp, Dict) ->
%% Error-Message AVP is not intended to be useful in real-time, and
%% SHOULD NOT be expected to be parsed by network entities.
-%% answer_message/3
+%% answer_message/5
-answer_message({OH, OR, RC}, Dict0, Avps) ->
+answer_message(OH, OR, RC, Dict0, Avps) ->
{Code, _, Vid} = Dict0:avp_header('Session-Id'),
['answer-message', {'Origin-Host', OH},
{'Origin-Realm', OR},
@@ -892,32 +932,48 @@ find(Pred, [H|T]) ->
%% incr/4
%%
-%% Increment a stats counter for an incoming or outgoing message.
+%% Increment a stats counter for result codes in incoming and outgoing
+%% answers.
%% Outgoing message as binary: don't count. (Sending binaries is only
%% partially supported.)
-incr(_, #diameter_packet{msg = undefined}, _, _) ->
+incr(_, #diameter_packet{msg = undefined}, _, _, _) ->
ok;
-incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid) ->
+%% Incoming with decode errors.
+incr(recv = D, #diameter_packet{header = H, errors = [_|_]}, _, TPid, _) ->
incr(TPid, {diameter_codec:msg_id(H), D, error});
-incr(Dir, Pkt, Dict, TPid) ->
+%% Incoming without errors or outgoing. Outgoing with encode errors
+%% never gets here since encode fails.
+incr(Dir, Pkt, Dict, TPid, Dict0) ->
#diameter_packet{header = #diameter_header{is_error = E}
= Hdr,
msg = Rec}
= Pkt,
RC = int(get_avp_value(Dict, 'Result-Code', Rec)),
- PE = is_protocol_error(RC),
- %% Check that the E bit is set only for 3xxx result codes.
- (not (E orelse PE))
- orelse (E andalso PE)
+ %% Exit on an improper Result-Code.
+ is_result(RC, E, Dict0)
orelse x({invalid_error_bit, RC}, answer, [Dir, Pkt]),
irc(TPid, Hdr, Dir, rc_counter(Dict, Rec, RC)).
+%% No E-bit: can't be 3xxx.
+is_result(RC, false, _Dict0) ->
+ RC < 3000 orelse 4000 =< RC;
+
+%% E-bit in RFC 3588: only 3xxx.
+is_result(RC, true, ?BASE) ->
+ 3000 =< RC andalso RC < 4000;
+
+%% E-bit in RFC 6733: 3xxx or 5xxx.
+is_result(RC, true, _) ->
+ 3000 =< RC andalso RC < 4000
+ orelse
+ 5000 =< RC andalso RC < 6000.
+
irc(_, _, _, undefined) ->
false;
@@ -929,7 +985,7 @@ irc(TPid, Hdr, Dir, Ctr) ->
incr(TPid, Counter) ->
diameter_stats:incr(Counter, TPid, 1).
-%% error_counter/2
+%% rc_counter/2
%% RFC 3588, 7.6:
%%
@@ -969,9 +1025,6 @@ int(N)
int(_) ->
undefined.
-is_protocol_error(RC) ->
- 3000 =< RC andalso RC < 4000.
-
-spec x(any(), atom(), list()) -> no_return().
%% Warn and exit request process on errors in an incoming answer.
@@ -1121,7 +1174,7 @@ send_R({eval_packet, RC, F}, Pkt, T, Opts, Caller, SvcName, Fs) ->
send_R(RC, Pkt, T, Opts, Caller, SvcName, [F|Fs]);
send_R(E, _, {_, _, App}, _, _, _, _) ->
- ?ERROR({invalid_return, prepare_request, App, E}).
+ ?ERROR({invalid_return, E, prepare_request, App}).
%% make_prepare_packet/2
%%
@@ -1268,28 +1321,33 @@ handle_answer(SvcName, App, {error, Req, Reason}) ->
handle_error(App, Req, Reason, SvcName);
handle_answer(SvcName,
- #diameter_app{dictionary = Dict}
+ #diameter_app{dictionary = Dict,
+ id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
Mod = dict(Dict, Dict0, Pkt),
- answer(examine(diameter_codec:decode(Mod, Pkt)),
- SvcName,
- Mod,
- App,
- Req).
+ handle_A(errors(Id, diameter_codec:decode(Mod, Pkt)),
+ SvcName,
+ Mod,
+ Dict0,
+ App,
+ Req).
%% We don't really need to do a full decode if we're a relay and will
%% just resend with a new hop by hop identifier, but might a proxy
%% want to examine the answer?
-answer(Pkt, SvcName, Dict, App, #request{transport = TPid} = Req) ->
+handle_A(Pkt, SvcName, Dict, Dict0, App, #request{transport = TPid} = Req) ->
try
- incr(recv, Pkt, Dict, TPid)
+ incr(recv, Pkt, Dict, TPid, Dict0) %% count incoming result codes
of
_ -> answer(Pkt, SvcName, App, Req)
catch
- exit: {invalid_error_bit, _} = E ->
- answer(Pkt#diameter_packet{errors = [E]}, SvcName, App, Req)
+ exit: {invalid_error_bit, RC} ->
+ #diameter_packet{errors = Es}
+ = Pkt,
+ E = {5004, #diameter_avp{name = 'Result-Code', value = RC}},
+ answer(Pkt#diameter_packet{errors = [E|Es]}, SvcName, App, Req)
end.
answer(Pkt,
@@ -1479,7 +1537,7 @@ retransmit({eval_packet, RC, F}, Transport, Req, SvcName, Timeout, Fs) ->
retransmit(RC, Transport, Req, SvcName, Timeout, [F|Fs]);
retransmit(T, {_, _, App}, _, _, _, _) ->
- ?ERROR({invalid_return, prepare_retransmit, App, T}).
+ ?ERROR({invalid_return, T, prepare_retransmit, App}).
resend_request(Pkt0,
{TPid, Caps, #diameter_app{dictionary = Dict}},
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index 1e31c40afe..80036879ea 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.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
@@ -128,8 +128,8 @@ gen(hrl, Spec, Mod, Path) ->
gen(erl, Spec, Mod, Path) ->
Forms = [{?attribute, module, Mod},
- {?attribute, compile, [{parse_transform, diameter_exprecs}]},
- {?attribute, compile, [{parse_transform, diameter_nowarn}]},
+ {?attribute, compile, {parse_transform, diameter_exprecs}},
+ {?attribute, compile, nowarn_unused_function},
{?attribute, export, [{name, 0},
{id, 0},
{vendor_id, 0},
diff --git a/lib/diameter/src/compiler/diameter_nowarn.erl b/lib/diameter/src/compiler/diameter_nowarn.erl
deleted file mode 100644
index 6c17af6563..0000000000
--- a/lib/diameter/src/compiler/diameter_nowarn.erl
+++ /dev/null
@@ -1,41 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. 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
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%% A parse transform to work around dialyzer currently not
-%% understanding nowarn_unused_function except on individual
-%% functions. The include of diameter_gen.hrl by generated dictionary
-%% modules contains code that may not be called depending on the
-%% dictionary. (The relay dictionary for example.)
-%%
-%% Even called functions may contain cases that aren't used for a
-%% particular dictionary. This also causes dialyzer to complain but
-%% there's no way to silence it in this case.
-%%
-
--module(diameter_nowarn).
-
--export([parse_transform/2]).
-
-parse_transform(Forms, _Options) ->
- [{attribute, ?LINE, compile, {nowarn_unused_function, {F,A}}}
- || {function, _, F, A, _} <- Forms]
- ++ Forms.
-%% Note that dialyzer also doesn't understand {nowarn_unused_function, FAs}
-%% with FAs a list of tuples.
diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk
index 2f3239e1aa..f8d3cf1d6f 100644
--- a/lib/diameter/src/modules.mk
+++ b/lib/diameter/src/modules.mk
@@ -70,7 +70,6 @@ CT_MODULES = \
base/diameter_info \
compiler/diameter_codegen \
compiler/diameter_exprecs \
- compiler/diameter_nowarn \
compiler/diameter_dict_scanner \
compiler/diameter_dict_util \
compiler/diameter_make
diff --git a/lib/diameter/test/.gitignore b/lib/diameter/test/.gitignore
index df38dfc5e3..4f19542bbe 100644
--- a/lib/diameter/test/.gitignore
+++ b/lib/diameter/test/.gitignore
@@ -1,3 +1,4 @@
/log
/depend.mk
+/coverspec
diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile
index aa4b7eaeb1..061f0bcbef 100644
--- a/lib/diameter/test/Makefile
+++ b/lib/diameter/test/Makefile
@@ -56,7 +56,8 @@ DATA_DIRS = $(sort $(dir $(DATA)))
ERL_COMPILE_FLAGS += +warn_export_vars \
+warn_unused_vars \
-I ../include \
- -I ../src/gen
+ -I ../src/gen \
+ $(STRICT_FLAGS)
# ----------------------------------------------------
# Targets
@@ -64,6 +65,9 @@ ERL_COMPILE_FLAGS += +warn_export_vars \
all debug opt: $(TARGET_FILES)
+strict:
+ $(MAKE) opt STRICT_FLAGS=-Werror
+
# Require success ...
run: $(SUITES)
@@ -73,7 +77,7 @@ any: opt
clean:
rm -f $(TARGET_FILES)
- rm -f depend.mk
+ rm -f depend.mk coverspec
realclean: clean
rm -rf log
@@ -114,7 +118,7 @@ help:
@echo " Echo some relevant variables."
@echo ========================================
-.PHONY: all any run clean debug docs help info opt realclean
+.PHONY: all any run clean debug docs help info opt realclean strict
# ----------------------------------------------------
# Special Targets
@@ -132,10 +136,21 @@ $(SUITES): log opt
| awk '{print} / FAILED /{rc=1} END{exit rc}' rc=0
# Shorter in sed but requires a GNU extension (ie. Q).
+cover: log opt coverspec
+ $(ERL) -noinput \
+ -pa $(realpath ../ebin) \
+ -sname diameter_cover \
+ -s diameter_ct cover \
+ -s init stop \
+ | awk '{print} / FAILED /{rc=1} END{exit rc}' rc=0
+
+coverspec: diameter.cover
+ sed -f [email protected] $< > $@
+
log:
mkdir $@
-.PHONY: $(SUITES)
+.PHONY: $(SUITES) cover
# ----------------------------------------------------
# Release Targets
diff --git a/lib/diameter/test/coverspec.sed b/lib/diameter/test/coverspec.sed
new file mode 100644
index 0000000000..5e81621593
--- /dev/null
+++ b/lib/diameter/test/coverspec.sed
@@ -0,0 +1,33 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 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
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+#
+# Morph diameter.cover into a legitimate cover spec. All that's being
+# retained is the list of excluded modules. This is used by Makefile
+# when running cover locally.
+#
+
+/^{incl_app,/{
+ i\
+{level, details}.\
+{incl_dirs, ["../ebin"]}.
+ d
+}
+
+/^{excl_mods,/s@ .*@@
diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl
new file mode 100644
index 0000000000..89c78d8b57
--- /dev/null
+++ b/lib/diameter/test/diameter_3xxx_SUITE.erl
@@ -0,0 +1,509 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Tests of application_opt() request_errors. There's some overlap
+%% between this suite and the traffic suite but latter exercises more
+%% config.
+%%
+
+-module(diameter_3xxx_SUITE).
+
+-export([suite/0,
+ all/0,
+ groups/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+%% testcases
+-export([start/1,
+ send_unknown_application/1,
+ send_unknown_command/1,
+ send_ok/1,
+ send_invalid_avp_bits/1,
+ send_missing_avp/1,
+ send_ignore_missing_avp/1,
+ send_double_error/1,
+ send_3xxx/1,
+ send_5xxx/1,
+ stop/1]).
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/5,
+ prepare_request/4,
+ handle_answer/5,
+ handle_error/5,
+ handle_request/3]).
+
+-include("diameter.hrl").
+-include("diameter_gen_base_rfc6733.hrl").
+%% Use the fact that STR/STA is identical in RFC's 3588 and 6733.
+
+%% ===========================================================================
+
+-define(util, diameter_util).
+-define(testcase(), proplists:get_value(testcase, get(?MODULE))).
+-define(group(Config), begin
+ put(?MODULE, Config),
+ ?util:name(proplists:get_value(group, Config))
+ end).
+
+-define(L, atom_to_list).
+-define(A, list_to_atom).
+
+-define(CLIENT, "CLIENT").
+-define(SERVER, "SERVER").
+-define(REALM, "erlang.org").
+-define(HOST(Host, Realm), Host ++ [$.|Realm]).
+
+-define(ERRORS, [answer, answer_3xxx, callback]).
+-define(RFCS, [rfc3588, rfc6733]).
+-define(DICT(RFC), ?A("diameter_gen_base_" ++ ?L(RFC))).
+-define(DICT, ?DICT(rfc6733)).
+
+-define(COMMON, ?DIAMETER_APP_ID_COMMON).
+
+%% Config for diameter:start_service/2.
+-define(SERVICE(Name, Errors, RFC),
+ [{'Origin-Host', Name ++ "." ++ ?REALM},
+ {'Origin-Realm', ?REALM},
+ {'Host-IP-Address', [{127,0,0,1}]},
+ {'Vendor-Id', 12345},
+ {'Product-Name', "OTP/diameter"},
+ {'Auth-Application-Id', [?COMMON]},
+ {application, [{dictionary, ?DICT(RFC)},
+ {module, ?MODULE},
+ {answer_errors, callback},
+ {request_errors, Errors}]}]).
+
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
+
+%% ===========================================================================
+
+suite() ->
+ [{timetrap, {seconds, 60}}].
+
+all() ->
+ [{group, ?util:name([E,D])} || E <- ?ERRORS, D <- ?RFCS].
+
+groups() ->
+ Tc = tc(),
+ [{?util:name([E,D]), [], [start] ++ Tc ++ [stop]}
+ || E <- ?ERRORS, D <- ?RFCS].
+
+init_per_suite(Config) ->
+ ok = diameter:start(),
+ Config.
+
+end_per_suite(_Config) ->
+ ok = diameter:stop().
+
+init_per_group(Group, Config) ->
+ [{group, Group} | Config].
+
+end_per_group(_, _) ->
+ ok.
+
+init_per_testcase(Name, Config) ->
+ [{testcase, Name} | Config].
+
+end_per_testcase(_, _) ->
+ ok.
+
+tc() ->
+ [send_unknown_application,
+ send_unknown_command,
+ send_ok,
+ send_invalid_avp_bits,
+ send_missing_avp,
+ send_ignore_missing_avp,
+ send_double_error,
+ send_3xxx,
+ send_5xxx].
+
+%% ===========================================================================
+
+%% start/1
+
+start(Config) ->
+ Group = proplists:get_value(group, Config),
+ [Errors, RFC] = ?util:name(Group),
+ ok = diameter:start_service(?SERVER, ?SERVICE(?L(Group),
+ Errors,
+ RFC)),
+ ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT,
+ callback,
+ rfc6733)),
+ LRef = ?util:listen(?SERVER, tcp),
+ ?util:connect(?CLIENT, tcp, LRef).
+
+%% stop/1
+
+stop(_Config) ->
+ ok = diameter:remove_transport(?CLIENT, true),
+ ok = diameter:remove_transport(?SERVER, true),
+ ok = diameter:stop_service(?SERVER),
+ ok = diameter:stop_service(?CLIENT).
+
+%% send_unknown_application/1
+%%
+%% Send an unknown application that a callback (which shouldn't take
+%% place) fails on.
+
+%% diameter answers.
+send_unknown_application([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3007,
+ %% UNSUPPORTED_APPLICATION
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_unknown_application(Config) ->
+ send_unknown_application(?group(Config)).
+
+%% send_unknown_command/1
+%%
+%% Send a unknown command that a callback discards.
+
+%% handle_request discards the request.
+send_unknown_command([callback, _]) ->
+ {error, timeout} = call();
+
+%% diameter answers.
+send_unknown_command([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3001,
+ %% UNSUPPORTED_COMMAND
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_unknown_command(Config) ->
+ send_unknown_command(?group(Config)).
+
+%% send_ok/1
+%%
+%% Send a correct STR that a callback answers with 5002.
+
+%% Callback answers.
+send_ok([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 5002, %% UNKNOWN_SESSION_ID
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_ok(Config) ->
+ send_ok(?group(Config)).
+
+%% send_invalid_avp_bits/1
+%%
+%% Send a request with an incorrect length on the optional
+%% Origin-State-Id that a callback ignores.
+
+%% Callback answers.
+send_invalid_avp_bits([callback, _]) ->
+ #diameter_base_STA{'Result-Code' = 2001, %% SUCCESS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+%% diameter answers.
+send_invalid_avp_bits([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3009, %% INVALID_AVP_BITS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_invalid_avp_bits(Config) ->
+ send_invalid_avp_bits(?group(Config)).
+
+%% send_missing_avp/1
+%%
+%% Send a request with a missing AVP that a callback answers.
+
+%% diameter answers.
+send_missing_avp([answer, rfc6733]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% Callback answers.
+send_missing_avp([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+send_missing_avp(Config) ->
+ send_missing_avp(?group(Config)).
+
+%% send_ignore_missing_avp/1
+%%
+%% Send a request with a missing AVP that a callback ignores.
+
+%% diameter answers.
+send_ignore_missing_avp([answer, rfc6733]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% Callback answers, ignores the error
+send_ignore_missing_avp([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 2001, %% SUCCESS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_ignore_missing_avp(Config) ->
+ send_ignore_missing_avp(?group(Config)).
+
+%% send_double_error/1
+%%
+%% Send a request with both an incorrect length on the optional
+%% Origin-State-Id and a missing AVP.
+
+%% Callback answers with STA.
+send_double_error([callback, _]) ->
+ #diameter_base_STA{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% diameter answers with answer-message.
+send_double_error([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3009, %% INVALID_AVP_BITS
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+send_double_error(Config) ->
+ send_double_error(?group(Config)).
+
+%% send_3xxx/1
+%%
+%% Send a request that's answered with a 3xxx result code.
+
+%% Callback answers.
+send_3xxx([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 3999,
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_3xxx(Config) ->
+ send_3xxx(?group(Config)).
+
+%% send_5xxx/1
+%%
+%% Send a request that's answered with a 5xxx result code.
+
+%% Callback answers but fails since 5xxx isn't allowed in an RFC 3588
+%% answer-message.
+send_5xxx([_, rfc3588]) ->
+ {error, timeout} = call();
+
+%% Callback answers.
+send_5xxx([_,_]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5999,
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_5xxx(Config) ->
+ send_5xxx(?group(Config)).
+
+%% ===========================================================================
+
+call() ->
+ Name = ?testcase(),
+ diameter:call(?CLIENT,
+ ?DICT,
+ #diameter_base_STR
+ {'Termination-Cause' = ?LOGOUT,
+ 'Auth-Application-Id' = ?COMMON,
+ 'Class' = [?L(Name)]},
+ [{extra, [Name]}]).
+
+%% ===========================================================================
+%% diameter callbacks
+
+%% peer_up/3
+
+peer_up(_SvcName, _Peer, State) ->
+ State.
+
+%% peer_down/3
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%% pick_peer/5
+
+pick_peer([Peer], _, ?CLIENT, _State, _Name) ->
+ {ok, Peer}.
+
+%% prepare_request/4
+
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) ->
+ {send, prepare(Pkt, Caps, Name)}.
+
+prepare(Pkt0, Caps, send_unknown_application) ->
+ Req = sta(Pkt0, Caps),
+ #diameter_packet{bin = <<H:8/binary, 0:32, T/binary>>}
+ = Pkt
+ = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
+
+ Pkt#diameter_packet{bin = <<H/binary, 23:32, T/binary>>};
+
+prepare(Pkt0, Caps, send_unknown_command) ->
+ Req = sta(Pkt0, Caps),
+ #diameter_packet{bin = <<H:5/binary, 275:24, T/binary>>}
+ = Pkt
+ = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
+
+ Pkt#diameter_packet{bin = <<H/binary, 572:24, T/binary>>};
+
+prepare(Pkt, Caps, T)
+ when T == send_ok;
+ T == send_3xxx;
+ T == send_5xxx ->
+ sta(Pkt, Caps);
+
+prepare(Pkt0, Caps, send_invalid_avp_bits) ->
+ Req0 = sta(Pkt0, Caps),
+ %% Append an Origin-State-Id with an incorrect AVP Length in order
+ %% to force 3009.
+ Req = Req0#diameter_base_STR{'Origin-State-Id' = [7]},
+ #diameter_packet{bin = Bin}
+ = Pkt
+ = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
+ Offset = size(Bin) - 12 + 5,
+ <<H:Offset/binary, Len:24, T/binary>> = Bin,
+ Pkt#diameter_packet{bin = <<H/binary, (Len + 2):24, T/binary>>};
+
+prepare(Pkt0, Caps, send_double_error) ->
+ dehost(prepare(Pkt0, Caps, send_invalid_avp_bits));
+
+prepare(Pkt, Caps, T)
+ when T == send_missing_avp;
+ T == send_ignore_missing_avp ->
+ Req = sta(Pkt, Caps),
+ dehost(diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req})).
+
+sta(Pkt, Caps) ->
+ #diameter_packet{msg = Req}
+ = Pkt,
+ #diameter_caps{origin_host = {OH, _},
+ origin_realm = {OR, DR}}
+ = Caps,
+ Req#diameter_base_STR{'Session-Id' = diameter:session_id(OH),
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Destination-Realm' = DR}.
+
+%% Strip Origin-Host.
+dehost(#diameter_packet{bin = Bin} = Pkt) ->
+ <<V, Len:24, H:16/binary, T0/binary>>
+ = Bin,
+ {SessionId, T1} = split_avp(T0),
+ {OriginHost, T} = split_avp(T1),
+ Delta = size(OriginHost),
+ Pkt#diameter_packet{bin = <<V, (Len - Delta):24, H/binary,
+ SessionId/binary,
+ T/binary>>}.
+
+%% handle_answer/5
+
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Name) ->
+ Pkt#diameter_packet.msg.
+
+%% handle_error/5
+
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) ->
+ {error, Reason}.
+
+split_avp(<<_:5/binary, Len:24, _/binary>> = Bin) ->
+ L = pad(Len),
+ <<Avp:L/binary, T/binary>> = Bin,
+ {Avp, T}.
+
+pad(N)
+ when 0 == N rem 4 ->
+ N;
+pad(N) ->
+ N - (N rem 4) + 4.
+
+%% handle_request/3
+
+handle_request(#diameter_packet{header = #diameter_header{application_id = 0},
+ msg = Msg},
+ ?SERVER,
+ {_, Caps}) ->
+ request(Msg, Caps).
+
+request(undefined, _) -> %% unknown command
+ discard;
+
+request(#diameter_base_STR{'Class' = [Name]} = Req, Caps) ->
+ request(?A(Name), Req, Caps).
+
+request(send_ok, Req, Caps) ->
+ {reply, #diameter_packet{msg = answer(Req, Caps),
+ errors = [5002]}}; %% UNKNOWN_SESSION_ID
+
+request(send_3xxx, _Req, _Caps) ->
+ {answer_message, 3999};
+
+request(send_5xxx, _Req, _Caps) ->
+ {answer_message, 5999};
+
+request(send_invalid_avp_bits, Req, Caps) ->
+ #diameter_base_STR{'Origin-State-Id' = []}
+ = Req,
+ %% Default errors field but a non-answer-message and only 3xxx
+ %% errors detected means diameter sets neither Result-Code nor
+ %% Failed-AVP.
+ {reply, #diameter_packet{msg = answer(Req, Caps)}};
+
+request(T, Req, Caps)
+ when T == send_double_error;
+ T == send_missing_avp ->
+ {reply, answer(Req, Caps)};
+
+request(send_ignore_missing_avp, Req, Caps) ->
+ {reply, #diameter_packet{msg = answer(Req, Caps),
+ errors = false}}. %% ignore errors
+
+answer(Req, Caps) ->
+ #diameter_base_STR{'Session-Id' = SId}
+ = Req,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}}
+ = Caps,
+ #diameter_base_STA{'Session-Id' = SId,
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Result-Code' = 2001}. %% SUCCESS
diff --git a/lib/diameter/test/diameter_app_SUITE.erl b/lib/diameter/test/diameter_app_SUITE.erl
index 53332af626..209f72adf1 100644
--- a/lib/diameter/test/diameter_app_SUITE.erl
+++ b/lib/diameter/test/diameter_app_SUITE.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
@@ -48,7 +48,6 @@
diameter_dict_parser,
diameter_dict_util,
diameter_exprecs,
- diameter_nowarn,
diameter_make]).
-define(HELP_MODULES, [diameter_dbg,
diff --git a/lib/diameter/test/diameter_ct.erl b/lib/diameter/test/diameter_ct.erl
index ded50bf6c5..1697287a22 100644
--- a/lib/diameter/test/diameter_ct.erl
+++ b/lib/diameter/test/diameter_ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2012. 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
@@ -23,16 +23,23 @@
%% Module used to run suites from Makefile.
%%
--export([run/1]).
+-export([run/1,
+ cover/0]).
%% The makefile looks for signs of failure so ignore the ct:run_test/1
%% return value.
-run([Suite]) ->
+run(Suites) ->
+ ct_run([{suite, Suites}]).
+
+cover() ->
+ ct_run([{spec, "./testspec"}]).
+
+ct_run(Opts) ->
Start = info(),
- ct:run_test([{suite, Suite},
- {logdir, "./log"},
- {auto_compile, false}]),
+ ct:run_test([{logdir, "./log"},
+ {auto_compile, false}
+ | Opts]),
info(Start , info()).
info() ->
diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl
index bb820a8bf2..0ea8ae2d4e 100644
--- a/lib/diameter/test/diameter_failover_SUITE.erl
+++ b/lib/diameter/test/diameter_failover_SUITE.erl
@@ -103,9 +103,9 @@
-define(SUCCESS, 2001).
%% Value of Termination-Cause determines client/server behaviour.
--define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
--define(MOVED, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED').
--define(TIMEOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_SESSION_TIMEOUT').
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
+-define(MOVED, ?'DIAMETER_BASE_TERMINATION-CAUSE_USER_MOVED').
+-define(TIMEOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_SESSION_TIMEOUT').
%% ===========================================================================
diff --git a/lib/diameter/test/diameter_length_SUITE.erl b/lib/diameter/test/diameter_length_SUITE.erl
index 4e413e6a42..ffb19d2288 100644
--- a/lib/diameter/test/diameter_length_SUITE.erl
+++ b/lib/diameter/test/diameter_length_SUITE.erl
@@ -41,10 +41,10 @@
%% diameter callbacks
-export([peer_up/3,
peer_down/3,
- pick_peer/6,
- prepare_request/5,
- handle_answer/6,
- handle_error/6,
+ pick_peer/5,
+ prepare_request/4,
+ handle_answer/5,
+ handle_error/5,
handle_request/3]).
-include("diameter.hrl").
@@ -73,14 +73,14 @@
{answer_errors, callback}]}]).
-define(SUCCESS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
+ ?'DIAMETER_BASE_RESULT-CODE_SUCCESS').
-define(MISSING_AVP,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_MISSING_AVP').
+ ?'DIAMETER_BASE_RESULT-CODE_MISSING_AVP').
-define(INVALID_MESSAGE_LENGTH,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_MESSAGE_LENGTH').
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_MESSAGE_LENGTH').
-define(LOGOUT,
- ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
-define(GROUPS, [exit, handle, discard]).
@@ -196,21 +196,18 @@ send(discard) ->
= call(0);
send(Config) ->
- Group = proplists:get_value(group, Config),
- put({?MODULE, group}, Group),
- send(Group).
+ send(proplists:get_value(group, Config)).
%% ===========================================================================
call(Delta) ->
- Group = get({?MODULE, group}),
diameter:call(?CLIENT,
?DICT,
#diameter_base_STR
{'Termination-Cause' = ?LOGOUT,
'Auth-Application-Id' = ?DIAMETER_APP_ID_COMMON,
'Origin-State-Id' = [7]},
- [{extra, [Group, Delta]}]).
+ [{extra, [Delta]}]).
%% ===========================================================================
%% diameter callbacks
@@ -225,14 +222,14 @@ peer_up(_SvcName, _Peer, State) ->
peer_down(_SvcName, _Peer, State) ->
State.
-%% pick_peer/6
+%% pick_peer/5
-pick_peer([Peer], _, ?CLIENT, _State, _Group, _Delta) ->
+pick_peer([Peer], _, ?CLIENT, _State, _Delta) ->
{ok, Peer}.
-%% prepare_request/5
+%% prepare_request/4
-prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, _Group, Delta) ->
+prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Delta) ->
{send, resize(Delta, prepare(Pkt, Caps))}.
prepare(#diameter_packet{msg = Req0} = Pkt, Caps) ->
@@ -253,14 +250,14 @@ resize(Delta, #diameter_packet{bin = Bin} = Pkt) ->
resize(Delta, <<V, Len:24, T/binary>>) ->
<<V, (Len + Delta):24, T/binary>>.
-%% handle_answer/6
+%% handle_answer/5
-handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Group, _Delta) ->
+handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Delta) ->
Pkt#diameter_packet.msg.
-%% handle_error/6
+%% handle_error/5
-handle_error(Reason, _Req, ?CLIENT, _Peer, _Group, _Delta) ->
+handle_error(Reason, _Req, ?CLIENT, _Peer, _Delta) ->
{error, Reason}.
%% handle_request/3
@@ -280,8 +277,12 @@ handle_request(Pkt, ?SERVER, {_Ref, Caps}) ->
answer(Group, #diameter_packet{errors = Es}, Ans) ->
answer(Group, Es, Ans);
+%% No errors: just answer.
answer(_, [], Ans) ->
{reply, Ans};
+
+%% Otherwise an invalid length should only reach the callback if
+%% length_errors = handle.
answer(Group, [RC|_], Ans)
when RC == ?INVALID_MESSAGE_LENGTH, Group == handle;
RC /= ?INVALID_MESSAGE_LENGTH ->
diff --git a/lib/diameter/test/diameter_relay_SUITE.erl b/lib/diameter/test/diameter_relay_SUITE.erl
index f10d82bdf8..614eb4d4ca 100644
--- a/lib/diameter/test/diameter_relay_SUITE.erl
+++ b/lib/diameter/test/diameter_relay_SUITE.erl
@@ -107,7 +107,7 @@
-define(LOOP_DETECTED, 3005).
-define(UNABLE_TO_DELIVER, 3002).
--define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
-define(AUTHORIZE_ONLY, ?'DIAMETER_BASE_RE-AUTH-REQUEST-TYPE_AUTHORIZE_ONLY').
%% ===========================================================================
diff --git a/lib/diameter/test/diameter_tls_SUITE.erl b/lib/diameter/test/diameter_tls_SUITE.erl
index 6cc34b20c5..92a1113758 100644
--- a/lib/diameter/test/diameter_tls_SUITE.erl
+++ b/lib/diameter/test/diameter_tls_SUITE.erl
@@ -122,7 +122,7 @@
{capabilities, Caps}]}).
-define(SUCCESS, 2001).
--define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
%% ===========================================================================
diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl
index 6727e88b66..781ed234cc 100644
--- a/lib/diameter/test/diameter_traffic_SUITE.erl
+++ b/lib/diameter/test/diameter_traffic_SUITE.erl
@@ -178,27 +178,27 @@
diameter_gen_acct_rfc6733]]]).
-define(SUCCESS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_SUCCESS').
+ ?'DIAMETER_BASE_RESULT-CODE_SUCCESS').
-define(COMMAND_UNSUPPORTED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_COMMAND_UNSUPPORTED').
+ ?'DIAMETER_BASE_RESULT-CODE_COMMAND_UNSUPPORTED').
-define(TOO_BUSY,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_TOO_BUSY').
+ ?'DIAMETER_BASE_RESULT-CODE_TOO_BUSY').
-define(APPLICATION_UNSUPPORTED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_APPLICATION_UNSUPPORTED').
+ ?'DIAMETER_BASE_RESULT-CODE_APPLICATION_UNSUPPORTED').
-define(INVALID_HDR_BITS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_HDR_BITS').
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_HDR_BITS').
-define(INVALID_AVP_BITS,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_BITS').
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_AVP_BITS').
-define(AVP_UNSUPPORTED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_AVP_UNSUPPORTED').
+ ?'DIAMETER_BASE_RESULT-CODE_AVP_UNSUPPORTED').
-define(UNSUPPORTED_VERSION,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNSUPPORTED_VERSION').
+ ?'DIAMETER_BASE_RESULT-CODE_UNSUPPORTED_VERSION').
-define(REALM_NOT_SERVED,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_REALM_NOT_SERVED').
+ ?'DIAMETER_BASE_RESULT-CODE_REALM_NOT_SERVED').
-define(UNABLE_TO_DELIVER,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_UNABLE_TO_DELIVER').
+ ?'DIAMETER_BASE_RESULT-CODE_UNABLE_TO_DELIVER').
-define(INVALID_AVP_LENGTH,
- ?'DIAMETER_BASE_RESULT-CODE_DIAMETER_INVALID_AVP_LENGTH').
+ ?'DIAMETER_BASE_RESULT-CODE_INVALID_AVP_LENGTH').
-define(EVENT_RECORD,
?'DIAMETER_BASE_ACCOUNTING-RECORD-TYPE_EVENT_RECORD').
@@ -208,11 +208,11 @@
?'DIAMETER_BASE_RE-AUTH-REQUEST-TYPE_AUTHORIZE_AUTHENTICATE').
-define(LOGOUT,
- ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_LOGOUT').
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
-define(BAD_ANSWER,
- ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_BAD_ANSWER').
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_BAD_ANSWER').
-define(USER_MOVED,
- ?'DIAMETER_BASE_TERMINATION-CAUSE_DIAMETER_USER_MOVED').
+ ?'DIAMETER_BASE_TERMINATION-CAUSE_USER_MOVED').
%% ===========================================================================
@@ -798,7 +798,8 @@ prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name, Group) ->
prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, send_detach, Group, _) ->
{eval_packet, {send, prepare(Pkt, Caps, Group)}, [fun log/2, detach]}.
-log(#diameter_packet{} = P, T) ->
+log(#diameter_packet{bin = Bin} = P, T)
+ when is_binary(Bin) ->
io:format("~p: ~p~n", [T,P]).
%% prepare/4
@@ -980,7 +981,8 @@ answer(T, {Tag, Action, Post}) ->
{Tag, answer(T, Action), Post};
answer({A,C}, {reply, Ans}) ->
answer(C, {reply, msg(Ans, A, diameter_gen_base_rfc3588)});
-answer(pkt, {reply, Ans}) ->
+answer(pkt, {reply, Ans})
+ when not is_record(Ans, diameter_packet) ->
{reply, #diameter_packet{msg = Ans}};
answer(_, T) ->
T.
diff --git a/lib/diameter/test/modules.mk b/lib/diameter/test/modules.mk
index f575085843..c4a713fb10 100644
--- a/lib/diameter/test/modules.mk
+++ b/lib/diameter/test/modules.mk
@@ -22,27 +22,28 @@ COVER_SPEC_FILE = diameter.cover
MODULES = \
diameter_ct \
- diameter_util \
diameter_enum \
- diameter_compiler_SUITE \
+ diameter_util \
+ diameter_3xxx_SUITE \
+ diameter_app_SUITE \
+ diameter_capx_SUITE \
diameter_codec_SUITE \
diameter_codec_test \
- diameter_app_SUITE \
+ diameter_compiler_SUITE \
diameter_dict_SUITE \
- diameter_reg_SUITE \
- diameter_sync_SUITE \
- diameter_stats_SUITE \
- diameter_watchdog_SUITE \
+ diameter_dpr_SUITE \
+ diameter_event_SUITE \
+ diameter_failover_SUITE \
diameter_gen_sctp_SUITE \
- diameter_transport_SUITE \
- diameter_capx_SUITE \
- diameter_traffic_SUITE \
+ diameter_length_SUITE \
+ diameter_reg_SUITE \
diameter_relay_SUITE \
+ diameter_stats_SUITE \
+ diameter_sync_SUITE \
diameter_tls_SUITE \
- diameter_failover_SUITE \
- diameter_dpr_SUITE \
- diameter_event_SUITE \
- diameter_length_SUITE
+ diameter_traffic_SUITE \
+ diameter_transport_SUITE \
+ diameter_watchdog_SUITE
HRL_FILES = \
diameter_ct.hrl
diff --git a/lib/diameter/test/testspec b/lib/diameter/test/testspec
new file mode 100644
index 0000000000..2fd8307281
--- /dev/null
+++ b/lib/diameter/test/testspec
@@ -0,0 +1,3 @@
+
+{suites, ".", all}.
+{cover, "./coverspec"}.
diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk
index 74f4c57b70..98e719c50a 100644
--- a/lib/diameter/vsn.mk
+++ b/lib/diameter/vsn.mk
@@ -18,5 +18,5 @@
# %CopyrightEnd%
APPLICATION = diameter
-DIAMETER_VSN = 1.4
+DIAMETER_VSN = 1.4.1
APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index 599036f380..a87a8471e3 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -660,7 +660,7 @@ read_source(Name, Opts0) ->
check_forms(Forms, Name),
Forms;
{error, R} ->
- edoc_report:error({"error reading file '~s'.",
+ edoc_report:error({"error reading file '~ts'.",
[edoc_lib:filename(Name)]}),
exit({error, R})
end.
@@ -677,7 +677,84 @@ read_source_2(Name, Opts) ->
Includes = proplists:append_values(includes, Opts)
++ [filename:dirname(Name)],
Macros = proplists:append_values(macros, Opts),
- epp:parse_file(Name, Includes, Macros).
+ %% epp:parse_file(Name, Includes, Macros).
+ parse_file(Name, Includes, Macros).
+
+%% The code below has been copied from epp.erl.
+%%
+%% Copy the line of the last token to the last token that will be
+%% part of the parse tree.
+%%
+%% The last line is used in edoc_extract:find_type_docs() to determine
+%% if a type declaration is followed by a comment.
+%% <example>
+%% -type t() :: [
+%% {tag, integer()}
+%% ].
+%% %% Protocol options.
+%% </example>
+%% The line of the dot token will be copied to the integer token.
+
+parse_file(Name, Includes, Macros) ->
+ case epp:open(Name, Includes, Macros) of
+ {ok, Epp} ->
+ try {ok, parse_file(Epp)}
+ after _ = epp:close(Epp)
+ end;
+ Error ->
+ Error
+ end.
+
+parse_file(Epp) ->
+ case scan_and_parse(Epp) of
+ {ok, Form} ->
+ case Form of
+ {attribute,La,record,{Record, Fields}} ->
+ case epp:normalize_typed_record_fields(Fields) of
+ {typed, NewFields} ->
+ [{attribute, La, record, {Record, NewFields}},
+ {attribute, La, type,
+ {{record, Record}, Fields, []}}
+ | parse_file(Epp)];
+ not_typed ->
+ [Form | parse_file(Epp)]
+ end;
+ _ ->
+ [Form | parse_file(Epp)]
+ end;
+ {error, E} ->
+ [{error, E} | parse_file(Epp)];
+ {eof, Location} ->
+ [{eof, Location}]
+ end.
+
+scan_and_parse(Epp) ->
+ case epp:scan_erl_form(Epp) of
+ {ok, Toks0} ->
+ Toks = fix_last_line(Toks0),
+ case erl_parse:parse_form(Toks) of
+ {ok, Form} ->
+ {ok, Form};
+ Else ->
+ Else
+ end;
+ Else ->
+ Else
+ end.
+
+fix_last_line(Toks0) ->
+ Toks1 = lists:reverse(Toks0),
+ {line, LastLine} = erl_scan:token_info(hd(Toks1), line),
+ fll(Toks1, LastLine, []).
+
+fll([{Category, Attributes0, Symbol} | L], LastLine, Ts) ->
+ F = fun(_OldLine) -> LastLine end,
+ Attributes = erl_scan:set_attribute(line, Attributes0, F),
+ lists:reverse(L, [{Category, Attributes, Symbol} | Ts]);
+fll([T | L], LastLine, Ts) ->
+ fll(L, LastLine, [T | Ts]);
+fll(L, _LastLine, Ts) ->
+ lists:reverse(L, Ts).
check_forms(Fs, Name) ->
Fun = fun (F) ->
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index a0c1ae1c0f..ce1e94a26a 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -200,7 +200,7 @@ source({M, P, Name, Path}, Dir, Suffix, Env, Set, Private, Hidden,
{Set, Error}
end;
R ->
- report("skipping source file '~s': ~W.", [File, R, 15]),
+ report("skipping source file '~ts': ~W.", [File, R, 15]),
{Set, true}
end.
@@ -216,14 +216,14 @@ check_name(M, M0, P0, File) ->
ok;
_ ->
if N =/= N0 ->
- warning("file '~s' actually contains module '~s'.",
+ warning("file '~ts' actually contains module '~s'.",
[File, M]);
true ->
ok
end
end,
if P =/= P0 ->
- warning("file '~s' belongs to package '~s', not '~s'.",
+ warning("file '~ts' belongs to package '~s', not '~s'.",
[File, P, P0]);
true ->
ok
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index d9c225e099..276f48453e 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -466,20 +466,20 @@ uri_get("file://localhost/" ++ Path) ->
uri_get_file(Path);
uri_get("file://" ++ Path) ->
Msg = io_lib:format("cannot handle 'file:' scheme with "
- "nonlocal network-path: 'file://~s'.",
+ "nonlocal network-path: 'file://~ts'.",
[Path]),
{error, Msg};
uri_get("file:/" ++ Path) ->
uri_get_file(Path);
uri_get("file:" ++ Path) ->
- Msg = io_lib:format("ignoring malformed URI: 'file:~s'.", [Path]),
+ Msg = io_lib:format("ignoring malformed URI: 'file:~ts'.", [Path]),
{error, Msg};
uri_get("http:" ++ Path) ->
uri_get_http("http:" ++ Path);
uri_get("ftp:" ++ Path) ->
uri_get_ftp("ftp:" ++ Path);
uri_get("//" ++ Path) ->
- Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]),
+ Msg = io_lib:format("cannot access network-path: '//~ts'.", [Path]),
{error, Msg};
uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
uri_get_file(Path); % special case for Windows
@@ -490,7 +490,7 @@ uri_get(URI) ->
true ->
uri_get_file(URI);
false ->
- Msg = io_lib:format("cannot handle URI: '~s'.", [URI]),
+ Msg = io_lib:format("cannot handle URI: '~ts'.", [URI]),
{error, Msg}
end.
@@ -555,12 +555,12 @@ uri_get_http_1(Result, URI) ->
end.
http_errmsg(Reason, URI) ->
- io_lib:format("http error: ~s: '~s'", [Reason, URI]).
+ io_lib:format("http error: ~ts: '~ts'", [Reason, URI]).
%% TODO: implement ftp access method
uri_get_ftp(URI) ->
- Msg = io_lib:format("cannot access ftp scheme yet: '~s'.", [URI]),
+ Msg = io_lib:format("cannot access ftp scheme yet: '~ts'.", [URI]),
{error, Msg}.
%% @private
@@ -615,7 +615,7 @@ copy_file(From, To) ->
{ok, _} -> ok;
{error, R} ->
R1 = file:format_error(R),
- report("error copying '~s' to '~s': ~s.", [From, To, R1]),
+ report("error copying '~ts' to '~ts': ~ts.", [From, To, R1]),
exit(error)
end.
@@ -631,7 +631,7 @@ list_dir(Dir, Error) ->
fun (S, As) -> warning(S, As), [] end
end,
R1 = file:format_error(R),
- F("could not read directory '~s': ~s.", [filename(Dir), R1])
+ F("could not read directory '~ts': ~ts.", [filename(Dir), R1])
end.
%% @private
@@ -667,7 +667,7 @@ simplify_path(P) ->
%% ok -> ok;
%% {error, R} ->
%% R1 = file:format_error(R),
-%% report("cannot create directory '~s': ~s.", [Dir, R1]),
+%% report("cannot create directory '~ts': ~ts.", [Dir, R1]),
%% exit(error)
%% end.
@@ -707,7 +707,7 @@ write_file(Text, Dir, Name, Package, Options) ->
ok = file:close(FD);
{error, R} ->
R1 = file:format_error(R),
- report("could not write file '~s': ~s.", [File, R1]),
+ report("could not write file '~ts': ~ts.", [File, R1]),
exit(error)
end.
@@ -761,7 +761,7 @@ read_info_file(Dir) ->
parse_info_file(Text, File);
{error, R} ->
R1 = file:format_error(R),
- warning("could not read '~s': ~s.", [File, R1]),
+ warning("could not read '~ts': ~ts.", [File, R1]),
{?NO_APP, [], []}
end;
false ->
@@ -776,7 +776,7 @@ uri_get_info_file(Base) ->
{ok, Text} ->
parse_info_file(Text, URI);
{error, Msg} ->
- warning("could not read '~s': ~s.", [URI, Msg]),
+ warning("could not read '~ts': ~ts.", [URI, Msg]),
{?NO_APP, [], []}
end.
@@ -785,10 +785,10 @@ parse_info_file(Text, Name) ->
{ok, Vs} ->
info_file_data(Vs);
{error, eof} ->
- warning("unexpected end of file in '~s'.", [Name]),
+ warning("unexpected end of file in '~ts'.", [Name]),
{?NO_APP, [], []};
{error, {_Line,Module,R}} ->
- warning("~s: ~s.", [Module:format_error(R), Name]),
+ warning("~ts: ~ts.", [Module:format_error(R), Name]),
{?NO_APP, [], []}
end.
@@ -1033,7 +1033,7 @@ run_plugin(Name, Key, Default, Fun, Opts) when is_atom(Name) ->
{ok, Value} ->
Value;
R ->
- report("error in ~s '~w': ~W.", [Name, Module, R, 20]),
+ report("error in ~ts '~w': ~W.", [Name, Module, R, 20]),
exit(error)
end.
diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl
index 08686c4fb5..8efbfd00c7 100644
--- a/lib/edoc/src/edoc_macros.erl
+++ b/lib/edoc/src/edoc_macros.erl
@@ -88,7 +88,7 @@ link_macro(S, Line, Env) ->
true -> " target=\"_top\""; % note the initial space
false -> ""
end,
- lists:flatten(io_lib:fwrite("<a href=\"~s\"~s>~ts</a>",
+ lists:flatten(io_lib:fwrite("<a href=\"~ts\"~ts>~ts</a>",
[URI, Target, Txt])).
section_macro(S, _Line, _Env) ->
@@ -102,7 +102,7 @@ type_macro(S, Line, Env) ->
Def = edoc_parser:parse_typedef(S1, Line),
{#t_typedef{type = T}, _} = Def,
Txt = edoc_layout:type(edoc_data:type(T, Env)),
- lists:flatten(io_lib:fwrite("<code>~s</code>", [Txt])).
+ lists:flatten(io_lib:fwrite("<code>~ts</code>", [Txt])).
%% Expand inline macros in tag content.
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index cf1a2d6b11..a20f152f34 100644
--- a/lib/edoc/src/edoc_parser.yrl
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -462,4 +462,4 @@ throw_error({parse_throws, E}, L) ->
throw_error(parse_param, L) ->
throw({error, L, "missing parameter name"});
throw_error({Where, E}, L) when is_list(Where) ->
- throw({error,L,{"unknown error parsing ~s: ~P.",[Where,E,15]}}).
+ throw({error,L,{"unknown error parsing ~ts: ~P.",[Where,E,15]}}).
diff --git a/lib/edoc/src/edoc_report.erl b/lib/edoc/src/edoc_report.erl
index 9bec08ab97..dc6320df6d 100644
--- a/lib/edoc/src/edoc_report.erl
+++ b/lib/edoc/src/edoc_report.erl
@@ -83,13 +83,13 @@ report(L, Where, S, Vs) ->
io:nl().
where({File, module}) ->
- io_lib:fwrite("~s, in module header: ", [File]);
+ io_lib:fwrite("~ts, in module header: ", [File]);
where({File, footer}) ->
- io_lib:fwrite("~s, in module footer: ", [File]);
+ io_lib:fwrite("~ts, in module footer: ", [File]);
where({File, header}) ->
- io_lib:fwrite("~s, in header file: ", [File]);
+ io_lib:fwrite("~ts, in header file: ", [File]);
where({File, {F, A}}) ->
- io_lib:fwrite("~s, function ~s/~w: ", [File, F, A]);
+ io_lib:fwrite("~ts, function ~s/~w: ", [File, F, A]);
where([]) ->
io_lib:fwrite("~s: ", [?APPLICATION]);
where(File) when is_list(File) ->
diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl
index 48b6137ac1..b5a1ef713d 100644
--- a/lib/edoc/src/edoc_run.erl
+++ b/lib/edoc/src/edoc_run.erl
@@ -162,7 +162,7 @@ file(Args) ->
-spec invalid_args(string(), list()) -> no_return().
invalid_args(Where, Args) ->
- report("invalid arguments to ~s: ~w.", [Where, Args]),
+ report("invalid arguments to ~ts: ~w.", [Where, Args]),
shutdown_error().
run(F) ->
@@ -213,13 +213,13 @@ parse_arg(A) ->
{ok, Expr} ->
case catch erl_parse:normalise(Expr) of
{'EXIT', _} ->
- report("bad argument: '~s':", [A]),
+ report("bad argument: '~ts':", [A]),
exit(error);
Term ->
Term
end;
{error, _, D} ->
- report("error parsing argument '~s'", [A]),
+ report("error parsing argument '~ts'", [A]),
error(D),
exit(error)
end.
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 2d986988c2..eb41f1922a 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -391,10 +391,10 @@ parse_header(Data, Line, Env, Where) when is_list(Where) ->
-spec throw_error(line(), err()) -> no_return().
throw_error(L, {read_file, File, R}) ->
- throw_error(L, {"error reading file '~s': ~w",
+ throw_error(L, {"error reading file '~ts': ~w",
[edoc_lib:filename(File), R]});
throw_error(L, {file_not_found, F}) ->
- throw_error(L, {"file not found: ~s", [F]});
+ throw_error(L, {"file not found: ~ts", [F]});
throw_error(L, file_not_string) ->
throw_error(L, "expected file name as a string");
throw_error(L, D) ->
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
index cc0529d2a9..5d0d78bf3c 100644
--- a/lib/edoc/src/edoc_wiki.erl
+++ b/lib/edoc/src/edoc_wiki.erl
@@ -295,7 +295,7 @@ expand_uri([], _, L, _Ss, Us, _As) ->
expand_uri_error(Us, L) ->
{Ps, _} = edoc_lib:split_at(lists:reverse(Us), $:),
- throw_error(L, {"reference '[~s:...' ended unexpectedly", [Ps]}).
+ throw_error(L, {"reference '[~ts:...' ended unexpectedly", [Ps]}).
push_uri(Us, Ss, As) ->
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 028676bfd3..0a2c6e822f 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1071,7 +1071,7 @@ type(hipe_bifs, ref_set, 2, Xs) ->
strict(arg_types(hipe_bifs, ref_set, 2), Xs, fun (_) -> t_nil() end);
type(hipe_bifs, remove_refs_from, 1, Xs) ->
strict(arg_types(hipe_bifs, remove_refs_from, 1), Xs,
- fun (_) -> t_nil() end);
+ fun (_) -> t_atom('ok') end);
type(hipe_bifs, set_funinfo_native_address, 3, Xs) ->
strict(arg_types(hipe_bifs, set_funinfo_native_address, 3), Xs,
fun (_) -> t_nil() end);
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index 532b2e43cd..d1243b2325 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -205,7 +205,7 @@
t_var/1,
t_var_name/1,
%% t_assign_variables_to_subtype/2,
- type_is_defined/3,
+ type_is_defined/4,
record_field_diffs_to_string/2,
subst_all_vars_to_any/1,
lift_list_to_pos_empty/1,
@@ -544,12 +544,12 @@ t_opaque_from_records(RecDict) ->
OpaqueRecDict =
dict:filter(fun(Key, _Value) ->
case Key of
- {opaque, _Name} -> true;
+ {opaque, _Name, _Arity} -> true;
_ -> false
end
end, RecDict),
OpaqueTypeDict =
- dict:map(fun({opaque, Name}, {Module, Type, ArgNames}) ->
+ dict:map(fun({opaque, Name, _Arity}, {Module, Type, ArgNames}) ->
case ArgNames of
[] ->
t_opaque(Module, Name, [], t_from_form(Type, RecDict));
@@ -707,8 +707,8 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
MFA = {RemMod, Name, ArgsLen},
case sets:is_element(MFA, ET) of
true ->
- case lookup_type(Name, RemDict) of
- {type, {_Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) ->
+ case lookup_type(Name, ArgsLen, RemDict) of
+ {type, {_Mod, Type, ArgNames}} ->
{NewType, NewCycle, NewRR} =
case can_unfold_more(RemType, C) of
true ->
@@ -726,7 +726,7 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
false -> RT
end,
{RT1, RetRR};
- {opaque, {Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) ->
+ {opaque, {Mod, Type, ArgNames}} ->
List = lists:zip(ArgNames, Args),
TmpVarDict = dict:from_list(List),
{Rep, NewCycle, NewRR} =
@@ -746,12 +746,6 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
{t_from_form({opaque, -1, Name, {Mod, Args, RT1}},
RemDict, TmpVarDict),
RetRR};
- {type, _} ->
- Msg = io_lib:format("Unknown remote type ~w\n", [Name]),
- throw({error, Msg});
- {opaque, _} ->
- Msg = io_lib:format("Unknown remote opaque type ~w\n", [Name]),
- throw({error, Msg});
error ->
Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
[RemMod, Name]),
@@ -3241,6 +3235,8 @@ t_to_string(?bitstr(0, 0), _RecDict) ->
"<<>>";
t_to_string(?bitstr(8, 0), _RecDict) ->
"binary()";
+t_to_string(?bitstr(1, 0), _RecDict) ->
+ "bitstring()";
t_to_string(?bitstr(0, B), _RecDict) ->
lists:flatten(io_lib:format("<<_:~w>>", [B]));
t_to_string(?bitstr(U, 0), _RecDict) ->
@@ -3680,8 +3676,9 @@ t_from_form({type, _L, union, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
{L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict),
{t_sup(L), R};
t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
- case lookup_type(Name, RecDict) of
- {type, {_Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
+ ArgsLen = length(Args),
+ case lookup_type(Name, ArgsLen, RecDict) of
+ {type, {_Module, Type, ArgNames}} ->
case can_unfold_more({type, Name}, TypeNames) of
true ->
List = lists:zipwith(
@@ -3701,7 +3698,7 @@ t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
end;
false -> {t_any(), [{type, Name}]}
end;
- {opaque, {Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
+ {opaque, {Module, Type, ArgNames}} ->
{Rep, Rret} =
case can_unfold_more({opaque, Name}, TypeNames) of
true ->
@@ -3730,12 +3727,9 @@ t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
RecDict, VarDict)
end,
{Tret, Rret};
- {type, _} ->
- throw({error, io_lib:format("Unknown type ~w\n", [Name])});
- {opaque, _} ->
- throw({error, io_lib:format("Unknown opaque type ~w\n", [Name])});
error ->
- throw({error, io_lib:format("Unable to find type ~w\n", [Name])})
+ Msg = io_lib:format("Unable to find type ~w/~w\n", [Name, ArgsLen]),
+ throw({error, Msg})
end;
t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _InOpaque,
_RecDict, _VarDict) ->
@@ -3870,12 +3864,14 @@ t_form_to_string({type, _L, binary, [Base, Unit]} = Type) ->
case {U, B} of
{0, 0} -> "<<>>";
{8, 0} -> "binary()";
+ {1, 0} -> "bitstring()";
{0, B} -> lists:flatten(io_lib:format("<<_:~w>>", [B]));
{U, 0} -> lists:flatten(io_lib:format("<<_:_*~w>>", [U]));
{U, B} -> lists:flatten(io_lib:format("<<_:~w,_:_*~w>>", [B, U]))
end;
_ -> io_lib:format("Badly formed bitstr type ~w", [Type])
end;
+t_form_to_string({type, _L, bitstring, []}) -> "bitstring()";
t_form_to_string({type, _L, 'fun', []}) -> "fun()";
t_form_to_string({type, _L, 'fun', [{type, _, any}, Range]}) ->
"fun(...) -> " ++ t_form_to_string(Range);
@@ -3986,20 +3982,20 @@ lookup_record(Tag, Arity, RecDict) when is_atom(Tag) ->
error -> error
end.
-lookup_type(Name, RecDict) ->
- case dict:find({type, Name}, RecDict) of
+lookup_type(Name, Arity, RecDict) ->
+ case dict:find({type, Name, Arity}, RecDict) of
error ->
- case dict:find({opaque, Name}, RecDict) of
+ case dict:find({opaque, Name, Arity}, RecDict) of
error -> error;
{ok, Found} -> {opaque, Found}
end;
{ok, Found} -> {type, Found}
end.
--spec type_is_defined('type' | 'opaque', atom(), dict()) -> boolean().
+-spec type_is_defined('type' | 'opaque', atom(), arity(), dict()) -> boolean().
-type_is_defined(TypeOrOpaque, Name, RecDict) ->
- dict:is_key({TypeOrOpaque, Name}, RecDict).
+type_is_defined(TypeOrOpaque, Name, Arity, RecDict) ->
+ dict:is_key({TypeOrOpaque, Name, Arity}, RecDict).
can_unfold_more(TypeName, TypeNames) ->
Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end,
diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl
index 1e12a647d7..7d463103e3 100644
--- a/lib/kernel/src/auth.erl
+++ b/lib/kernel/src/auth.erl
@@ -391,7 +391,7 @@ create_cookie(Name) ->
{error,Reason} ->
{error,
lists:flatten(
- io_lib:format("Failed to create cookie file '~s': ~p", [Name, Reason]))}
+ io_lib:format("Failed to create cookie file '~ts': ~p", [Name, Reason]))}
end.
random_cookie(0, _, Result) ->
diff --git a/lib/kernel/src/file_server.erl b/lib/kernel/src/file_server.erl
index 73202319b9..6b413ff630 100644
--- a/lib/kernel/src/file_server.erl
+++ b/lib/kernel/src/file_server.erl
@@ -170,7 +170,7 @@ handle_call({read_link_info, Name, Opts}, _From, Handle) ->
handle_call({read_link, Name}, _From, Handle) ->
{reply, ?PRIM_FILE:read_link(Handle, Name), Handle};
handle_call({read_link_all, Name}, _From, Handle) ->
- {reply, ?PRIM_FILE:read_link(Handle, Name), Handle};
+ {reply, ?PRIM_FILE:read_link_all(Handle, Name), Handle};
handle_call({make_link, Old, New}, _From, Handle) ->
{reply, ?PRIM_FILE:make_link(Handle, Old, New), Handle};
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index 06d404905d..e676ca997d 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -218,7 +218,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
{MFAs,Addresses} = exports(ExportMap, CodeAddress),
%% Remove references to old versions of the module.
ReferencesToPatch = get_refs_from(MFAs, []),
- remove_refs_from(MFAs),
+ ok = remove_refs_from(MFAs),
%% Patch all dynamic references in the code.
%% Function calls, Atoms, Constants, System calls
patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
@@ -802,7 +802,7 @@ patch_to_emu_step1(Mod) ->
%% Find all call sites that call these MFAs. As a side-effect,
%% create native stubs for any MFAs that are referred.
ReferencesToPatch = get_refs_from(MFAs, []),
- remove_refs_from(MFAs),
+ ok = remove_refs_from(MFAs),
ReferencesToPatch;
false ->
%% The first time we load the module, no redirection needs to be done.
@@ -846,11 +846,8 @@ get_refs_from(MFAs, []) ->
mark_referred_from(MFAs),
MFAs.
-mark_referred_from([MFA|MFAs]) ->
- hipe_bifs:mark_referred_from(MFA),
- mark_referred_from(MFAs);
-mark_referred_from([]) ->
- [].
+mark_referred_from(MFAs) ->
+ lists:foreach(fun(MFA) -> hipe_bifs:mark_referred_from(MFA) end, MFAs).
%%--------------------------------------------------------------------
%% Given a list of MFAs with referred_from references, update their
@@ -858,11 +855,8 @@ mark_referred_from([]) ->
%%
%% The {MFA,Refs} list must come from get_refs_from/2.
%%
-redirect([MFA|Rest]) ->
- hipe_bifs:redirect_referred_from(MFA),
- redirect(Rest);
-redirect([]) ->
- ok.
+redirect(MFAs) ->
+ lists:foreach(fun(MFA) -> hipe_bifs:redirect_referred_from(MFA) end, MFAs).
%%--------------------------------------------------------------------
%% Given a list of MFAs, remove all referred_from references having
@@ -874,11 +868,8 @@ redirect([]) ->
%% list. The refers_to list is used here to find the CalleeMFAs whose
%% referred_from lists should be updated.
%%
-remove_refs_from([CallerMFA|CallerMFAs]) ->
- hipe_bifs:remove_refs_from(CallerMFA),
- remove_refs_from(CallerMFAs);
-remove_refs_from([]) ->
- [].
+remove_refs_from(MFAs) ->
+ lists:foreach(fun(MFA) -> hipe_bifs:remove_refs_from(MFA) end, MFAs).
%%--------------------------------------------------------------------
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 4c040f0a0e..79d11c155c 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -716,7 +716,7 @@ analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) ->
%% These modules should be loaded by code.erl before
%% the code_server is started.
OK = [erlang, os, prim_file, erl_prim_loader, init, ets,
- code_server, lists, lists_sort, unicode, binary, filename, packages,
+ code_server, lists, lists_sort, unicode, binary, filename,
gb_sets, gb_trees, hipe_unified_loader, hipe_bifs,
prim_zip, zlib],
ErrCnt1 =
@@ -822,6 +822,10 @@ check_funs({'$M_EXPR','$F_EXPR',2},
check_funs({'$M_EXPR','$F_EXPR',1},
[{lists,foreach,2},
{hipe_unified_loader,patch_consts,3} | _]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',1},
+ [{lists,foreach,2},
+ {hipe_unified_loader,mark_referred_from,1},
+ {hipe_unified_loader,get_refs_from,2}| _]) -> 0;
check_funs({'$M_EXPR',warning_msg,2},
[{code_server,finish_on_load_report,2} | _]) -> 0;
%% This is cheating! /raimo
@@ -1590,7 +1594,7 @@ native_early_modules_1(Architecture) ->
?line true = lists:all(fun code:is_module_native/1,
[ets,file,filename,gb_sets,gb_trees,
%%hipe_unified_loader, no_native as workaround
- lists,os,packages]),
+ lists,os]),
ok
end.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index fd4d5bd24e..eda901f3d6 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -45,7 +45,7 @@
init_per_testcase/2, end_per_testcase/2,
read_write_file/1, names/1]).
-export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1,
- list_dir/1,list_dir_error/1,
+ list_dir/1,list_dir_error/1, untranslatable_names/1,
pos1/1, pos2/1]).
-export([close/1, consult1/1, path_consult/1, delete/1]).
-export([ eval1/1, path_eval/1, script1/1, path_script/1,
@@ -117,7 +117,7 @@ all() ->
groups() ->
[{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1,
- list_dir, list_dir_error]},
+ list_dir, list_dir_error, untranslatable_names]},
{files, [],
[{group, open}, {group, pos}, {group, file_info},
{group, consult}, {group, eval}, {group, script},
@@ -557,6 +557,78 @@ list_dir_1(TestDir, Cnt, Sorted0) ->
Sorted = lists:sort(DirList1),
list_dir_1(TestDir, Cnt-1, Sorted).
+untranslatable_names(Config) ->
+ case no_untranslatable_names() of
+ true ->
+ {skip,"Not a problem on this OS"};
+ false ->
+ untranslatable_names_1(Config)
+ end.
+
+untranslatable_names_1(Config) ->
+ {ok,OldCwd} = file:get_cwd(),
+ PrivDir = ?config(priv_dir, Config),
+ Dir = filename:join(PrivDir, "untranslatable_names"),
+ ok = file:make_dir(Dir),
+ Node = start_node(untranslatable_names, "+fnu"),
+ try
+ ok = file:set_cwd(Dir),
+ [ok = file:write_file(F, F) || {_,F} <- untranslatable_names()],
+
+ ExpectedListDir0 = [unicode:characters_to_list(N, utf8) ||
+ {utf8,N} <- untranslatable_names()],
+ ExpectedListDir = lists:sort(ExpectedListDir0),
+ io:format("ExpectedListDir: ~p\n", [ExpectedListDir]),
+ ExpectedListDir = call_and_sort(Node, file, list_dir, [Dir]),
+
+ ExpectedListDirAll0 = [case Enc of
+ utf8 ->
+ unicode:characters_to_list(N, utf8);
+ latin1 ->
+ N
+ end || {Enc,N} <- untranslatable_names()],
+ ExpectedListDirAll = lists:sort(ExpectedListDirAll0),
+ io:format("ExpectedListDirAll: ~p\n", [ExpectedListDirAll]),
+ ExpectedListDirAll = call_and_sort(Node, file, list_dir_all, [Dir])
+ after
+ catch test_server:stop_node(Node),
+ file:set_cwd(OldCwd),
+ [file:delete(F) || {_,F} <- untranslatable_names()],
+ file:del_dir(Dir)
+ end,
+ ok.
+
+untranslatable_names() ->
+ [{utf8,<<"abc">>},
+ {utf8,<<"def">>},
+ {utf8,<<"Lagerl",195,182,"f">>},
+ {utf8,<<195,150,"stra Emterwik">>},
+ {latin1,<<"M",229,"rbacka">>},
+ {latin1,<<"V",228,"rmland">>}].
+
+call_and_sort(Node, M, F, A) ->
+ {ok,Res} = rpc:call(Node, M, F, A),
+ lists:sort(Res).
+
+no_untranslatable_names() ->
+ case os:type() of
+ {unix,darwin} -> true;
+ {win32,_} -> true;
+ _ -> false
+ end.
+
+start_node(Name, Args) ->
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+ ct:log("Trying to start ~w@~s~n", [Name,Host]),
+ case test_server:start_node(Name, peer, [{args,Args}]) of
+ {error,Reason} ->
+ test_server:fail(Reason);
+ {ok,Node} ->
+ ct:log("Node ~p started~n", [Node]),
+ Node
+ end.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
index e8529db1dc..808a10ee27 100644
--- a/lib/kernel/test/file_name_SUITE.erl
+++ b/lib/kernel/test/file_name_SUITE.erl
@@ -340,9 +340,9 @@ check_icky(Mod) ->
?line true=(length("åäö") =:= 3),
?line UniMode = file:native_name_encoding() =/= latin1,
?line make_icky_dir(Mod),
- ?line {ok, L0} = Mod:list_dir("."),
+ {ok, L0} = Mod:list_dir_all("."),
?line L1 = lists:sort(L0),
- io:format("~p ~p~n",[L1,list(icky_dir())]),
+ io:format("~p~n~p~n~n",[L1,lists:sort(list(icky_dir()))]),
?line L1 = lists:sort(convlist(list(icky_dir()))),
?line {ok,D2} = Mod:get_cwd(),
?line true = is_list(D2),
@@ -357,7 +357,8 @@ check_icky(Mod) ->
?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,icky_dir()))}
|| {T,S,Targ} <- icky_dir(), T =:= symlink ],
?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
- ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
+ [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) ||
+ {SymL,Targ,_} <- Syms ],
?line chk_cre_dir(Mod,[{directory,"åäö_dir",icky_dir()}]),
?line {ok,BeginAt} = Mod:get_cwd(),
?line true = is_list(BeginAt),
@@ -369,7 +370,7 @@ check_icky(Mod) ->
?line ok = Mod:set_cwd(".."),
?line {ok,BeginAt} = Mod:get_cwd(),
?line rm_r2(Mod,"åäö_dir"),
- {OS,TYPE} = os:type(),
+ {OS,_} = os:type(),
% Check that treat_icky really converts to the same as the OS
case UniMode of
true ->
@@ -377,7 +378,7 @@ check_icky(Mod) ->
?line ok = Mod:set_cwd("åäö_dir"),
?line ok = Mod:write_file(<<"ååå">>,<<"hello">>),
?line Treated = treat_icky(<<"ååå">>),
- ?line {ok,[Treated]} = Mod:list_dir("."),
+ {ok,[Treated]} = Mod:list_dir_all("."),
?line ok = Mod:delete(<<"ååå">>),
?line {ok,[]} = Mod:list_dir("."),
?line ok = Mod:set_cwd(".."),
@@ -393,15 +394,7 @@ check_icky(Mod) ->
true ->
ok
end,
- ?line ok = Mod:set_cwd(treat_icky(<<"åäö_dir">>)),
- ?line {ok, NowAt2} = Mod:get_cwd(),
- io:format("~p~n",[NowAt2]),
- % Cannot create raw unicode-breaking filenames on windows or macos
- ?line true = ((((not UniMode) or (OS =:= win32) or (TYPE=:=darwin)) and is_list(NowAt2)) orelse ((UniMode) and is_binary(NowAt2))),
- ?line true = BeginAt =/= NowAt2,
- ?line ok = Mod:set_cwd(".."),
?line {ok,BeginAt} = Mod:get_cwd(),
- ?line rm_r2(Mod,conv(treat_icky(<<"åäö_dir">>))),
case has_links() of
true ->
?line ok = Mod:make_link("fil1","nisseö"),
@@ -485,7 +478,7 @@ check_very_icky(Mod) ->
ok
end,
?line make_very_icky_dir(Mod),
- ?line {ok, L0} = Mod:list_dir("."),
+ {ok, L0} = Mod:list_dir_all("."),
?line L1 = lists:sort(L0),
?line L1 = lists:sort(convlist(list(very_icky_dir()))),
?line {ok,D2} = Mod:get_cwd(),
@@ -494,7 +487,8 @@ check_very_icky(Mod) ->
?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,very_icky_dir()))}
|| {T,S,Targ} <- very_icky_dir(), T =:= symlink ],
?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
- ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
+ ?line [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) ||
+ {SymL,Targ,_} <- Syms ],
?line chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]),
?line {ok,BeginAt} = Mod:get_cwd(),
?line true = is_list(BeginAt),
@@ -559,22 +553,6 @@ check_very_icky(Mod) ->
FI#file_info{mode = NewMode2}),
?line {ok,#file_info{mode = NewMode2}} =
Mod:read_file_info([956,965,963,954,959,49]),
- ?line NumOK0 = case has_links() of
- true -> 5;
- false -> 3
- end,
- ?line NumNOK0 = case has_links() of
- true -> 4;
- false -> 3
- end,
- ?line {NumOK,NumNOK} = case is_binary(treat_icky(<<"foo">>)) of
- false ->
- {NumOK0+NumNOK0,0};
- true ->
- {NumOK0,NumNOK0}
- end,
- ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}),
- ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false),
ok
catch
throw:need_unicode_mode ->
@@ -593,7 +571,7 @@ check_very_icky(Mod) ->
rm_rf(Mod,Dir) ->
case Mod:read_link_info(Dir) of
{ok, #file_info{type = directory}} ->
- {ok, Content} = Mod:list_dir(Dir),
+ {ok, Content} = Mod:list_dir_all(Dir),
[ rm_rf(Mod,filename:join(Dir,C)) || C <- Content ],
Mod:del_dir(Dir),
ok;
@@ -608,7 +586,7 @@ rm_r(Mod,Dir) ->
case Mod:read_link_info(Dir) of
{ok, #file_info{type = directory}} ->
{ok,#file_info{type = directory}} = Mod:read_file_info(Dir),
- {ok, Content} = Mod:list_dir(Dir),
+ {ok, Content} = Mod:list_dir_all(Dir),
[ true = is_list(Part) || Part <- Content ],
[ true = is_list(filename:join(Dir,Part)) || Part <- Content ],
[ rm_r(Mod,filename:join(Dir,C)) || C <- Content ],
@@ -626,7 +604,7 @@ rm_r2(Mod,Dir) ->
case Mod:read_link_info(Dir) of
{ok, #file_info{type = directory}} ->
{ok,#file_info{type = directory}} = Mod:read_file_info(Dir),
- {ok, Content} = Mod:list_dir(Dir),
+ {ok, Content} = Mod:list_dir_all(Dir),
UniMode = file:native_name_encoding() =/= latin1,
[ true = (is_list(Part) orelse UniMode) || Part <- Content ],
[ true = (is_list(filename:join(Dir,Part)) orelse UniMode) || Part <- Content ],
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index 22b496258f..e531b78a5b 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -127,7 +127,7 @@ file(File, Opts0) ->
leex_ret(St).
format_error({file_error, Reason}) ->
- io_lib:fwrite("~s",[file:format_error(Reason)]);
+ io_lib:fwrite("~ts",[file:format_error(Reason)]);
format_error(missing_defs) -> "missing Definitions";
format_error(missing_rules) -> "missing Rules";
format_error(missing_code) -> "missing Erlang code";
@@ -301,10 +301,10 @@ pack_warnings([]) ->
report_errors(St) ->
when_opt(fun () ->
foreach(fun({File,{none,Mod,E}}) ->
- io:fwrite("~s: ~ts\n",
+ io:fwrite("~ts: ~ts\n",
[File,Mod:format_error(E)]);
({File,{Line,Mod,E}}) ->
- io:fwrite("~s:~w: ~ts\n",
+ io:fwrite("~ts:~w: ~ts\n",
[File,Line,Mod:format_error(E)])
end, sort(St#leex.errors))
end, report_errors, St#leex.opts).
@@ -319,11 +319,11 @@ report_warnings(St) ->
ShouldReport = member(report_warnings, St#leex.opts) orelse ReportWerror,
when_bool(fun () ->
foreach(fun({File,{none,Mod,W}}) ->
- io:fwrite("~s: ~s~ts\n",
+ io:fwrite("~ts: ~s~ts\n",
[File,Prefix,
Mod:format_error(W)]);
({File,{Line,Mod,W}}) ->
- io:fwrite("~s:~w: ~s~ts\n",
+ io:fwrite("~ts:~w: ~s~ts\n",
[File,Line,Prefix,
Mod:format_error(W)])
end, sort(St#leex.warnings))
@@ -401,7 +401,7 @@ parse_file(St0) ->
{ok,Xfile} ->
St1 = St0#leex{encoding = epp:set_encoding(Xfile)},
try
- verbose_print(St1, "Parsing file ~s, ", [St1#leex.xfile]),
+ verbose_print(St1, "Parsing file ~ts, ", [St1#leex.xfile]),
%% We KNOW that errors throw so we can ignore them here.
{ok,Line1,St2} = parse_head(Xfile, St1),
{ok,Line2,Macs,St3} = parse_defs(Xfile, Line1, St2),
@@ -1292,7 +1292,7 @@ pack_dfa([], _, Rs, PDFA) -> {PDFA,Rs}.
%% the code for the actions.
out_file(St0, DFA, DF, Actions, Code) ->
- verbose_print(St0, "Writing file ~s, ", [St0#leex.efile]),
+ verbose_print(St0, "Writing file ~ts, ", [St0#leex.efile]),
case open_inc_file(St0) of
{ok,Ifile} ->
try
@@ -1582,7 +1582,7 @@ pp_sep(_, _, _, _) -> " ".
%% with Graphviz.
out_dfa_graph(St, DFA, DF) ->
- verbose_print(St, "Writing DFA to file ~s, ", [St#leex.gfile]),
+ verbose_print(St, "Writing DFA to file ~ts, ", [St#leex.gfile]),
case file:open(St#leex.gfile, [write]) of
{ok,Gfile} ->
try
@@ -1644,7 +1644,7 @@ output_encoding_comment(File, #leex{encoding = Encoding}) ->
io:fwrite(File, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]).
output_file_directive(File, Filename, Line) ->
- io:fwrite(File, <<"-file(~s, ~w).\n">>,
+ io:fwrite(File, <<"-file(~ts, ~w).\n">>,
[format_filename(Filename), Line]).
format_filename(Filename) ->
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index 30e0db421e..53292b037a 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -185,7 +185,7 @@ format_error({endsymbol_is_terminal, Symbol}) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({file_error, Reason}) ->
- io_lib:fwrite("~s",[file:format_error(Reason)]);
+ io_lib:fwrite("~ts",[file:format_error(Reason)]);
format_error(illegal_empty) ->
io_lib:fwrite("illegal use of empty symbol", []);
format_error({internal_error, Error}) ->
@@ -481,7 +481,7 @@ generate(St0) ->
?PASS(action_conflicts), ?PASS(write_file)],
F = case member(time, St1#yecc.options) of
true ->
- io:fwrite(<<"Generating parser from grammar in ~s\n">>,
+ io:fwrite(<<"Generating parser from grammar in ~ts\n">>,
[format_filename(St1#yecc.infile)]),
fun timeit/3;
false ->
@@ -858,10 +858,10 @@ report_errors(St) ->
case member(report_errors, St#yecc.options) of
true ->
foreach(fun({File,{none,Mod,E}}) ->
- io:fwrite(<<"~s: ~ts\n">>,
+ io:fwrite(<<"~ts: ~ts\n">>,
[File,Mod:format_error(E)]);
({File,{Line,Mod,E}}) ->
- io:fwrite(<<"~s:~w: ~ts\n">>,
+ io:fwrite(<<"~ts:~w: ~ts\n">>,
[File,Line,Mod:format_error(E)])
end, sort(St#yecc.errors));
false ->
@@ -878,11 +878,11 @@ report_warnings(St) ->
case member(report_warnings, St#yecc.options) orelse ReportWerror of
true ->
foreach(fun({File,{none,Mod,W}}) ->
- io:fwrite(<<"~s: ~s~ts\n">>,
+ io:fwrite(<<"~ts: ~s~ts\n">>,
[File,Prefix,
Mod:format_error(W)]);
({File,{Line,Mod,W}}) ->
- io:fwrite(<<"~s:~w: ~s~ts\n">>,
+ io:fwrite(<<"~ts:~w: ~s~ts\n">>,
[File,Line,Prefix,
Mod:format_error(W)])
end, sort(St#yecc.warnings));
@@ -2518,7 +2518,7 @@ output_encoding_comment(#yecc{encoding = Encoding}=St) ->
fwrite(St, <<"%% ~s\n">>, [epp:encoding_to_string(Encoding)]).
output_file_directive(St, Filename, Line) when St#yecc.file_attrs ->
- fwrite(St, <<"-file(~s, ~w).\n">>,
+ fwrite(St, <<"-file(~ts, ~w).\n">>,
[format_filename(Filename), Line]);
output_file_directive(St, _Filename, _Line) ->
St.
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index f57ee13460..bd0d3d49dd 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -91,7 +91,8 @@
</type>
<desc>
<p>Connects to an SSH server. No channel is started. This is done
- by calling ssh_connect:session_channel/2.</p>
+ by calling
+ <seealso marker="ssh_connection#session_channel/2">ssh_connection:session_channel/[2, 4]</seealso>.</p>
<p>Options are:</p>
<taglist>
<tag><c><![CDATA[{user_dir, string()}]]></c></tag>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 76e14860ec..a8a494b2fc 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,12 +1,14 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {<<"5.2">>, [{restart_application, ssl}]},
{<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
{<<"3\\.*">>, [{restart_application, ssl}]}
],
[
+ {<<"5.2">>, [{restart_application, ssl}]},
{<<"5.1\\*">>, [{restart_application, ssl}]},
{<<"5.0\\*">>, [{restart_application, ssl}]},
{<<"4\\.*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 647daeb1ac..0ba59cede2 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -162,7 +162,7 @@ connect(Host, Port, Options, Timeout) ->
%% Description: Creates an ssl listen socket.
%%--------------------------------------------------------------------
listen(_Port, []) ->
- {error, enooptions};
+ {error, nooptions};
listen(Port, Options0) ->
try
{ok, Config} = handle_options(Options0, server),
@@ -380,13 +380,13 @@ getopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}},
{ok, _} = Result ->
Result;
{error, InetError} ->
- {error, {eoptions, {socket_options, OptionTags, InetError}}}
+ {error, {options, {socket_options, OptionTags, InetError}}}
catch
_:_ ->
- {error, {eoptions, {socket_options, OptionTags}}}
+ {error, {options, {socket_options, OptionTags}}}
end;
getopts(#sslsocket{}, OptionTags) ->
- {error, {eoptions, {socket_options, OptionTags}}}.
+ {error, {options, {socket_options, OptionTags}}}.
%%--------------------------------------------------------------------
-spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}.
@@ -400,7 +400,7 @@ setopts(#sslsocket{pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) ->
ssl_connection:set_opts(Pid, Options)
catch
_:_ ->
- {error, {eoptions, {not_a_proplist, Options0}}}
+ {error, {options, {not_a_proplist, Options0}}}
end;
setopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}, Options) when is_list(Options) ->
@@ -408,13 +408,13 @@ setopts(#sslsocket{pid = {ListenSocket, #config{cb = {Transport,_,_,_}}}}, Optio
ok ->
ok;
{error, InetError} ->
- {error, {eoptions, {socket_options, Options, InetError}}}
+ {error, {options, {socket_options, Options, InetError}}}
catch
_:Error ->
- {error, {eoptions, {socket_options, Options, Error}}}
+ {error, {options, {socket_options, Options, Error}}}
end;
setopts(#sslsocket{}, Options) ->
- {error, {eoptions,{not_a_proplist, Options}}}.
+ {error, {options,{not_a_proplist, Options}}}.
%%---------------------------------------------------------------
-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}.
@@ -503,24 +503,26 @@ format_error({error, Reason}) ->
format_error(Reason) when is_list(Reason) ->
Reason;
format_error(closed) ->
- "The connection is closed";
-format_error({ecacertfile, _}) ->
- "Own CA certificate file is invalid.";
-format_error({ecertfile, _}) ->
- "Own certificate file is invalid.";
-format_error({ekeyfile, _}) ->
- "Own private key file is invalid.";
-format_error({essl, Description}) ->
- Description;
-format_error({eoptions, Options}) ->
- lists:flatten(io_lib:format("Error in options list: ~p~n", [Options]));
+ "TLS connection is closed";
+format_error({tls_alert, Description}) ->
+ "TLS Alert: " ++ Description;
+format_error({options,{FileType, File, Reason}}) when FileType == cacertfile;
+ FileType == certfile;
+ FileType == keyfile;
+ FileType == dhfile ->
+ Error = file_error_format(Reason),
+ file_desc(FileType) ++ File ++ ": " ++ Error;
+format_error({options, {socket_options, Option, Error}}) ->
+ lists:flatten(io_lib:format("Invalid transport socket option ~p: ~s", [Option, format_error(Error)]));
+format_error({options, {socket_options, Option}}) ->
+ lists:flatten(io_lib:format("Invalid socket option: ~p", [Option]));
+format_error({options, Options}) ->
+ lists:flatten(io_lib:format("Invalid TLS option: ~p", [Options]));
format_error(Error) ->
- case (catch inet:format_error(Error)) of
- "unkknown POSIX" ++ _ ->
- no_format(Error);
- {'EXIT', _} ->
- no_format(Error);
+ case inet:format_error(Error) of
+ "unknown POSIX" ++ _ ->
+ unexpected_format(Error);
Other ->
Other
end.
@@ -541,8 +543,6 @@ random_bytes(N) ->
crypto:rand_bytes(N)
end.
-
-
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -559,11 +559,11 @@ do_connect(Address, Port,
{error, Reason}
catch
exit:{function_clause, _} ->
- {error, {eoptions, {cb_info, CbInfo}}};
+ {error, {options, {cb_info, CbInfo}}};
exit:badarg ->
- {error, {eoptions, {socket_options, UserOpts}}};
+ {error, {options, {socket_options, UserOpts}}};
exit:{badarg, _} ->
- {error, {eoptions, {socket_options, UserOpts}}}
+ {error, {options, {socket_options, UserOpts}}}
end.
handle_options(Opts0, _Role) ->
@@ -607,7 +607,7 @@ handle_options(Opts0, _Role) ->
{verify_peer, UserFailIfNoPeerCert,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
Value ->
- throw({error, {eoptions, {verify, Value}}})
+ throw({error, {options, {verify, Value}}})
end,
CertFile = handle_option(certfile, Opts, <<>>),
@@ -754,9 +754,9 @@ validate_option(ciphers, Value) when is_list(Value) ->
try cipher_suites(Version, Value)
catch
exit:_ ->
- throw({error, {eoptions, {ciphers, Value}}});
+ throw({error, {options, {ciphers, Value}}});
error:_->
- throw({error, {eoptions, {ciphers, Value}}})
+ throw({error, {options, {ciphers, Value}}})
end;
validate_option(reuse_session, Value) when is_function(Value) ->
Value;
@@ -781,7 +781,7 @@ validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredPro
when is_list(PreferredProtocols) ->
case ssl_record:highest_protocol_version([]) of
{3,0} ->
- throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}});
_ ->
validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
validate_npn_ordering(Precedence),
@@ -792,7 +792,7 @@ validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredPro
byte_size(Default) > 0, byte_size(Default) < 256 ->
case ssl_record:highest_protocol_version([]) of
{3,0} ->
- throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}});
_ ->
validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
validate_npn_ordering(Precedence),
@@ -804,7 +804,7 @@ validate_option(client_preferred_next_protocols, undefined) ->
validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
case ssl_record:highest_protocol_version([]) of
{3,0} ->
- throw({error, {eoptions, {not_supported_in_sslv3, {Opt, Value}}}});
+ throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}});
_ ->
validate_binary_list(next_protocols_advertised, Value),
Value
@@ -813,14 +813,14 @@ validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
validate_option(next_protocols_advertised, undefined) ->
undefined;
validate_option(Opt, Value) ->
- throw({error, {eoptions, {Opt, Value}}}).
+ throw({error, {options, {Opt, Value}}}).
validate_npn_ordering(client) ->
ok;
validate_npn_ordering(server) ->
ok;
validate_npn_ordering(Value) ->
- throw({error, {eoptions, {client_preferred_next_protocols, {invalid_precedence, Value}}}}).
+ throw({error, {options, {client_preferred_next_protocols, {invalid_precedence, Value}}}}).
validate_binary_list(Opt, List) ->
lists:foreach(
@@ -829,7 +829,7 @@ validate_binary_list(Opt, List) ->
byte_size(Bin) < 256 ->
ok;
(Bin) ->
- throw({error, {eoptions, {Opt, {invalid_protocol, Bin}}}})
+ throw({error, {options, {Opt, {invalid_protocol, Bin}}}})
end, List).
validate_versions([], Versions) ->
@@ -840,23 +840,23 @@ validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2';
Version == sslv3 ->
validate_versions(Rest, Versions);
validate_versions([Ver| _], Versions) ->
- throw({error, {eoptions, {Ver, {versions, Versions}}}}).
+ throw({error, {options, {Ver, {versions, Versions}}}}).
validate_inet_option(mode, Value)
when Value =/= list, Value =/= binary ->
- throw({error, {eoptions, {mode,Value}}});
+ throw({error, {options, {mode,Value}}});
validate_inet_option(packet, Value)
when not (is_atom(Value) orelse is_integer(Value)) ->
- throw({error, {eoptions, {packet,Value}}});
+ throw({error, {options, {packet,Value}}});
validate_inet_option(packet_size, Value)
when not is_integer(Value) ->
- throw({error, {eoptions, {packet_size,Value}}});
+ throw({error, {options, {packet_size,Value}}});
validate_inet_option(header, Value)
when not is_integer(Value) ->
- throw({error, {eoptions, {header,Value}}});
+ throw({error, {options, {header,Value}}});
validate_inet_option(active, Value)
when Value =/= true, Value =/= false, Value =/= once ->
- throw({error, {eoptions, {active,Value}}});
+ throw({error, {options, {active,Value}}});
validate_inet_option(_, _) ->
ok.
@@ -935,8 +935,27 @@ cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
cipher_suites(Version, Ciphers).
-no_format(Error) ->
- lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])).
+unexpected_format(Error) ->
+ lists:flatten(io_lib:format("Unexpected error: ~p", [Error])).
+
+file_error_format({error, Error})->
+ case file:format_error(Error) of
+ "unknown POSIX error" ->
+ "decoding error";
+ Str ->
+ Str
+ end;
+file_error_format(_) ->
+ "decoding error".
+
+file_desc(cacertfile) ->
+ "Invalid CA certificate file ";
+file_desc(certfile) ->
+ "Invalid certificate file ";
+file_desc(keyfile) ->
+ "Invalid key file ";
+file_desc(dhfile) ->
+ "Invalid DH params file ".
detect(_Pred, []) ->
undefined;
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index f94a1136a0..94e95d3cd3 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -45,7 +45,7 @@
reason_code(#alert{description = ?CLOSE_NOTIFY}, _) ->
closed;
reason_code(#alert{description = Description}, _) ->
- {essl, description_txt(Description)}.
+ {tls_alert, description_txt(Description)}.
%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index f51f1c6115..52b765f191 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1136,9 +1136,8 @@ init_certificates(#ssl_options{cacerts = CaCerts,
end,
{ok, _, _, _, _, _} = ssl_manager:connection_init(Certs, Role)
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CACertFile, {ecacertfile, Reason},
- erlang:get_stacktrace())
+ _:Reason ->
+ file_error(CACertFile, {cacertfile, Reason})
end,
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role).
@@ -1158,9 +1157,8 @@ init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHan
[OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle),
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert}
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason, CertFile, {ecertfile, Reason},
- erlang:get_stacktrace())
+ _:Reason ->
+ file_error(CertFile, {certfile, Reason})
end;
init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) ->
{ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}.
@@ -1177,9 +1175,8 @@ init_private_key(DbHandle, undefined, KeyFile, Password, _) ->
],
private_key(public_key:pem_entry_decode(PemEntry, Password))
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason, KeyFile, {ekeyfile, Reason},
- erlang:get_stacktrace())
+ _:Reason ->
+ file_error(KeyFile, {keyfile, Reason})
end;
%% First two clauses are for backwards compatibility
@@ -1205,18 +1202,14 @@ private_key(#'PrivateKeyInfo'{privateKeyAlgorithm =
private_key(Key) ->
Key.
--spec(handle_file_error(_,_,_,_,_,_) -> no_return()).
-handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) ->
- file_error(Line, Error, Reason, File, Throw, Stack);
-handle_file_error(Line, Error, Reason, File, Throw, Stack) ->
- file_error(Line, Error, Reason, File, Throw, Stack).
-
--spec(file_error(_,_,_,_,_,_) -> no_return()).
-file_error(Line, Error, Reason, File, Throw, Stack) ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [Line, Error, Reason, File, Stack]),
- error_logger:error_report(Report),
- throw(Throw).
+-spec(file_error(_,_) -> no_return()).
+file_error(File, Throw) ->
+ case Throw of
+ {Opt,{badmatch, {error, {badmatch, Error}}}} ->
+ throw({options, {Opt, binary_to_list(File), Error}});
+ _ ->
+ throw(Throw)
+ end.
init_diffie_hellman(_,Params, _,_) when is_binary(Params)->
public_key:der_decode('DHParameter', Params);
@@ -1234,9 +1227,8 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) ->
?DEFAULT_DIFFIE_HELLMAN_PARAMS
end
catch
- Error:Reason ->
- handle_file_error(?LINE, Error, Reason,
- DHParamFile, {edhfile, Reason}, erlang:get_stacktrace())
+ _:Reason ->
+ file_error(DHParamFile, {dhfile, Reason})
end.
sync_send_all_state_event(FsmPid, Event) ->
@@ -2179,13 +2171,13 @@ get_socket_opts(Transport, Socket, [Tag | Tags], SockOpts, Acc) ->
{ok, [Opt]} ->
get_socket_opts(Transport, Socket, Tags, SockOpts, [Opt | Acc]);
{error, Error} ->
- {error, {eoptions, {socket_option, Tag, Error}}}
+ {error, {options, {socket_options, Tag, Error}}}
catch
%% So that inet behavior does not crash our process
- _:Error -> {error, {eoptions, {socket_option, Tag, Error}}}
+ _:Error -> {error, {options, {socket_options, Tag, Error}}}
end;
get_socket_opts(_, _,Opts, _,_) ->
- {error, {eoptions, {socket_options, Opts, function_clause}}}.
+ {error, {options, {socket_options, Opts, function_clause}}}.
set_socket_opts(_,_, [], SockOpts, []) ->
{ok, SockOpts};
@@ -2195,18 +2187,18 @@ set_socket_opts(Transport, Socket, [], SockOpts, Other) ->
ok ->
{ok, SockOpts};
{error, InetError} ->
- {{error, {eoptions, {socket_option, Other, InetError}}}, SockOpts}
+ {{error, {options, {socket_options, Other, InetError}}}, SockOpts}
catch
_:Error ->
%% So that inet behavior does not crash our process
- {{error, {eoptions, {socket_option, Other, Error}}}, SockOpts}
+ {{error, {options, {socket_options, Other, Error}}}, SockOpts}
end;
set_socket_opts(Transport,Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{mode = Mode}, Other);
set_socket_opts(_, _, [{mode, _} = Opt| _], SockOpts, _) ->
- {{error, {eoptions, {socket_option, Opt}}}, SockOpts};
+ {{error, {options, {socket_options, Opt}}}, SockOpts};
set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw;
Packet == 0;
Packet == 1;
@@ -2225,19 +2217,19 @@ set_socket_opts(Transport,Socket, [{packet, Packet}| Opts], SockOpts, Other) whe
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
set_socket_opts(_, _, [{packet, _} = Opt| _], SockOpts, _) ->
- {{error, {eoptions, {socket_option, Opt}}}, SockOpts};
+ {{error, {options, {socket_options, Opt}}}, SockOpts};
set_socket_opts(Transport, Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{header = Header}, Other);
set_socket_opts(_, _, [{header, _} = Opt| _], SockOpts, _) ->
- {{error,{eoptions, {socket_option, Opt}}}, SockOpts};
+ {{error,{options, {socket_options, Opt}}}, SockOpts};
set_socket_opts(Transport, Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once;
Active == true;
Active == false ->
set_socket_opts(Transport, Socket, Opts,
SockOpts#socket_options{active = Active}, Other);
set_socket_opts(_, _, [{active, _} = Opt| _], SockOpts, _) ->
- {{error, {eoptions, {socket_option, Opt}} }, SockOpts};
+ {{error, {options, {socket_options, Opt}} }, SockOpts};
set_socket_opts(Transport, Socket, [Opt | Opts], SockOpts, Other) ->
set_socket_opts(Transport, Socket, Opts, SockOpts, [Opt | Other]).
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index db203a47c4..b5c6a1da49 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -99,10 +99,10 @@ options_tests() ->
invalid_inet_set_option_not_list,
invalid_inet_set_option_improper_list,
dh_params,
- ecertfile,
- ecacertfile,
- ekeyfile,
- eoptions,
+ invalid_certfile,
+ invalid_cacertfile,
+ invalid_keyfile,
+ invalid_options,
protocol_versions,
empty_protocol_versions,
ipv6,
@@ -822,7 +822,7 @@ invalid_inet_get_option_not_list(Config) when is_list(Config) ->
get_invalid_inet_option_not_list(Socket) ->
- {error, {eoptions, {socket_options, some_invalid_atom_here}}}
+ {error, {options, {socket_options, some_invalid_atom_here}}}
= ssl:getopts(Socket, some_invalid_atom_here),
ok.
@@ -854,7 +854,7 @@ invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
get_invalid_inet_option_improper_list(Socket) ->
- {error, {eoptions, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
+ {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
ok.
%%--------------------------------------------------------------------
@@ -884,10 +884,10 @@ invalid_inet_set_option(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
set_invalid_inet_option(Socket) ->
- {error, {eoptions, {socket_option, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
- {error, {eoptions, {socket_option, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
- {error, {eoptions, {socket_option, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
- {error, {eoptions, {socket_option, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
+ {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
+ {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
+ {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
+ {error, {options, {socket_options, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
ok.
%%--------------------------------------------------------------------
invalid_inet_set_option_not_list() ->
@@ -917,7 +917,7 @@ invalid_inet_set_option_not_list(Config) when is_list(Config) ->
set_invalid_inet_option_not_list(Socket) ->
- {error, {eoptions, {not_a_proplist, some_invalid_atom_here}}}
+ {error, {options, {not_a_proplist, some_invalid_atom_here}}}
= ssl:setopts(Socket, some_invalid_atom_here),
ok.
@@ -948,7 +948,7 @@ invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
set_invalid_inet_option_improper_list(Socket) ->
- {error, {eoptions, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
+ {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
ssl:setopts(Socket, [{packet, 0} | {foo, 2}]),
ok.
@@ -1286,9 +1286,9 @@ ipv6(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ekeyfile() ->
+invalid_keyfile() ->
[{doc,"Test what happens with an invalid key file"}].
-ekeyfile(Config) when is_list(Config) ->
+invalid_keyfile(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
BadOpts = ?config(server_bad_key, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1304,16 +1304,17 @@ ekeyfile(Config) when is_list(Config) ->
ssl_test_lib:start_client_error([{node, ClientNode},
{port, Port}, {host, Hostname},
{from, self()}, {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, {error, ekeyfile}, Client,
- {error, closed}).
+
+ File = proplists:get_value(keyfile,BadOpts),
+ ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client,
+ {error, closed}).
%%--------------------------------------------------------------------
-ecertfile() ->
+invalid_certfile() ->
[{doc,"Test what happens with an invalid cert file"}].
-ecertfile(Config) when is_list(Config) ->
+invalid_certfile(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerBadOpts = ?config(server_bad_cert, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1330,16 +1331,16 @@ ecertfile(Config) when is_list(Config) ->
{port, Port}, {host, Hostname},
{from, self()},
{options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, {error, ecertfile}, Client,
- {error, closed}).
+ File = proplists:get_value(certfile, ServerBadOpts),
+ ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}},
+ Client, {error, closed}).
%%--------------------------------------------------------------------
-ecacertfile() ->
+invalid_cacertfile() ->
[{doc,"Test what happens with an invalid cacert file"}].
-ecacertfile(Config) when is_list(Config) ->
+invalid_cacertfile(Config) when is_list(Config) ->
ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)],
ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)],
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1357,11 +1358,12 @@ ecacertfile(Config) when is_list(Config) ->
{port, Port0}, {host, Hostname},
{from, self()},
{options, ClientOpts}]),
+
+ File0 = proplists:get_value(cacertfile, ServerBadOpts),
- ssl_test_lib:check_result(Server0, {error, ecacertfile},
+ ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}},
Client0, {error, closed}),
- File0 = proplists:get_value(cacertfile, ServerBadOpts),
File = File0 ++ "do_not_exit.pem",
ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)],
@@ -1378,31 +1380,32 @@ ecacertfile(Config) when is_list(Config) ->
{from, self()},
{options, ClientOpts}]),
- ssl_test_lib:check_result(Server1, {error, ecacertfile},
+
+ ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}},
Client1, {error, closed}),
ok.
%%--------------------------------------------------------------------
-eoptions() ->
+invalid_options() ->
[{doc,"Test what happens when we give invalid options"}].
-eoptions(Config) when is_list(Config) ->
+invalid_options(Config) when is_list(Config) ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) ->
ssl_test_lib:check_result(Server,
- {error, {eoptions, {sslv2, Option}}},
+ {error, {options, {sslv2, Option}}},
Client,
- {error, {eoptions, {sslv2, Option}}});
+ {error, {options, {sslv2, Option}}});
(Client, Server, Option) ->
ssl_test_lib:check_result(Server,
- {error, {eoptions, Option}},
+ {error, {options, Option}},
Client,
- {error, {eoptions, Option}})
+ {error, {options, Option}})
end,
TestOpts = [{versions, [sslv2, sslv3]},
@@ -1593,8 +1596,8 @@ default_reject_anonymous(Config) when is_list(Config) ->
[{ciphers,[Cipher]} |
ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {essl, "insufficient security"}},
- Client, {error, {essl, "insufficient security"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}},
+ Client, {error, {tls_alert, "insufficient security"}}).
%%--------------------------------------------------------------------
reuse_session() ->
@@ -3147,7 +3150,7 @@ treashold(N, _) ->
N + 1.
get_invalid_inet_option(Socket) ->
- {error, {eoptions, {socket_option, foo, _}}} = ssl:getopts(Socket, [foo]),
+ {error, {options, {socket_options, foo, _}}} = ssl:getopts(Socket, [foo]),
ok.
shutdown_result(Socket, server) ->
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 86e1d47be7..26938bda50 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -252,8 +252,8 @@ server_require_peer_cert_fail(Config) when is_list(Config) ->
{from, self()},
{options, [{active, false} | BadClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {essl, "handshake failure"}},
- Client, {error, {essl, "handshake failure"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}},
+ Client, {error, {tls_alert, "handshake failure"}}).
%%--------------------------------------------------------------------
@@ -293,14 +293,14 @@ verify_fun_always_run_client(Config) when is_list(Config) ->
[{verify, verify_peer},
{verify_fun, FunAndState}
| ClientOpts]}]),
- %% Server error may be {essl,"handshake failure"} or closed depending on timing
+ %% Server error may be {tls_alert,"handshake failure"} or closed depending on timing
%% this is not a bug it is a circumstance of how tcp works!
receive
{Server, ServerError} ->
ct:print("Server Error ~p~n", [ServerError])
end,
- ssl_test_lib:check_result(Client, {error, {essl, "handshake failure"}}).
+ ssl_test_lib:check_result(Client, {error, {tls_alert, "handshake failure"}}).
%%--------------------------------------------------------------------
verify_fun_always_run_server() ->
@@ -342,14 +342,14 @@ verify_fun_always_run_server(Config) when is_list(Config) ->
[{verify, verify_peer}
| ClientOpts]}]),
- %% Client error may be {essl, "handshake failure" } or closed depending on timing
+ %% Client error may be {tls_alert, "handshake failure" } or closed depending on timing
%% this is not a bug it is a circumstance of how tcp works!
receive
{Client, ClientError} ->
ct:print("Client Error ~p~n", [ClientError])
end,
- ssl_test_lib:check_result(Server, {error, {essl, "handshake failure"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "handshake failure"}}).
%%--------------------------------------------------------------------
@@ -432,8 +432,8 @@ cert_expired(Config) when is_list(Config) ->
{from, self()},
{options, [{verify, verify_peer} | ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {essl, "certificate expired"}},
- Client, {error, {essl, "certificate expired"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "certificate expired"}},
+ Client, {error, {tls_alert, "certificate expired"}}).
two_digits_str(N) when N < 10 ->
lists:flatten(io_lib:format("0~p", [N]));
@@ -710,8 +710,8 @@ invalid_signature_server(Config) when is_list(Config) ->
{from, self()},
{options, [{verify, verify_peer} | ClientOpts]}]),
- tcp_delivery_workaround(Server, {error, {essl, "bad certificate"}},
- Client, {error, {essl, "bad certificate"}}).
+ tcp_delivery_workaround(Server, {error, {tls_alert, "bad certificate"}},
+ Client, {error, {tls_alert, "bad certificate"}}).
%%--------------------------------------------------------------------
@@ -747,8 +747,8 @@ invalid_signature_client(Config) when is_list(Config) ->
{from, self()},
{options, NewClientOpts}]),
- tcp_delivery_workaround(Server, {error, {essl, "bad certificate"}},
- Client, {error, {essl, "bad certificate"}}).
+ tcp_delivery_workaround(Server, {error, {tls_alert, "bad certificate"}},
+ Client, {error, {tls_alert, "bad certificate"}}).
%%--------------------------------------------------------------------
@@ -792,7 +792,7 @@ server_verify_no_cacerts(Config) when is_list(Config) ->
{options, [{verify, verify_peer}
| ServerOpts]}]),
- ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}).
+ ssl_test_lib:check_result(Server, {error, {options, {cacertfile, ""}}}).
%%--------------------------------------------------------------------
@@ -829,8 +829,8 @@ unknown_server_ca_fail(Config) when is_list(Config) ->
{verify_fun, FunAndState}
| ClientOpts]}]),
- ssl_test_lib:check_result(Server, {error, {essl, "unknown ca"}},
- Client, {error, {essl, "unknown ca"}}).
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "unknown ca"}},
+ Client, {error, {tls_alert, "unknown ca"}}).
%%--------------------------------------------------------------------
unknown_server_ca_accept_verify_none() ->
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index 4e848095a5..862690cd7b 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -106,15 +106,15 @@ end_per_group(_GroupName, Config) ->
%%--------------------------------------------------------------------
validate_empty_protocols_are_not_allowed(Config) when is_list(Config) ->
- {error, {eoptions, {next_protocols_advertised, {invalid_protocol, <<>>}}}}
+ {error, {options, {next_protocols_advertised, {invalid_protocol, <<>>}}}}
= (catch ssl:listen(9443,
[{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])),
- {error, {eoptions, {client_preferred_next_protocols, {invalid_protocol, <<>>}}}}
+ {error, {options, {client_preferred_next_protocols, {invalid_protocol, <<>>}}}}
= (catch ssl:connect({127,0,0,1}, 9443,
[{client_preferred_next_protocols,
{client, [<<"foo/1">>, <<"">>], <<"foox/1">>}}], infinity)),
Option = {client_preferred_next_protocols, {invalid_protocol, <<"">>}},
- {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)).
+ {error, {options, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)).
%--------------------------------------------------------------------------------
@@ -126,12 +126,12 @@ validate_empty_advertisement_list_is_allowed(Config) when is_list(Config) ->
validate_advertisement_must_be_a_binary_list(Config) when is_list(Config) ->
Option = {next_protocols_advertised, blah},
- {error, {eoptions, Option}} = (catch ssl:listen(9443, [Option])).
+ {error, {options, Option}} = (catch ssl:listen(9443, [Option])).
%--------------------------------------------------------------------------------
validate_client_protocols_must_be_a_tuple(Config) when is_list(Config) ->
Option = {client_preferred_next_protocols, [<<"foo/1">>]},
- {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option])).
+ {error, {options, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option])).
%--------------------------------------------------------------------------------
@@ -220,7 +220,7 @@ npn_not_supported_client(Config) when is_list(Config) ->
{from, self()}, {options, ClientOpts}]),
ssl_test_lib:check_result(Client, {error,
- {eoptions,
+ {options,
{not_supported_in_sslv3, PrefProtocols}}}).
%--------------------------------------------------------------------------------
@@ -229,7 +229,7 @@ npn_not_supported_server(Config) when is_list(Config)->
AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
ServerOpts = [AdvProtocols] ++ ServerOpts0,
- {error, {eoptions, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
+ {error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 8d96a70a6e..d58541df52 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -206,7 +206,7 @@ close(Pid) ->
check_result(Server, {error, SReason} = ServerMsg, Client, {error, closed} = ClientMsg) ->
receive
- {Server, {error, {SReason, _}}} ->
+ {Server, {error, SReason}} ->
receive
{Client, ClientMsg} ->
ok;
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 7c0c00bf36..4f53132d5d 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -902,7 +902,7 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ok
end,
- ssl_test_lib:check_result(Server, {error, {essl, "protocol version"}}),
+ ssl_test_lib:check_result(Server, {error, {tls_alert, "protocol version"}}),
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index cb73e86ede..1f3bef83c8 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 5.2
+SSL_VSN = 5.2.1
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index e31ae6b9ef..91d317489c 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -512,7 +512,7 @@ m(M) ->
print_object_file(Mod) ->
case code:is_loaded(Mod) of
{file,File} ->
- format("Object file: ~s\n", [File]);
+ format("Object file: ~ts\n", [File]);
_ ->
ignore
end.
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 6a27cff589..48f6622565 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -3709,7 +3709,7 @@ maybe_error_logger(Name, Why) ->
Trimmer = fun(M, _F, _A) -> M =:= erl_eval end,
Formater = fun(Term, I) -> io_lib:print(Term, I, 80, -1) end,
X = lib:format_stacktrace(1, Stacktrace, Trimmer, Formater),
- error_logger:Name("qlc: temporary file was needed for ~w\n~s\n",
+ error_logger:Name("qlc: temporary file was needed for ~w\n~ts\n",
[Why, lists:flatten(X)]).
expand_stacktrace() ->
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index dc17e5d33c..4a51ef564c 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -96,7 +96,7 @@
misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1,
heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
- types_do/1, sleeper/0, rpc_externals/0, memory_do/1,
+ types_do/1, sleeper/0, memory_do/1,
ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4
]).
@@ -5989,33 +5989,103 @@ make_ext_ref() ->
init_externals() ->
case get(externals) of
undefined ->
- SysDistSz = ets:info(sys_dist,size),
- ?line Pa = filename:dirname(code:which(?MODULE)),
- ?line {ok, Node} = test_server:start_node(plopp, slave, [{args, " -pa " ++ Pa}]),
- ?line Res = case rpc:call(Node, ?MODULE, rpc_externals, []) of
- {badrpc, {'EXIT', E}} ->
- test_server:fail({rpcresult, E});
- R -> R
- end,
- ?line test_server:stop_node(Node),
-
- %% Wait for table 'sys_dist' to stabilize
- repeat_while(fun() ->
- case ets:info(sys_dist,size) of
- SysDistSz -> false;
- Sz ->
- io:format("Waiting for sys_dist to revert size from ~p to size ~p\n",
- [Sz, SysDistSz]),
- receive after 1000 -> true end
- end
- end),
+ OtherNode = {gurka@sallad, 1},
+ Res = {mk_pid(OtherNode, 7645, 8123),
+ mk_port(OtherNode, 187489773),
+ mk_ref(OtherNode, [262143, 1293964255, 3291964278])},
put(externals, Res);
{_,_,_} -> ok
end.
-rpc_externals() ->
- {self(), make_port(), make_ref()}.
+%%
+%% Node container constructor functions
+%%
+
+-define(VERSION_MAGIC, 131).
+-define(PORT_EXT, 102).
+-define(PID_EXT, 103).
+-define(NEW_REFERENCE_EXT, 114).
+
+uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
+ [(Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint32_be(Uint) ->
+ exit({badarg, uint32_be, [Uint]}).
+
+uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
+ [(Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint16_be(Uint) ->
+ exit({badarg, uint16_be, [Uint]}).
+
+uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
+ Uint band 16#ff;
+uint8(Uint) ->
+ exit({badarg, uint8, [Uint]}).
+
+mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_pid({NodeNameExt, Creation}, Number, Serial);
+mk_pid({NodeNameExt, Creation}, Number, Serial) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?PID_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint32_be(Serial),
+ uint8(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_port({NodeNameExt, Creation}, Number);
+mk_port({NodeNameExt, Creation}, Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?PORT_EXT,
+ NodeNameExt,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
+ is_integer(Creation),
+ is_list(Numbers) ->
+ <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName),
+ mk_ref({NodeNameExt, Creation}, Numbers);
+mk_ref({NodeNameExt, Creation}, Numbers) when is_binary(NodeNameExt),
+ is_integer(Creation),
+ is_list(Numbers) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?NEW_REFERENCE_EXT,
+ uint16_be(length(Numbers)),
+ NodeNameExt,
+ uint8(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
make_sub_binary(Bin) when is_binary(Bin) ->
{_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index b949675f1c..dae7530ce7 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -291,7 +291,7 @@ filename(N) ->
exit(error).
error_read_file(Name) ->
- report_error("error reading file `~s'.", [Name]).
+ report_error("error reading file `~ts'.", [Name]).
report_error(S, Vs) ->
error_logger:error_msg(lists:concat([?MODULE, ": ", S, "\n"]), Vs).
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 35fd0c2e02..0c149634f6 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -151,7 +151,7 @@ dir_1(Dir, Regexp, Env) ->
lists:foreach(fun (X) -> dir_2(X, Regexp, Dir, Env) end,
Files);
{error, _} ->
- report_error("error reading directory `~s'",
+ report_error("error reading directory `~ts'",
[filename(Dir)]),
exit(error)
end.
@@ -180,7 +180,7 @@ dir_2(Name, Regexp, Dir, Env) ->
dir_3(Name, Dir, Regexp, Env) ->
Dir1 = filename:join(Dir, Name),
- verbose("tidying directory `~s'.", [Dir1], Env#dir.options),
+ verbose("tidying directory `~ts'.", [Dir1], Env#dir.options),
dir_1(Dir1, Regexp, Env).
dir_4(File, Regexp, Env) ->
@@ -189,7 +189,7 @@ dir_4(File, Regexp, Env) ->
Opts = [{outfile, File}, {dir, ""} | Env#dir.options],
case catch file(File, Opts) of
{'EXIT', Value} ->
- warn("error tidying `~s'.~n~p", [File,Value], Opts);
+ warn("error tidying `~ts'.~n~p", [File,Value], Opts);
_ ->
ok
end;
@@ -314,7 +314,7 @@ file_2(Name, Opts) ->
end.
read_module(Name, Opts) ->
- verbose("reading module `~s'.", [filename(Name)], Opts),
+ verbose("reading module `~ts'.", [filename(Name)], Opts),
case epp_dodger:parse_file(Name, [no_fail]) of
{ok, Forms} ->
check_forms(Forms, Name),
@@ -335,7 +335,7 @@ check_forms(Fs, Name) ->
"unknown error"
end,
report_error({Name, erl_syntax:get_pos(F),
- "\n ~s"}, [S]),
+ "\n ~ts"}, [S]),
exit(error);
_ ->
ok
@@ -357,18 +357,18 @@ write_module(Tree, Name, Opts) ->
{value, directory} ->
ok;
{value, _} ->
- report_error("`~s' is not a directory.",
+ report_error("`~ts' is not a directory.",
[filename(Dir)]),
exit(error);
none ->
case file:make_dir(Dir) of
ok ->
- verbose("created directory `~s'.",
+ verbose("created directory `~ts'.",
[filename(Dir)], Opts),
ok;
E ->
report_error("failed to create "
- "directory `~s'.",
+ "directory `~ts'.",
[filename(Dir)]),
exit({make_dir, E})
end
@@ -385,7 +385,7 @@ write_module(Tree, Name, Opts) ->
end,
Printer = proplists:get_value(printer, Opts),
FD = open_output_file(File, Encoding),
- verbose("writing to file `~s'.", [File], Opts),
+ verbose("writing to file `~ts'.", [File], Opts),
V = (catch {ok, output(FD, Printer, Tree, Opts++Encoding)}),
ok = file:close(FD),
case V of
@@ -471,7 +471,7 @@ backup_file_1(Name, Opts) ->
filename:basename(Name) ++ Suffix),
case catch file:rename(Name, Dest) of
ok ->
- verbose("made backup of file `~s'.", [Name], Opts);
+ verbose("made backup of file `~ts'.", [Name], Opts);
{error, R} ->
error_backup_file(Name),
exit({error, R});
@@ -1839,17 +1839,17 @@ report_export_vars(F, L, Type, Opts) ->
[Type], Opts).
error_read_file(Name) ->
- report_error("error reading file `~s'.", [filename(Name)]).
+ report_error("error reading file `~ts'.", [filename(Name)]).
error_write_file(Name) ->
- report_error("error writing to file `~s'.", [filename(Name)]).
+ report_error("error writing to file `~ts'.", [filename(Name)]).
error_backup_file(Name) ->
- report_error("could not create backup of file `~s'.",
+ report_error("could not create backup of file `~ts'.",
[filename(Name)]).
error_open_output(Name) ->
- report_error("cannot open file `~s' for output.", [filename(Name)]).
+ report_error("cannot open file `~ts' for output.", [filename(Name)]).
verbosity(Opts) ->
case proplists:get_bool(quiet, Opts) of
@@ -1908,9 +1908,9 @@ format({"", L, D}, Vs) when is_integer(L), L > 0 ->
format({"", _L, D}, Vs) ->
format(D, Vs);
format({F, L, D}, Vs) when is_integer(L), L > 0 ->
- [io_lib:fwrite("~s:~w: ", [filename(F), L]), format(D, Vs)];
+ [io_lib:fwrite("~ts:~w: ", [filename(F), L]), format(D, Vs)];
format({F, _L, D}, Vs) ->
- [io_lib:fwrite("~s: ", [filename(F)]), format(D, Vs)];
+ [io_lib:fwrite("~ts: ", [filename(F)]), format(D, Vs)];
format(S, Vs) when is_list(S) ->
[io_lib:fwrite(S, Vs), $\n].
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 567bec00cb..d385c2b690 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -2749,7 +2749,7 @@ read_module(Name, Options) ->
end.
read_module_1(Name, Options) ->
- verbose("reading module `~s'.", [filename(Name)], Options),
+ verbose("reading module `~ts'.", [filename(Name)], Options),
{Forms, Enc} = read_module_2(Name, Options),
case proplists:get_bool(comments, Options) of
false ->
@@ -2794,7 +2794,7 @@ check_forms([F | Fs], File) ->
_ ->
"unknown error"
end,
- report_error("in file `~s' at line ~w:\n ~ts",
+ report_error("in file `~ts' at line ~w:\n ~ts",
[filename(File), erl_syntax:get_pos(F), S]),
exit(error);
_ ->
@@ -2841,18 +2841,18 @@ write_module(Tree, Name, Dir, Opts) ->
{value, directory} ->
ok;
{value, _} ->
- report_error("`~s' is not a directory.",
+ report_error("`~ts' is not a directory.",
[Dir1]),
exit(error);
none ->
case file:make_dir(Dir1) of
ok ->
- verbose("created directory `~s'.",
+ verbose("created directory `~ts'.",
[Dir1], Opts),
ok;
E ->
report_error("failed to create "
- "directory `~s'.",
+ "directory `~ts'.",
[Dir1]),
exit({make_dir, E})
end
@@ -2870,7 +2870,7 @@ write_module(Tree, Name, Dir, Opts) ->
Printer = proplists:get_value(printer, Opts),
FD = open_output_file(File),
ok = output_encoding(FD, Opts),
- verbose("writing to file `~s'.", [File], Opts),
+ verbose("writing to file `~ts'.", [File], Opts),
V = (catch {ok, output(FD, Printer, Tree, Opts)}),
ok = file:close(FD),
case V of
@@ -2911,7 +2911,7 @@ backup_file_1(Name, Opts) ->
filename:basename(Name1) ++ Suffix),
case catch file:rename(Name1, Dest) of
ok ->
- verbose("made backup of file `~s'.", [Name1], Opts);
+ verbose("made backup of file `~ts'.", [Name1], Opts);
{error, R} ->
error_backup_file(Name1),
exit({error, R});
@@ -3036,19 +3036,19 @@ warning_apply_2(Module, Target) ->
"possibly unsafe in `~s'.", [Module, Target]).
error_open_output(Name) ->
- report_error("cannot open file `~s' for output.", [filename(Name)]).
+ report_error("cannot open file `~ts' for output.", [filename(Name)]).
error_read_file(Name) ->
- report_error("error reading file `~s'.", [filename(Name)]).
+ report_error("error reading file `~ts'.", [filename(Name)]).
error_read_file_info(Name) ->
- report_error("error getting file info: `~s'.", [filename(Name)]).
+ report_error("error getting file info: `~ts'.", [filename(Name)]).
error_write_file(Name) ->
- report_error("error writing to file `~s'.", [filename(Name)]).
+ report_error("error writing to file `~ts'.", [filename(Name)]).
error_backup_file(Name) ->
- report_error("could not create backup of file `~s'.",
+ report_error("could not create backup of file `~ts'.",
[filename(Name)]).
verbose(S, Opts) ->
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 70cb6fa220..5d4d392166 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -681,7 +681,7 @@ handle_call({abort_current_testcase,Reason}, _From, State) ->
handle_call({finish,Fini}, _From, State) ->
case State#state.jobs of
[] ->
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Fini) end,
State#state.idle_notify),
State2 = State#state{finish=false},
{stop,shutdown,{ok,self()}, State2};
@@ -699,14 +699,11 @@ handle_call({finish,Fini}, _From, State) ->
handle_call({idle_notify,Fun}, {Cli,_Ref}, State) ->
case State#state.jobs of
- [] ->
- Fun(Cli),
- {reply, {ok,self()}, State};
- _ ->
- Subscribed = State#state.idle_notify,
- {reply, {ok,self()},
- State#state{idle_notify=[{Cli,Fun}|Subscribed]}}
- end;
+ [] -> self() ! report_idle;
+ _ -> ok
+ end,
+ Subscribed = State#state.idle_notify,
+ {reply, {ok,self()}, State#state{idle_notify=[{Cli,Fun}|Subscribed]}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call(start_get_totals, From, State) -> {ok,Pid}
@@ -1000,6 +997,13 @@ handle_cast({node_started,Node}, State) ->
%% lost contact with target. The test_server_ctrl process is
%% terminated, and teminate/2 will do the cleanup
+handle_info(report_idle, State) ->
+ Finish = State#state.finish,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Finish) end,
+ State#state.idle_notify),
+ {noreply,State#state{idle_notify=[]}};
+
+
handle_info({'EXIT',Pid,Reason}, State) ->
case lists:keysearch(Pid,2,State#state.jobs) of
false ->
@@ -1017,11 +1021,12 @@ handle_info({'EXIT',Pid,Reason}, State) ->
[Name,Reason])
end,
State2 = State#state{jobs=NewJobs},
+ Finish = State2#state.finish,
case NewJobs of
[] ->
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Finish) end,
State2#state.idle_notify),
- case State2#state.finish of
+ case Finish of
false ->
{noreply,State2#state{idle_notify=[]}};
_ -> % true | abort
@@ -1031,9 +1036,9 @@ handle_info({'EXIT',Pid,Reason}, State) ->
{stop,shutdown,State2#state{finish=false}}
end;
_ -> % pending jobs
- case State2#state.finish of
+ case Finish of
abort -> % abort test now!
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli,Finish) end,
State2#state.idle_notify),
{stop,shutdown,State2#state{finish=false}};
_ -> % true | false
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index 741dd483f5..2be892d8d3 100644
--- a/lib/test_server/src/ts_run.erl
+++ b/lib/test_server/src/ts_run.erl
@@ -261,13 +261,17 @@ run_batch(Vars, _Spec, State) ->
ts_lib:progress(Vars, 1, "Command: ~s~n", [Command]),
io:format(user, "Command: ~s~n",[Command]),
Port = open_port({spawn, Command}, [stream, in, eof]),
- tricky_print_data(Port).
+ Timeout = 30000 * case os:getenv("TS_RUN_VALGRIND") of
+ false -> 1;
+ _ -> 100
+ end,
+ tricky_print_data(Port, Timeout).
-tricky_print_data(Port) ->
+tricky_print_data(Port, Timeout) ->
receive
{Port, {data, Bytes}} ->
io:put_chars(Bytes),
- tricky_print_data(Port);
+ tricky_print_data(Port, Timeout);
{Port, eof} ->
Port ! {self(), close},
receive
@@ -280,7 +284,7 @@ tricky_print_data(Port) ->
after 1 -> % force context switch
ok
end
- after 30000 ->
+ after Timeout ->
case erl_epmd:names() of
{ok,Names} ->
case is_testnode_dead(Names) of
@@ -288,10 +292,10 @@ tricky_print_data(Port) ->
io:put_chars("WARNING: No EOF, but "
"test_server node is down!\n");
false ->
- tricky_print_data(Port)
+ tricky_print_data(Port, Timeout)
end;
_ ->
- tricky_print_data(Port)
+ tricky_print_data(Port, Timeout)
end
end.