From 6e9f640c9cb4ac586378503f32919400083cae85 Mon Sep 17 00:00:00 2001 From: Sascha Kattelmann Date: Thu, 1 Feb 2018 10:43:38 +0100 Subject: [PATCH 01/70] Fix git version parsing --- src/ec_git_vsn.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ec_git_vsn.erl b/src/ec_git_vsn.erl index dff002c..569d78c 100644 --- a/src/ec_git_vsn.erl +++ b/src/ec_git_vsn.erl @@ -90,7 +90,7 @@ parse_tags({}) -> parse_tags(Pattern) -> Cmd = io_lib:format("git describe --abbrev=0 --tags --match \"~s*\"", [Pattern]), Tag = os:cmd(Cmd), - Vsn = slice(Tag, len(Pattern) + 1), + Vsn = slice(Tag, len(Pattern)), Vsn1 = trim(trim(Vsn, left, "v"), right, "\n"), {Tag, Vsn1}. @@ -103,5 +103,5 @@ slice(Str, Len) -> string:slice(Str, Len). -else. len(Str) -> string:len(Str). trim(Str, Dir, [Chars|_]) -> string:strip(Str, Dir, Chars). -slice(Str, Len) -> string:substr(Str, Len). +slice(Str, Len) -> string:substr(Str, Len + 1). -endif. From 51de0f2c7abc7f54945e9984cd9699b4ff8144ee Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 2 Feb 2018 20:27:02 -0800 Subject: [PATCH 02/70] version bump --- src/erlware_commons.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/erlware_commons.app.src b/src/erlware_commons.app.src index b219c60..e75d51c 100644 --- a/src/erlware_commons.app.src +++ b/src/erlware_commons.app.src @@ -1,6 +1,6 @@ {application,erlware_commons, [{description,"Additional standard library for Erlang"}, - {vsn,"1.0.4"}, + {vsn,"1.0.5"}, {modules,[]}, {registered,[]}, {applications,[kernel,stdlib,cf]}, From ad2d57d8b62b86ec1f3d03441f7defa595f2c59c Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Wed, 2 May 2018 21:47:52 -0400 Subject: [PATCH 03/70] Work around OTP-21 deprecation of get_stacktrace() --- rebar.config | 1 + src/ec_plists.erl | 45 +++++++++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/rebar.config b/rebar.config index 668108d..e705b66 100644 --- a/rebar.config +++ b/rebar.config @@ -15,6 +15,7 @@ {platform_define, "^1[8|9]", rand_module}, {platform_define, "^2", rand_module}, {platform_define, "^2", unicode_str}, + {platform_define, "^(R|1|20)", fun_stacktrace}, debug_info, warnings_as_errors]}. diff --git a/src/ec_plists.erl b/src/ec_plists.erl index e3c2175..50f122e 100644 --- a/src/ec_plists.erl +++ b/src/ec_plists.erl @@ -822,20 +822,7 @@ cluster_runmany(Fun, Fuse, [Task|TaskList], [N|Nodes], Running, Results) -> Parent ! {erlang:self(), fuse, FuseFunc(R1, R2)} end end, - Fun3 = fun () -> - try - Fun2() - catch - exit:siblingdied -> - ok; - exit:Reason -> - Parent ! {erlang:self(), error, Reason}; - error:R -> - Parent ! {erlang:self(), error, {R, erlang:get_stacktrace()}}; - throw:R -> - Parent ! {erlang:self(), error, {{nocatch, R}, erlang:get_stacktrace()}} - end - end, + Fun3 = fun() -> runmany_wrap(Fun2, Parent) end, Pid = proc_lib:spawn(N, Fun3), erlang:monitor(process, Pid), cluster_runmany(Fun, Fuse, TaskList, Nodes, [{Pid, N, Task}|Running], Results); @@ -885,6 +872,36 @@ cluster_runmany(_, _, [_Non|_Empty], []=_Nodes, []=_Running, _) -> %% We have data, but no nodes either available or occupied erlang:exit(allnodescrashed). +-ifdef(fun_stacktrace). +runmany_wrap(Fun, Parent) -> + try + Fun + catch + exit:siblingdied -> + ok; + exit:Reason -> + Parent ! {erlang:self(), error, Reason}; + error:R -> + Parent ! {erlang:self(), error, {R, erlang:get_stacktrace()}}; + throw:R -> + Parent ! {erlang:self(), error, {{nocatch, R}, erlang:get_stacktrace()}} + end. +-else. +runmany_wrap(Fun, Parent) -> + try + Fun + catch + exit:siblingdied -> + ok; + exit:Reason -> + Parent ! {erlang:self(), error, Reason}; + error:R:Stacktrace -> + Parent ! {erlang:self(), error, {R, Stacktrace}}; + throw:R:Stacktrace -> + Parent ! {erlang:self(), error, {{nocatch, R}, Stacktrace}} + end. +-endif. + delete_running(Pid, [{Pid, Node, List}|Running], Acc) -> {Running ++ Acc, Node, List}; delete_running(Pid, [R|Running], Acc) -> From 885f961c025f406645fa88c63f0a8f6c60a6eb17 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Wed, 2 May 2018 20:44:27 -0600 Subject: [PATCH 04/70] upgrade cf dep --- rebar.config | 2 +- rebar.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rebar.config b/rebar.config index e705b66..e269132 100644 --- a/rebar.config +++ b/rebar.config @@ -2,7 +2,7 @@ %% Dependencies ================================================================ {deps, [ - {cf, "0.2.2"} + {cf, "~>0.3"} ]}. {erl_first_files, ["ec_dictionary", "ec_vsn"]}. diff --git a/rebar.lock b/rebar.lock index 28a2fc2..f39d00d 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,6 @@ {"1.1.0", -[{<<"cf">>,{pkg,<<"cf">>,<<"0.2.2">>},0}]}. +[{<<"cf">>,{pkg,<<"cf">>,<<"0.3.1">>},0}]}. [ {pkg_hash,[ - {<<"cf">>, <<"7F2913FFF90ABCABD0F489896CFEB0B0674F6C8DF6C10B17A83175448029896C">>}]} + {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>}]} ]. From 9137b5dc54993874a2026397aad92573de334b59 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Wed, 2 May 2018 20:44:59 -0600 Subject: [PATCH 05/70] version bump --- src/erlware_commons.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/erlware_commons.app.src b/src/erlware_commons.app.src index e75d51c..70eea3f 100644 --- a/src/erlware_commons.app.src +++ b/src/erlware_commons.app.src @@ -1,6 +1,6 @@ {application,erlware_commons, [{description,"Additional standard library for Erlang"}, - {vsn,"1.0.5"}, + {vsn,"1.1.0"}, {modules,[]}, {registered,[]}, {applications,[kernel,stdlib,cf]}, From c730da3b9ddc3ed5e53d42644e9fbd5d6a6daca6 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 14 Jun 2018 08:18:33 +0200 Subject: [PATCH 06/70] Use environment variables to find the tmp directory --- src/ec_file.erl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index 7d89ace..9637529 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -277,9 +277,15 @@ remove_recursive(Path, Options) -> tmp() -> case erlang:system_info(system_architecture) of "win32" -> - "./tmp"; + case os:getenv("TEMP") of + false -> "./tmp"; + Val -> Val + end; _SysArch -> - "/tmp" + case os:getenv("TMPDIR") of + false -> "/tmp"; + Val -> Val + end end. %% Copy the subfiles of the From directory to the to directory. From 8302adf8310c0613642fed7fff533fd0cdf32347 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Fri, 22 Jun 2018 16:28:07 -0600 Subject: [PATCH 07/70] support more fine grained file info copy levels --- src/ec_file.erl | 83 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index 9637529..310d7fa 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -11,6 +11,7 @@ exists/1, copy/2, copy/3, + copy_file_info/3, insecure_mkdtemp/0, mkdir_path/1, mkdir_p/1, @@ -40,7 +41,8 @@ %%============================================================================ %% Types %%============================================================================ --type option() :: recursive. +-type file_info() :: mode | time | owner | group. +-type option() :: recursive | {file_info, [file_info()]}. %%%=================================================================== %%% API @@ -57,36 +59,79 @@ exists(Filename) -> %% @doc copy an entire directory to another location. -spec copy(file:name(), file:name(), Options::[option()]) -> ok | {error, Reason::term()}. copy(From, To, []) -> - copy(From, To); -copy(From, To, [recursive] = Options) -> - case is_dir(From) of - false -> - copy(From, To); + copy_(From, To, []); +copy(From, To, Options) -> + case proplists:get_value(recursive, Options, false) of true -> - make_dir_if_dir(To), - copy_subfiles(From, To, Options) + case is_dir(From) of + false -> + copy_(From, To, Options); + true -> + make_dir_if_dir(To), + copy_subfiles(From, To, Options) + end; + false -> + copy_(From, To, Options) end. %% @doc copy a file including timestamps,ownership and mode etc. -spec copy(From::file:filename(), To::file:filename()) -> ok | {error, Reason::term()}. copy(From, To) -> + copy_(From, To, [{file_info, [mode, time, owner, group]}]). + +copy_(From, To, Options) -> case file:copy(From, To) of {ok, _} -> - case file:read_file_info(From) of - {ok, FileInfo} -> - case file:write_file_info(To, FileInfo) of - ok -> - ok; - {error, WFError} -> - {error, {write_file_info_failed, WFError}} - end; - {error, RFError} -> - {error, {read_file_info_failed, RFError}} - end; + copy_file_info(To, From, proplists:get_value(file_info, Options, [])); {error, Error} -> {error, {copy_failed, Error}} end. +copy_file_info(To, From, FileInfoToKeep) -> + case file:read_file_info(From) of + {ok, FileInfo} -> + case write_file_info(To, FileInfo, FileInfoToKeep) of + [] -> + ok; + Errors -> + {error, {write_file_info_failed_for, Errors}} + end; + {error, RFError} -> + {error, {read_file_info_failed, RFError}} + end. + +write_file_info(To, FileInfo, FileInfoToKeep) -> + WriteInfoFuns = [{mode, fun try_write_mode/2}, + {time, fun try_write_time/2}, + {group, fun try_write_group/2}, + {owner, fun try_write_owner/2}], + lists:foldl(fun(Info, Acc) -> + case proplists:get_value(Info, WriteInfoFuns, undefined) of + undefined -> + Acc; + F -> + case F(To, FileInfo) of + ok -> + Acc; + {error, Reason} -> + [{Info, Reason} | Acc] + end + end + end, [], FileInfoToKeep). + + +try_write_mode(To, #file_info{mode=Mode}) -> + file:write_file_info(To, #file_info{mode=Mode}). + +try_write_time(To, #file_info{atime=Atime, mtime=Mtime}) -> + file:write_file_info(To, #file_info{atime=Atime, mtime=Mtime}). + +try_write_owner(To, #file_info{uid=OwnerId}) -> + file:write_file_info(To, #file_info{uid=OwnerId}). + +try_write_group(To, #file_info{gid=OwnerId}) -> + file:write_file_info(To, #file_info{gid=OwnerId}). + %% @doc return an md5 checksum string or a binary. Same as unix utility of %% same name. -spec md5sum(string() | binary()) -> string(). From 64d76963c726e380313024a1c2feb2117cbc0b8f Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sat, 23 Jun 2018 15:35:56 -0600 Subject: [PATCH 08/70] use git tag for version in .app --- src/erlware_commons.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/erlware_commons.app.src b/src/erlware_commons.app.src index 70eea3f..66f75bd 100644 --- a/src/erlware_commons.app.src +++ b/src/erlware_commons.app.src @@ -1,6 +1,6 @@ {application,erlware_commons, [{description,"Additional standard library for Erlang"}, - {vsn,"1.1.0"}, + {vsn,"git"}, {modules,[]}, {registered,[]}, {applications,[kernel,stdlib,cf]}, From f6992d72cae65ecfb3e8ae488e899f9f94a60da4 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Mon, 27 Aug 2018 14:59:32 -0600 Subject: [PATCH 09/70] fix insecure_mkdtemp type spec --- src/ec_file.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index 310d7fa..c623573 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -217,7 +217,7 @@ real_dir_path(Path) -> %% @doc make a unique temporary directory. Similar function to BSD stdlib %% function of the same name. --spec insecure_mkdtemp() -> TmpDirPath::file:name(). +-spec insecure_mkdtemp() -> TmpDirPath::file:name() | {error, term()}. insecure_mkdtemp() -> UniqueNumber = erlang:integer_to_list(erlang:trunc(random_uniform() * 1000000000000)), TmpDirPath = From d4079cd12787e4a10095fe7547ef77bf4a3ec652 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Sat, 13 Oct 2018 08:39:12 -0600 Subject: [PATCH 10/70] fix ec_semver:pes test to not ignore alpha versions --- src/ec_semver.erl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/ec_semver.erl b/src/ec_semver.erl index 0298872..2982dfc 100644 --- a/src/ec_semver.erl +++ b/src/ec_semver.erl @@ -287,24 +287,24 @@ normalize(Other = {{_, _, _, _}, {_,_}}) -> %% the internal implementation of the of the pessimistic run. The %% external just ensures that versions are parsed. -spec internal_pes(semver(), semver()) -> boolean(). -internal_pes(VsnA, {{LM, LMI}, _}) +internal_pes(VsnA, {{LM, LMI}, Alpha}) when erlang:is_integer(LM), erlang:is_integer(LMI) -> - gte(VsnA, {{LM, LMI, 0}, {[], []}}) andalso + gte(VsnA, {{LM, LMI, 0}, Alpha}) andalso lt(VsnA, {{LM + 1, 0, 0, 0}, {[], []}}); -internal_pes(VsnA, {{LM, LMI, LP}, _}) +internal_pes(VsnA, {{LM, LMI, LP}, Alpha}) when erlang:is_integer(LM), erlang:is_integer(LMI), erlang:is_integer(LP) -> - gte(VsnA, {{LM, LMI, LP}, {[], []}}) + gte(VsnA, {{LM, LMI, LP}, Alpha}) andalso lt(VsnA, {{LM, LMI + 1, 0, 0}, {[], []}}); -internal_pes(VsnA, {{LM, LMI, LP, LMP}, _}) +internal_pes(VsnA, {{LM, LMI, LP, LMP}, Alpha}) when erlang:is_integer(LM), erlang:is_integer(LMI), erlang:is_integer(LP), erlang:is_integer(LMP) -> - gte(VsnA, {{LM, LMI, LP, LMP}, {[], []}}) + gte(VsnA, {{LM, LMI, LP, LMP}, Alpha}) andalso lt(VsnA, {{LM, LMI, LP + 1, 0}, {[], []}}); internal_pes(Vsn, LVsn) -> @@ -671,6 +671,10 @@ between_test() -> ?assertMatch(true, not between("aaa", "ddd", "zzz")). pes_test() -> + ?assertMatch(true, pes("1.0.0-rc.0", "1.0.0-rc.0")), + ?assertMatch(true, pes("1.0.0-rc.1", "1.0.0-rc.0")), + ?assertMatch(true, pes("1.0.0", "1.0.0-rc.0")), + ?assertMatch(false, pes("1.0.0-rc.0", "1.0.0-rc.1")), ?assertMatch(true, pes("2.6.0", "2.6")), ?assertMatch(true, pes("2.7", "2.6")), ?assertMatch(true, pes("2.8", "2.6")), From 4e3b177be7700c8afe38663eb88d953dd3c175cf Mon Sep 17 00:00:00 2001 From: Kuba Odias Date: Fri, 14 Dec 2018 14:21:34 +0100 Subject: [PATCH 11/70] Allow for extra hyphens in pre-release build version --- src/ec_semver.erl | 48 ++++++++++++++++++++++++++++++++++++++++ src/ec_semver_parser.erl | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/ec_semver.erl b/src/ec_semver.erl index 2982dfc..8141065 100644 --- a/src/ec_semver.erl +++ b/src/ec_semver.erl @@ -340,6 +340,8 @@ eql_test() -> "1.0.0.0-alpha.1+build.1")), ?assertMatch(true, eql("1.0-alpha.1+build.1", "v1.0.0.0-alpha.1+build.1")), + ?assertMatch(true, eql("1.0-pre-alpha.1", + "1.0.0-pre-alpha.1")), ?assertMatch(true, eql("aa", "aa")), ?assertMatch(true, eql("AA.BB", "AA.BB")), ?assertMatch(true, eql("BBB-super", "BBB-super")), @@ -367,6 +369,8 @@ gt_test() -> "1.0.0-alpha.1")), ?assertMatch(true, gt("1.0.0-beta.11", "1.0.0-beta.2")), + ?assertMatch(true, gt("1.0.0-pre-alpha.14", + "1.0.0-pre-alpha.3")), ?assertMatch(true, gt("1.0.0-beta.11", "1.0.0.0-beta.2")), ?assertMatch(true, gt("1.0.0-rc.1", "1.0.0-beta.11")), @@ -392,6 +396,8 @@ gt_test() -> "1.0.0-beta.11")), ?assertMatch(true, not gt("1.0.0-beta.11", "1.0.0-rc.1")), + ?assertMatch(true, not gt("1.0.0-pre-alpha.3", + "1.0.0-pre-alpha.14")), ?assertMatch(true, not gt("1.0.0-rc.1", "1.0.0-rc.1+build.1")), ?assertMatch(true, not gt("1.0.0-rc.1+build.1", @@ -430,6 +436,8 @@ lt_test() -> "1.0.0-beta.2")), ?assertMatch(true, lt("1.0.0-beta.2", "1.0.0-beta.11")), + ?assertMatch(true, lt("1.0.0-pre-alpha.3", + "1.0.0-pre-alpha.14")), ?assertMatch(true, lt("1.0.0-beta.11", "1.0.0-rc.1")), ?assertMatch(true, lt("1.0.0.1-beta.11", @@ -469,6 +477,8 @@ lt_test() -> "1.0.0-alpha.1")), ?assertMatch(true, not lt("1.0.0-beta.11", "1.0.0-beta.2")), + ?assertMatch(true, not lt("1.0.0-pre-alpha.14", + "1.0.0-pre-alpha.3")), ?assertMatch(true, not lt("1.0.0-rc.1", "1.0.0-beta.11")), ?assertMatch(true, not lt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), ?assertMatch(true, not lt("1.0.0", "1.0.0-rc.1+build.1")), @@ -505,6 +515,8 @@ gte_test() -> "1.0.0.0-alpha.1+build.1")), ?assertMatch(true, gte("1.0.0-alpha.1", "1.0.0-alpha")), + ?assertMatch(true, gte("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha")), ?assertMatch(true, gte("1.0.0-beta.2", "1.0.0-alpha.1")), ?assertMatch(true, gte("1.0.0-beta.11", @@ -522,6 +534,8 @@ gte_test() -> "1.3.7+build.2.b8f12d7")), ?assertMatch(true, not gte("1.0.0-alpha", "1.0.0-alpha.1")), + ?assertMatch(true, not gte("1.0.0-pre-alpha", + "1.0.0-pre-alpha.1")), ?assertMatch(true, not gte("CC", "DD")), ?assertMatch(true, not gte("1.0.0-alpha.1", "1.0.0-beta.2")), @@ -550,6 +564,8 @@ lte_test() -> "1.0.0-beta.2")), ?assertMatch(true, lte("1.0.0-beta.2", "1.0.0-beta.11")), + ?assertMatch(true, lte("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha.11")), ?assertMatch(true, lte("1.0.0-beta.11", "1.0.0-rc.1")), ?assertMatch(true, lte("1.0.0-rc.1", @@ -582,6 +598,8 @@ lte_test() -> ?assertMatch(true, lte("cc","cc")), ?assertMatch(true, not lte("1.0.0-alpha.1", "1.0.0-alpha")), + ?assertMatch(true, not lte("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha")), ?assertMatch(true, not lte("cc", "aa")), ?assertMatch(true, not lte("1.0.0-beta.2", "1.0.0-alpha.1")), @@ -607,6 +625,9 @@ between_test() -> ?assertMatch(true, between("1.0.0-beta.2", "1.0.0-beta.11", "1.0.0-beta.7")), + ?assertMatch(true, between("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha.11", + "1.0.0-pre-alpha.7")), ?assertMatch(true, between("1.0.0-beta.11", "1.0.0-rc.3", "1.0.0-rc.1")), @@ -660,6 +681,9 @@ between_test() -> ?assertMatch(true, not between("1.0.0-alpha.1", "1.0.0-alpha.22", "1.0.0")), + ?assertMatch(true, not between("1.0.0-pre-alpha.1", + "1.0.0-pre-alpha.22", + "1.0.0")), ?assertMatch(true, not between("1.0.0", "1.0.0-alpha.1", "2.0")), @@ -693,6 +717,27 @@ pes_test() -> ?assertMatch(true, not pes("A.A", "A.B")), ?assertMatch(true, not pes("2.5", "2.6.5")). +parse_test() -> + ?assertEqual({1, {[],[]}}, parse(<<"1">>)), + ?assertEqual({{1,2,34},{[],[]}}, parse(<<"1.2.34">>)), + ?assertEqual({<<"a">>, {[],[]}}, parse(<<"a">>)), + ?assertEqual({{<<"a">>,<<"b">>}, {[],[]}}, parse(<<"a.b">>)), + ?assertEqual({1, {[],[]}}, parse(<<"1">>)), + ?assertEqual({{1,2}, {[],[]}}, parse(<<"1.2">>)), + ?assertEqual({{1,2,2}, {[],[]}}, parse(<<"1.2.2">>)), + ?assertEqual({{1,99,2}, {[],[]}}, parse(<<"1.99.2">>)), + ?assertEqual({{1,99,2}, {[<<"alpha">>],[]}}, parse(<<"1.99.2-alpha">>)), + ?assertEqual({{1,99,2}, {[<<"alpha">>,1], []}}, parse(<<"1.99.2-alpha.1">>)), + ?assertEqual({{1,99,2}, {[<<"pre-alpha">>,1], []}}, parse(<<"1.99.2-pre-alpha.1">>)), + ?assertEqual({{1,99,2}, {[], [<<"build">>, 1, <<"a36">>]}}, + parse(<<"1.99.2+build.1.a36">>)), + ?assertEqual({{1,99,2,44}, {[], [<<"build">>, 1, <<"a36">>]}}, + parse(<<"1.99.2.44+build.1.a36">>)), + ?assertEqual({{1,99,2}, {[<<"alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}, + parse("1.99.2-alpha.1+build.1.a36")), + ?assertEqual({{1,99,2}, {[<<"pre-alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}, + parse("1.99.2-pre-alpha.1+build.1.a36")). + version_format_test() -> ?assertEqual(["1", [], []], format({1, {[],[]}})), ?assertEqual(["1", ".", "2", ".", "34", [], []], format({{1,2,34},{[],[]}})), @@ -704,12 +749,15 @@ version_format_test() -> ?assertEqual(<<"1.99.2">>, erlang:iolist_to_binary(format({{1,99,2}, {[],[]}}))), ?assertEqual(<<"1.99.2-alpha">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"alpha">>],[]}}))), ?assertEqual(<<"1.99.2-alpha.1">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"alpha">>,1], []}}))), + ?assertEqual(<<"1.99.2-pre-alpha.1">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"pre-alpha">>,1], []}}))), ?assertEqual(<<"1.99.2+build.1.a36">>, erlang:iolist_to_binary(format({{1,99,2}, {[], [<<"build">>, 1, <<"a36">>]}}))), ?assertEqual(<<"1.99.2.44+build.1.a36">>, erlang:iolist_to_binary(format({{1,99,2,44}, {[], [<<"build">>, 1, <<"a36">>]}}))), ?assertEqual(<<"1.99.2-alpha.1+build.1.a36">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}))), + ?assertEqual(<<"1.99.2-pre-alpha.1+build.1.a36">>, + erlang:iolist_to_binary(format({{1,99,2}, {[<<"pre-alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}))), ?assertEqual(<<"1">>, erlang:iolist_to_binary(format({1, {[],[]}}))). -endif. diff --git a/src/ec_semver_parser.erl b/src/ec_semver_parser.erl index 556f984..57b7264 100644 --- a/src/ec_semver_parser.erl +++ b/src/ec_semver_parser.erl @@ -44,7 +44,7 @@ parse(Input) when is_binary(Input) -> -spec 'alpha_part'(input(), index()) -> parse_result(). 'alpha_part'(Input, Index) -> - p(Input, Index, 'alpha_part', fun(I,D) -> (p_one_or_more(p_charclass(<<"[A-Za-z0-9]">>)))(I,D) end, fun(Node, _Idx) ->erlang:iolist_to_binary(Node) end). + p(Input, Index, 'alpha_part', fun(I,D) -> (p_one_or_more(p_charclass(<<"[A-Za-z0-9-]">>)))(I,D) end, fun(Node, _Idx) ->erlang:iolist_to_binary(Node) end). transform(_,Node,_Index) -> Node. From 8aadd8b27829d1fe015e79c66adda7ae3b4f7f25 Mon Sep 17 00:00:00 2001 From: Martin Rehfeld Date: Thu, 21 Feb 2019 17:11:30 +0100 Subject: [PATCH 12/70] Fix spelling/grammar in signatures.md While reading through the document, I came across some spelling/grammar issues and thought I could just as well help fix them. --- doc/signatures.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/doc/signatures.md b/doc/signatures.md index cf95960..18aab3c 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -16,7 +16,7 @@ implement a dictionary. Just a few are: * [Skip Lists](http://en.wikipedia.org/wiki/Skip_list) * Many, many more .... -Each of these approaches has there own performance characteristics, +Each of these approaches has their own performance characteristics, memory footprints etc. For example, a table of size n with open addressing has no collisions and holds up to n elements, with a single comparison for successful lookup, and a table of size n with chaining @@ -67,9 +67,9 @@ someone could inadvertantly pass an unexpected function name as `ModuleToUse` and you would never get any warnings, just an exception at run time. -Fortunately, Erlang is a pretty flexable language so we can use a +Fortunately, Erlang is a pretty flexible language so we can use a similar approach with a few adjustments to give us the best of both -worlds. Both the flexibiltiy of ignoreing a specific implementation +worlds. Both the flexibility of ignoring a specific implementation and keeping all the nice locality we get by using an explicit module name. @@ -90,7 +90,7 @@ reasons, this is a much better approach to the problem. This is what Signatures ---------- -How do we actually do this in Erlang now that Erlang is missing what Java, SML and friends has built in? +How do we actually do this in Erlang now that Erlang is missing what Java, SML and friends have built in? The first thing we need to do is to define a [Behaviour](http://metajack.im/2008/10/29/custom-behaviors-in-erlang/) @@ -119,7 +119,7 @@ Behaviour for dictionaries. That Behaviour looks like this: So we have our Behaviour now. Unfortunately, this doesn't give us much yet. It will make sure that any dictionaries we write will have all -the functions they need to have, but it wont help use actually use the +the functions they need to have, but it won't help us actually use the dictionaries in an abstract way in our code. To do that we need to add a bit of functionality. We do that by actually implementing our own behaviour, starting with `new/1`. @@ -137,7 +137,7 @@ This code creates a new dictionary for us. Or to be more specific it actually creates a new dictionary Signature record, that will be used subsequently in other calls. This might look a bit familiar from our previous less optimal approach. We have both the module name and the -data. here in the record. We call the module name named in +data in the record. We call the module name named in `ModuleName` to create the initial data. We then construct the record and return that record to the caller and we have a new dictionary. What about the other functions, the ones that don't create @@ -197,7 +197,7 @@ implementation in Using Signatures ---------------- -Its a good idea to work through an example so we have a bit better +It's a good idea to work through an example so we have a bit better idea of how to use these Signatures. If you are like me, you probably have some questions about what kind of performance burden this places on the code. At the very least we have an additional function call @@ -206,7 +206,7 @@ lets write a little timing test, so we can get a good idea of how much this is all costing us. In general, there are two kinds of concrete implementations for -Signatures. The first is a native implementations, the second is a +Signatures. The first is a native implementation, the second is a wrapper. ### Native Signature Implementations @@ -223,16 +223,16 @@ implements the ec_dictionary module directly. A Signature Wrapper is a module that wraps another module. Its purpose is to help a preexisting module implement the Behaviour -defined by a Signature. A good example if this in our current example +defined by a Signature. A good example of this in our current example is the [erlware_commons/ec_dict](https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_dict.erl) module. It implements the ec_dictionary Behaviour, but all the functionality is provided by the [stdlib/dict](http://www.erlang.org/doc/man/dict.html) module -itself. Lets take a look at one example to see how this is done. +itself. Let's take a look at one example to see how this is done. We will take a look at one of the functions we have already seen. The -`get` function an ec_dictionary `get` doesn't have quite the same +`get` function in ec_dictionary doesn't have quite the same semantics as any of the functions in the dict module. So a bit of translation needs to be done. We do that in the ec_dict module `get` function. @@ -258,11 +258,11 @@ the mix and that adds a bit of additional overhead. ### Creating the Timing Module -We are going to creating timings for both Native Signature +We are going to be creating timings for both Native Signature Implementations and Signature Wrappers. -Lets get started by looking at some helper functions. We want -dictionaries to have a bit of data in them. So to that end we are will +Let's get started by looking at some helper functions. We want +dictionaries to have a bit of data in them. So to that end we will create a couple of functions that create dictionaries for each type we want to test. The first we want to time is the Signature Wrapper, so `dict` vs `ec_dict` called as a Signature. @@ -299,7 +299,7 @@ Signatures. Otherwise, this does the exact same thing as the dict We are going to use two function calls in our timing. One that updates data and one that returns data, just to get good coverage. For our -dictionaries that we are going to use the `size` function as well as +dictionaries we are going to use the `size` function as well as the `add` function. :::erlang @@ -315,8 +315,8 @@ the `add` function. The `test_avg` function runs the provided function the number of times specified in the second argument and collects timing information. We -are going to run these one million times to get a good average (its -fast so it doesn't take long). You can see that in the anonymous +are going to run these one million times to get a good average (it's +fast so it doesn't take long). You can see in the anonymous function that we directly call `dict:size/1` and `dict:store/3` to perform the test. However, because we are in the wonderful world of Signatures we don't have to hard code the calls for the Signature @@ -426,14 +426,14 @@ results. So no difference it time. Well the reality is that there is a difference in timing, there must be, but we don't have enough resolution in the timing system to be able to figure out what that -difference is. Essentially that means its really, really small - or small +difference is. Essentially that means it's really, really small - or small enough not to worry about at the very least. Conclusion ---------- Signatures are a viable, useful approach to the problem of interfaces -in Erlang. The have little or no over head depending on the type of +in Erlang. They have little or no overhead depending on the type of implementation, and greatly increase the flexibility of the a library while retaining testability and locality. @@ -456,7 +456,7 @@ Signature Wrapper ### Code Referenced -* [ec_dictionary Implementation] (https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_dictionary.erl) -* [ec_dict Signature Wrapper] (https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_dict.erl) -* [ec_rbdict Native Signature Implementation] (https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_rbdict.erl) -* [ec_timing Signature Use Example and Timing Collector] (https://github.com/ericbmerritt/erlware_commons/blob/types/examples/ec_timing.erl) +* [ec_dictionary Implementation](https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_dictionary.erl) +* [ec_dict Signature Wrapper](https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_dict.erl) +* [ec_rbdict Native Signature Implementation](https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_rbdict.erl) +* [ec_timing Signature Use Example and Timing Collector](https://github.com/ericbmerritt/erlware_commons/blob/types/examples/ec_timing.erl) From 0dc260c04c0d5acdb668ea41296d4c65f94c230f Mon Sep 17 00:00:00 2001 From: Roger Lipscombe Date: Mon, 19 Aug 2019 22:49:35 +0100 Subject: [PATCH 13/70] Replace ericbmerritt links in README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d4e53eb..4367eaf 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ runmany, which is as a generalization of parallel list operations. A complete parser for the [semver](http://semver.org/) standard. Including a complete set of conforming comparison functions. -### [ec_lists](https://github.com/ericbmerritt/erlware_commons/blob/master/src/ec_lists.erl) +### [ec_lists](https://github.com/erlware/erlware_commons/blob/master/src/ec_lists.erl) A set of additional list manipulation functions designed to supliment the `lists` module in stdlib. @@ -110,19 +110,19 @@ This provides an implementation of the ec_dictionary signature using erlang's dicts as a base. The function documentation for ec_dictionary applies here as well. -### [ec_gb_trees](https://github.com/ericbmerritt/erlware_commons/blob/master/src/ec_gb_trees.erl) +### [ec_gb_trees](https://github.com/erlware/erlware_commons/blob/master/src/ec_gb_trees.erl) This provides an implementation of the ec_dictionary signature using erlang's gb_trees as a base. The function documentation for ec_dictionary applies here as well. -### [ec_orddict](https://github.com/ericbmerritt/erlware_commons/blob/master/src/ec_orddict.erl) +### [ec_orddict](https://github.com/erlware/erlware_commons/blob/master/src/ec_orddict.erl) This provides an implementation of the ec_dictionary signature using erlang's orddict as a base. The function documentation for ec_dictionary applies here as well. -### [ec_rbdict](https://github.com/ericbmerritt/erlware_commons/blob/master/src/ec_rbdict.erl) +### [ec_rbdict](https://github.com/erlware/erlware_commons/blob/master/src/ec_rbdict.erl) This provides an implementation of the ec_dictionary signature using Robert Virding's rbdict module as a base. The function documentation From c2b7863a53549029d8841da5c35fd3bdbf767417 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 15 Dec 2020 01:45:43 +0300 Subject: [PATCH 14/70] Avoid a warning on Erlang/OTP 24 to make sure Rebar 3 can bootstrap on that version with warnings-as-errors compiler settings. Closes #145. --- src/ec_cmd_log.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index b61bbd0..4151216 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -22,6 +22,9 @@ %%% use this to talk to the users if you are wrting code for the system -module(ec_cmd_log). +%% Avoids a warning on Erlang/OTP 24 +-compile({no_auto_import,[error/3]}). + -export([new/1, new/2, new/3, From 8eef97234fc69ec59b1c086ce13cab6564fa7c8e Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Wed, 16 Dec 2020 00:10:38 +0300 Subject: [PATCH 15/70] Better comment wording as suggested by @ferd Co-authored-by: Fred Hebert --- src/ec_cmd_log.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index 4151216..c601782 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -22,7 +22,7 @@ %%% use this to talk to the users if you are wrting code for the system -module(ec_cmd_log). -%% Avoids a warning on Erlang/OTP 24 +%% Avoid clashing with `error/3` BIF added in Erlang/OTP 24 -compile({no_auto_import,[error/3]}). -export([new/1, From 2e1b59ece6b1fb61390c3e3ed17d2dcb51acc6ad Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Mon, 4 Jan 2021 09:43:52 -0700 Subject: [PATCH 16/70] move to github actions for CI --- .github/workflows/main.yml | 31 +++++++++++++++++++++++++++++++ .gitignore | 1 + .travis.yml | 22 ---------------------- rebar.lock | 6 ++++-- 4 files changed, 36 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..0831922 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,31 @@ +name: Common Test + +on: + pull_request: + branches: + - 'main' + push: + branches: + - 'main' + +jobs: + build: + name: Test on OTP ${{ matrix.otp_version }} and ${{ matrix.os }} + runs-on: ${{ matrix.os }} + + strategy: + matrix: + otp_version: ['23.2.1', '22.3.4.2', '21.3.8.16', '20.3.8.21', '19.3.6.13'] + os: [ubuntu-latest] + + steps: + - uses: actions/checkout@v2 + + - uses: bajankristof/setup-erlang@master + with: + otp-version: ${{ matrix.otp_version }} + + - name: Compile + run: rebar3 compile + - name: EUnit tests + run: rebar3 eunit diff --git a/.gitignore b/.gitignore index 929000d..e8f4b5a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ doc/edoc-info doc/erlang.png ebin/* .* +!.github _build erl_crash.dump *.pyc diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 1de9b24..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: erlang -otp_release: - - 18.0 - - 17.0 - - R16B03-1 - - R16B03 - - R16B02 - - R16B01 - - R16B -script: "./rebar3 update && ./rebar3 compile && ./rebar3 eunit" -branches: - only: - - master -notifications: - email: - - core@erlware.org - irc: - channels: - - "irc.freenode.org#erlware" - use_notice: true - skip_join: true -sudo: false diff --git a/rebar.lock b/rebar.lock index f39d00d..7873d25 100644 --- a/rebar.lock +++ b/rebar.lock @@ -1,6 +1,8 @@ -{"1.1.0", +{"1.2.0", [{<<"cf">>,{pkg,<<"cf">>,<<"0.3.1">>},0}]}. [ {pkg_hash,[ - {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>}]} + {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>}]}, +{pkg_hash_ext,[ + {<<"cf">>, <<"315E8D447D3A4B02BCDBFA397AD03BBB988A6E0AA6F44D3ADD0F4E3C3BF97672">>}]} ]. From 2256b68e4fda6c9c9aebd8034ce0170bf2d4ceb1 Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Mon, 4 Jan 2021 10:18:05 -0700 Subject: [PATCH 17/70] update readme status badge and CI branch --- .github/workflows/main.yml | 6 +++--- README.md | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0831922..f3ae8be 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,12 @@ -name: Common Test +name: EUnit on: pull_request: branches: - - 'main' + - 'master' push: branches: - - 'main' + - 'master' jobs: build: diff --git a/README.md b/README.md index 4367eaf..2230425 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ Erlware Commons Current Status -------------- -[![Build Status](https://secure.travis-ci.org/erlware/erlware_commons.png)](http://travis-ci.org/erlware/erlware_commons) + +![Tests](https://github.com/erlware/erlware_commons/workflows/EUnit/badge.svg) Introduction ------------ From 32e62781bbbd33934800c63ae0686cd41fd7da8f Mon Sep 17 00:00:00 2001 From: Tristan Sloughter Date: Mon, 4 Jan 2021 11:52:55 -0700 Subject: [PATCH 18/70] set TERM os var to xterm to get cf to not drop color in logs --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f3ae8be..f76e374 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,4 +28,4 @@ jobs: - name: Compile run: rebar3 compile - name: EUnit tests - run: rebar3 eunit + run: TERM=xterm rebar3 eunit From 01e08a3605731f2badd6a4151676ec1510a5f81a Mon Sep 17 00:00:00 2001 From: Enid Gjoleka Date: Wed, 10 Feb 2021 14:43:53 +0100 Subject: [PATCH 19/70] Remove unused -file attribute on ec_semver_parser --- src/ec_semver_parser.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ec_semver_parser.erl b/src/ec_semver_parser.erl index 57b7264..c2fe186 100644 --- a/src/ec_semver_parser.erl +++ b/src/ec_semver_parser.erl @@ -48,7 +48,6 @@ parse(Input) when is_binary(Input) -> transform(_,Node,_Index) -> Node. --file("peg_includes.hrl", 1). -type index() :: {{line, pos_integer()}, {column, pos_integer()}}. -type input() :: binary(). -type parse_failure() :: {fail, term()}. From 0a4fde35e7d802ed48118ccab1ae6570f66f2cd6 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Thu, 25 Mar 2021 00:02:53 +0000 Subject: [PATCH 20/70] Remove dead code --- src/ec_git_vsn.erl | 3 +-- src/ec_talk.erl | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ec_git_vsn.erl b/src/ec_git_vsn.erl index 569d78c..12bbb4e 100644 --- a/src/ec_git_vsn.erl +++ b/src/ec_git_vsn.erl @@ -97,8 +97,7 @@ parse_tags(Pattern) -> -ifdef(unicode_str). len(Str) -> string:length(Str). trim(Str, right, Chars) -> string:trim(Str, trailing, Chars); -trim(Str, left, Chars) -> string:trim(Str, leading, Chars); -trim(Str, both, Chars) -> string:trim(Str, both, Chars). +trim(Str, left, Chars) -> string:trim(Str, leading, Chars). slice(Str, Len) -> string:slice(Str, Len). -else. len(Str) -> string:len(Str). diff --git a/src/ec_talk.erl b/src/ec_talk.erl index 36cff8e..454b1f8 100644 --- a/src/ec_talk.erl +++ b/src/ec_talk.erl @@ -199,8 +199,6 @@ get_string(String) -> -ifdef(unicode_str). trim(Str) -> string:trim(Str). -trim(Str, right, Chars) -> string:trim(Str, trailing, Chars); -trim(Str, left, Chars) -> string:trim(Str, leading, Chars); trim(Str, both, Chars) -> string:trim(Str, both, Chars). -else. trim(Str) -> string:strip(Str). From 4406953a8715a8042e410bd0b328ab6ca620d0ff Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Thu, 25 Mar 2021 00:03:13 +0000 Subject: [PATCH 21/70] Have CI working with extra checks --- .github/workflows/main.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f76e374..49cfa50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,20 +12,20 @@ jobs: build: name: Test on OTP ${{ matrix.otp_version }} and ${{ matrix.os }} runs-on: ${{ matrix.os }} + container: + image: erlang:${{matrix.otp_version}} strategy: matrix: - otp_version: ['23.2.1', '22.3.4.2', '21.3.8.16', '20.3.8.21', '19.3.6.13'] + otp_version: ['23.2', '22.3', '21.3', '20.3', '19.3'] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 - - uses: bajankristof/setup-erlang@master - with: - otp-version: ${{ matrix.otp_version }} - - name: Compile run: rebar3 compile + - name: Dialyze + run: rebar3 as test dialyzer - name: EUnit tests run: TERM=xterm rebar3 eunit From f5e8aa655151bb004c48e59bb910a180a19b9a9f Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Thu, 25 Mar 2021 00:03:37 +0000 Subject: [PATCH 22/70] gitignore test-generated elements --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e8f4b5a..d306af0 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,7 @@ _build erl_crash.dump *.pyc *~ +TEST-*.xml +/foo src/ec_semver_parser.peg From 9d67e2651050cd1eb725aa16c22939136d4171a4 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Thu, 25 Mar 2021 00:04:10 +0000 Subject: [PATCH 23/70] Solve dialyzer warnings while approaching expected behaviour (with minor unit tests) --- src/ec_git_vsn.erl | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/ec_git_vsn.erl b/src/ec_git_vsn.erl index 12bbb4e..c932c58 100644 --- a/src/ec_git_vsn.erl +++ b/src/ec_git_vsn.erl @@ -34,7 +34,7 @@ new() -> {}. --spec vsn(t()) -> {ok, string()} | {error, Reason::any()}. +-spec vsn(t()|string()) -> {ok, string()} | {error, Reason::any()}. vsn(Data) -> {Vsn, RawRef, RawCount} = collect_default_refcount(Data), {ok, build_vsn_string(Vsn, RawRef, RawCount)}. @@ -61,12 +61,7 @@ collect_default_refcount(Data) -> build_vsn_string(Vsn, RawRef, RawCount) -> %% Cleanup the tag and the Ref information. Basically leading 'v's and %% whitespace needs to go away. - RefTag = case RawRef of - undefined -> - ""; - RawRef -> - [".ref", re:replace(RawRef, "\\s", "", [global])] - end, + RefTag = [".ref", re:replace(RawRef, "\\s", "", [global])], Count = erlang:iolist_to_binary(re:replace(RawCount, "\\s", "", [global])), %% Create the valid [semver](http://semver.org) version from the tag @@ -82,17 +77,27 @@ get_patch_count(RawRef) -> Ref = re:replace(RawRef, "\\s", "", [global]), Cmd = io_lib:format("git rev-list --count ~s..HEAD", [Ref]), - os:cmd(Cmd). + case os:cmd(Cmd) of + "fatal: " ++ _ -> + 0; + Count -> + Count + end. --spec parse_tags(t()) -> {string()|undefined, ec_semver:version_string()}. +-spec parse_tags(t()|string()) -> {string()|undefined, ec_semver:version_string()}. parse_tags({}) -> parse_tags(""); parse_tags(Pattern) -> Cmd = io_lib:format("git describe --abbrev=0 --tags --match \"~s*\"", [Pattern]), Tag = os:cmd(Cmd), - Vsn = slice(Tag, len(Pattern)), - Vsn1 = trim(trim(Vsn, left, "v"), right, "\n"), - {Tag, Vsn1}. + case Tag of + "fatal: " ++ _ -> + {undefined, ""}; + _ -> + Vsn = slice(Tag, len(Pattern)), + Vsn1 = trim(trim(Vsn, left, "v"), right, "\n"), + {Tag, Vsn1} + end. -ifdef(unicode_str). len(Str) -> string:length(Str). @@ -104,3 +109,16 @@ len(Str) -> string:len(Str). trim(Str, Dir, [Chars|_]) -> string:strip(Str, Dir, Chars). slice(Str, Len) -> string:substr(Str, Len + 1). -endif. + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +parse_tags_test() -> + ?assertEqual({undefined, ""}, parse_tags("a.b.c")). + +get_patch_count_test() -> + ?assertEqual(0, get_patch_count("a.b.c")). + +collect_default_refcount_test() -> + ?assertMatch({"", _, _}, collect_default_refcount("a.b.c")). +-endif. From f0347d88d769dd1dff24228e3ab0c6b2017f5e76 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 2 Apr 2021 21:25:49 +0100 Subject: [PATCH 24/70] Improve on license-related information --- README.md | 12 ++++++++++++ src/erlware_commons.app.src | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2230425..6ff669c 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,18 @@ Goals for the project * Well Documented * Well Tested +Licenses +-------- + +This project contains elements licensed with Apache License, Version 2.0, +as well as elements licensed with The MIT License. + +You'll find license-related information in the header of specific files, +where warranted. + +In cases where no such information is present refer to +[COPYING](COPYING). + Currently Available Modules/Systems ------------------------------------ diff --git a/src/erlware_commons.app.src b/src/erlware_commons.app.src index 66f75bd..7709d81 100644 --- a/src/erlware_commons.app.src +++ b/src/erlware_commons.app.src @@ -6,6 +6,6 @@ {applications,[kernel,stdlib,cf]}, {maintainers,["Eric Merritt","Tristan Sloughter", "Jordan Wilberding","Martin Logan"]}, - {licenses,["Apache"]}, + {licenses,["Apache", "MIT"]}, {links,[{"Github", "https://github.com/erlware/erlware_commons"}]}]}. From c47c938537863fe8fbb0a83d04c43a3599502155 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 2 Apr 2021 22:44:02 +0100 Subject: [PATCH 25/70] Keep symlinks in copies --- src/ec_file.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index c623573..a139e6e 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -80,7 +80,14 @@ copy(From, To) -> copy_(From, To, [{file_info, [mode, time, owner, group]}]). copy_(From, To, Options) -> - case file:copy(From, To) of + Linked + = case file:read_link(From) of + {ok, Linked0} -> Linked0; + {error, _} -> undefined + end, + case Linked =/= undefined orelse file:copy(From, To) of + true -> + file:make_symlink(Linked, To); {ok, _} -> copy_file_info(To, From, proplists:get_value(file_info, Options, [])); {error, Error} -> From 4c0180f157fcb9cd85c6dfdf8f624bc35f6c31e1 Mon Sep 17 00:00:00 2001 From: "Paulo F. Oliveira" Date: Fri, 14 May 2021 10:11:56 +0100 Subject: [PATCH 26/70] Tweak CI versions (add 24.0, move 23.2 to 23.3) --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 49cfa50..4b80ea1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - otp_version: ['23.2', '22.3', '21.3', '20.3', '19.3'] + otp_version: ['24.0', '23.3', '22.3', '21.3', '20.3', '19.3'] os: [ubuntu-latest] steps: From 916539338fab7c6084f6fee78ea4b20158feb8fd Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Mon, 9 Aug 2021 15:06:57 +0000 Subject: [PATCH 27/70] Support Unicode string formatting Fixes https://github.com/erlware/erlware_commons/issues/157 --- src/ec_cmd_log.erl | 14 +++++++------- src/ec_compile.erl | 2 +- src/ec_git_vsn.erl | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index c601782..56efa5c 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -126,7 +126,7 @@ debug(LogState, Fun) colorize(LogState, ?CYAN, false, Fun()) end); debug(LogState, String) -> - debug(LogState, "~s~n", [String]). + debug(LogState, "~ts~n", [String]). %% @doc log at the debug level given the current log state with a format string %% and argements @see io:format/2 @@ -143,7 +143,7 @@ info(LogState, Fun) colorize(LogState, ?GREEN, false, Fun()) end); info(LogState, String) -> - info(LogState, "~s~n", [String]). + info(LogState, "~ts~n", [String]). %% @doc log at the info level given the current log state with a format string %% and argements @see io:format/2 @@ -160,7 +160,7 @@ error(LogState, Fun) colorize(LogState, ?RED, false, Fun()) end); error(LogState, String) -> - error(LogState, "~s~n", [String]). + error(LogState, "~ts~n", [String]). %% @doc log at the error level given the current log state with a format string %% and argements @see io:format/2 @@ -175,7 +175,7 @@ warn(LogState, Fun) when erlang:is_function(Fun) -> log(LogState, ?EC_WARN, fun() -> colorize(LogState, ?MAGENTA, false, Fun()) end); warn(LogState, String) -> - warn(LogState, "~s~n", [String]). + warn(LogState, "~ts~n", [String]). %% @doc log at the warn level given the current log state with a format string %% and argements @see io:format/2 @@ -187,7 +187,7 @@ warn(LogState, FormatString, Args) -> -spec log(t(), int_log_level(), log_fun()) -> ok. log(#state_t{log_level=DetailLogLevel}, LogLevel, Fun) when DetailLogLevel >= LogLevel -> - io:format("~s~n", [Fun()]); + io:format("~ts~n", [Fun()]); log(_, _, _) -> ok. @@ -254,10 +254,10 @@ colorize(State, Color, true, Msg) when ?VALID_COLOR(Color) -> colorize(State, Color - 32, false, Msg); colorize(#state_t{caller=command_line, intensity = high}, Color, false, Msg) when ?VALID_COLOR(Color) -> - lists:flatten(cf:format("~!" ++ [Color] ++"~s~s", [?PREFIX, Msg])); + lists:flatten(cf:format("~!" ++ [Color] ++"~ts~ts", [?PREFIX, Msg])); colorize(#state_t{caller=command_line, intensity = low}, Color, false, Msg) when ?VALID_COLOR(Color) -> - lists:flatten(cf:format("~!" ++ [Color] ++"~s~!!~s", [?PREFIX, Msg])); + lists:flatten(cf:format("~!" ++ [Color] ++"~ts~!!~ts", [?PREFIX, Msg])); colorize(_LogState, _Color, _Bold, Msg) -> Msg. diff --git a/src/ec_compile.erl b/src/ec_compile.erl index 6c15520..7199610 100644 --- a/src/ec_compile.erl +++ b/src/ec_compile.erl @@ -38,7 +38,7 @@ beam_to_erl_source(BeamFName, ErlFName) -> Src = erl_prettypr:format(erl_syntax:form_list(tl(Forms))), {ok, Fd} = file:open(ErlFName, [write]), - io:fwrite(Fd, "~s~n", [Src]), + io:fwrite(Fd, "~ts~n", [Src]), file:close(Fd); Error -> Error diff --git a/src/ec_git_vsn.erl b/src/ec_git_vsn.erl index c932c58..8c2376f 100644 --- a/src/ec_git_vsn.erl +++ b/src/ec_git_vsn.erl @@ -75,7 +75,7 @@ build_vsn_string(Vsn, RawRef, RawCount) -> get_patch_count(RawRef) -> Ref = re:replace(RawRef, "\\s", "", [global]), - Cmd = io_lib:format("git rev-list --count ~s..HEAD", + Cmd = io_lib:format("git rev-list --count ~ts..HEAD", [Ref]), case os:cmd(Cmd) of "fatal: " ++ _ -> @@ -88,7 +88,7 @@ get_patch_count(RawRef) -> parse_tags({}) -> parse_tags(""); parse_tags(Pattern) -> - Cmd = io_lib:format("git describe --abbrev=0 --tags --match \"~s*\"", [Pattern]), + Cmd = io_lib:format("git describe --abbrev=0 --tags --match \"~ts*\"", [Pattern]), Tag = os:cmd(Cmd), case Tag of "fatal: " ++ _ -> From d34da1d107199d710b78cf534e8482b87b06ec3f Mon Sep 17 00:00:00 2001 From: FlyingLu <77955264+FlyingLu@users.noreply.github.com> Date: Mon, 1 Nov 2021 16:24:15 +0800 Subject: [PATCH 28/70] fixed incorrect 'G' in the format parameter string In the format parameter string, 'G' should have represented 24-hour format of an hour WITHOUT leading zeros. But it actually results in 24-hour format of an hour WITH leading zeros. --- src/ec_date.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 97bcea3..7ba7e05 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -522,7 +522,7 @@ format([$g|T], {_,{H,_,_}}=Dt, Acc) when H > 12 -> format([$g|T], {_,{H,_,_}}=Dt, Acc) -> format(T, Dt, [itol(H)|Acc]); format([$G|T], {_,{H,_,_}}=Dt, Acc) -> - format(T, Dt, [pad2(H)|Acc]); + format(T, Dt, [itol(H)|Acc]); format([$h|T], {_,{H,_,_}}=Dt, Acc) when H > 12 -> format(T, Dt, [pad2(H-12)|Acc]); format([$h|T], {_,{H,_,_}}=Dt, Acc) -> From 791729c30a3f420abdf4f14103994c906c724701 Mon Sep 17 00:00:00 2001 From: FlyingLu <77955264+FlyingLu@users.noreply.github.com> Date: Tue, 2 Nov 2021 11:27:57 +0800 Subject: [PATCH 29/70] Fixed the incorrect macro. --- src/ec_date.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 7ba7e05..a7a2b66 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -45,8 +45,8 @@ -define( is_tz_offset(H1,H2,M1,M2), (?is_num(H1) andalso ?is_num(H2) andalso ?is_num(M1) andalso ?is_num(M2)) ). -define(GREGORIAN_SECONDS_1970, 62167219200). --define(ISO_8601_DATETIME_FORMAT, "Y-m-dTG:i:sZ"). --define(ISO_8601_DATETIME_WITH_MS_FORMAT, "Y-m-dTG:i:s.fZ"). +-define(ISO_8601_DATETIME_FORMAT, "Y-m-dTH:i:sZ"). +-define(ISO_8601_DATETIME_WITH_MS_FORMAT, "Y-m-dTH:i:s.fZ"). -type year() :: non_neg_integer(). -type month() :: 1..12 | {?MONTH_TAG, 1..12}. From e89e95de5f31840ef10ded25e065aa6a9b90503d Mon Sep 17 00:00:00 2001 From: FlyingLu <77955264+FlyingLu@users.noreply.github.com> Date: Wed, 3 Nov 2021 00:11:05 +0800 Subject: [PATCH 30/70] Added more test cases. Added more test cases to show the difference between 'G' and 'H'. --- src/ec_date.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ec_date.erl b/src/ec_date.erl index a7a2b66..918845f 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -762,6 +762,8 @@ basic_format_test_() -> ?_assertEqual(format("H:i:s",?DATE), "17:16:17"), ?_assertEqual(format("z",?DATE), "68"), ?_assertEqual(format("D M j G:i:s Y",?DATE), "Sat Mar 10 17:16:17 2001"), + ?_assertEqual(format("D M j G:i:s Y", {{2001,3,10},{5,16,17}}), "Sat Mar 10 5:16:17 2001"), + ?_assertEqual(format("D M j H:i:s Y", {{2001,3,10},{5,16,17}}), "Sat Mar 10 05:16:17 2001"), ?_assertEqual(format("ga",?DATE_NOON), "12pm"), ?_assertEqual(format("gA",?DATE_NOON), "12PM"), ?_assertEqual(format("ga",?DATE_MIDNIGHT), "12am"), From 8dd7378a75a2b5b96ad5009ce05029cbf3d5d833 Mon Sep 17 00:00:00 2001 From: "Kian-Meng, Ang" Date: Sun, 19 Dec 2021 17:36:19 +0800 Subject: [PATCH 31/70] Fix typos --- CONTRIBUTING.md | 2 +- README.md | 6 +++--- doc/signatures.md | 8 ++++---- src/ec_cmd_log.erl | 14 +++++++------- src/ec_file.erl | 6 +++--- src/ec_lists.erl | 2 +- src/ec_plists.erl | 28 ++++++++++++++-------------- src/ec_rbdict.erl | 4 ++-- src/ec_semver.erl | 6 +++--- src/ec_talk.erl | 8 ++++---- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e63ac46..cca6505 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ $ git stash pop ``` You SHOULD use these commands both before working on your patch and before -submitting the pull request. If conflicts arise it is your responsability +submitting the pull request. If conflicts arise it is your responsibility to deal with them. You MUST create a new branch for your work. First make sure you have diff --git a/README.md b/README.md index 6ff669c..f1fbfba 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ href="http://www.erlang.org/doc/man/lists.html">lists, making most list operations parallel. It can operate on each element in parallel, for IO-bound operations, on sublists in parallel, for taking advantage of multi-core machines with CPU-bound operations, and across erlang -nodes, for parallizing inside a cluster. It handles errors and node +nodes, for parallelizing inside a cluster. It handles errors and node failures. It can be configured, tuned, and tweaked to get optimal performance while minimizing overhead. @@ -77,7 +77,7 @@ Almost all the functions are identical to equivalent functions in lists, returning exactly the same result, and having both a form with an identical syntax that operates on each element in parallel and a form which takes an optional "malt", a specification for how to -parallize the operation. +parallelize the operation. fold is the one exception, parallel fold is different from linear fold. This module also include a simple mapreduce implementation, and @@ -106,7 +106,7 @@ Other languages, have built in support for **Interface** or **signature** functionality. Java has Interfaces, SML has Signatures. Erlang, though, doesn't currently support this model, at least not directly. There are a few ways you can approximate it. We -have defined a mechnism called *signatures* and several modules that +have defined a mechanism called *signatures* and several modules that to serve as examples and provide a good set of *dictionary* signatures. More information about signatures can be found at [signature](https://github.com/erlware/erlware_commons/blob/master/doc/signatures.md). diff --git a/doc/signatures.md b/doc/signatures.md index 18aab3c..8279622 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -2,7 +2,7 @@ Signatures ========== It often occurs in coding that we need a library, a set of -functionaly. Often there are several algorithms that could provide +functionally. Often there are several algorithms that could provide this functionality. However, the code that uses it, either doesn't care about the individual algorithm or wishes to delegate choosing that algorithm to some higher level. Lets take the concrete example of @@ -26,7 +26,7 @@ characteristics are about as good as that of randomly-built binary search trees - namely (O log n). So the choice of which to select depends very much on memory available, insert/read characteristics, etc. So delegating the choice to a single point in your code is a very -good idea. Unfortunately, in Erlang thats ot so easy to do at the moment. +good idea. Unfortunately, in Erlang that's so easy to do at the moment. Other languages, have built in support for this functionality. [Java](http://en.wikipedia.org/wiki/Java_(programming_language)) @@ -63,7 +63,7 @@ mistakes that you might have made. Tools like [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html) have just as hard a time figuring out the what `ModuleToUse` is pointing to as you do. So they can't give you warnings about potential problems. In fact -someone could inadvertantly pass an unexpected function name as +someone could inadvertently pass an unexpected function name as `ModuleToUse` and you would never get any warnings, just an exception at run time. @@ -361,7 +361,7 @@ what it looked like. 2> So for the direct dict call, we average about 3 mics per call, while -for the Signature Wrapper we average around 4. Thats a 25% cost for +for the Signature Wrapper we average around 4. That's a 25% cost for Signature Wrappers in this example, for a very small number of calls. Depending on what you are doing that is going to be greater or lesser. In any case, we can see that there is some cost associated diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index 56efa5c..ba616f4 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -19,7 +19,7 @@ %%% @copyright (C) 2012 Erlware, LLC. %%% %%% @doc This provides simple output functions for command line apps. You should -%%% use this to talk to the users if you are wrting code for the system +%%% use this to talk to the users if you are writing code for the system -module(ec_cmd_log). %% Avoid clashing with `error/3` BIF added in Erlang/OTP 24 @@ -129,7 +129,7 @@ debug(LogState, String) -> debug(LogState, "~ts~n", [String]). %% @doc log at the debug level given the current log state with a format string -%% and argements @see io:format/2 +%% and arguments @see io:format/2 -spec debug(t(), string(), [any()]) -> ok. debug(LogState, FormatString, Args) -> log(LogState, ?EC_DEBUG, colorize(LogState, ?CYAN, false, FormatString), Args). @@ -146,7 +146,7 @@ info(LogState, String) -> info(LogState, "~ts~n", [String]). %% @doc log at the info level given the current log state with a format string -%% and argements @see io:format/2 +%% and arguments @see io:format/2 -spec info(t(), string(), [any()]) -> ok. info(LogState, FormatString, Args) -> log(LogState, ?EC_INFO, colorize(LogState, ?GREEN, false, FormatString), Args). @@ -163,7 +163,7 @@ error(LogState, String) -> error(LogState, "~ts~n", [String]). %% @doc log at the error level given the current log state with a format string -%% and argements @see io:format/2 +%% and arguments @see io:format/2 -spec error(t(), string(), [any()]) -> ok. error(LogState, FormatString, Args) -> log(LogState, ?EC_ERROR, colorize(LogState, ?RED, false, FormatString), Args). @@ -178,7 +178,7 @@ warn(LogState, String) -> warn(LogState, "~ts~n", [String]). %% @doc log at the warn level given the current log state with a format string -%% and argements @see io:format/2 +%% and arguments @see io:format/2 -spec warn(t(), string(), [any()]) -> ok. warn(LogState, FormatString, Args) -> log(LogState, ?EC_WARN, colorize(LogState, ?MAGENTA, false, FormatString), Args). @@ -243,12 +243,12 @@ format(Log) -> colorize(#state_t{intensity=none}, _, _, Msg) -> Msg; -%% When it is suposed to be bold and we already have a uppercase +%% When it is supposed to be bold and we already have a uppercase %% (bold color) we don't need to modify the color colorize(State, Color, true, Msg) when ?VALID_COLOR(Color), Color >= $A, Color =< $Z -> colorize(State, Color, false, Msg); -%% We're sneaky we can substract 32 to get the uppercase character if we want +%% We're sneaky we can subtract 32 to get the uppercase character if we want %% bold but have a non bold color. colorize(State, Color, true, Msg) when ?VALID_COLOR(Color) -> colorize(State, Color - 32, false, Msg); diff --git a/src/ec_file.erl b/src/ec_file.erl index a139e6e..e310336 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -174,7 +174,7 @@ remove(Path, Options) -> remove(Path) -> remove(Path, []). -%% @doc indicates witha boolean if the path supplied refers to symlink. +%% @doc indicates with a boolean if the path supplied refers to symlink. -spec is_symlink(file:name()) -> boolean(). is_symlink(Path) -> case file:read_link_info(Path) of @@ -252,7 +252,7 @@ mkdir_path(Path) -> mkdir_p(Path). -%% @doc read a file from the file system. Provide UEX exeption on failure. +%% @doc read a file from the file system. Provide UEX exception on failure. -spec read(FilePath::file:filename()) -> {ok, binary()} | {error, Reason::term()}. read(FilePath) -> %% Now that we are moving away from exceptions again this becomes @@ -261,7 +261,7 @@ read(FilePath) -> file:read_file(FilePath). -%% @doc write a file to the file system. Provide UEX exeption on failure. +%% @doc write a file to the file system. Provide UEX exception on failure. -spec write(FileName::file:filename(), Contents::string()) -> ok | {error, Reason::term()}. write(FileName, Contents) -> %% Now that we are moving away from exceptions again this becomes diff --git a/src/ec_lists.erl b/src/ec_lists.erl index c95078b..0ae5204 100644 --- a/src/ec_lists.erl +++ b/src/ec_lists.erl @@ -52,7 +52,7 @@ find(_Fun, []) -> error. %% @doc Fetch a value from the list. If the function returns true the -%% value is returend. If processing reaches the end of the list and +%% value is returned. If processing reaches the end of the list and %% the function has never returned true an exception not_found is %% thrown. -spec fetch(fun(), list()) -> term(). diff --git a/src/ec_plists.erl b/src/ec_plists.erl index 50f122e..27715e3 100644 --- a/src/ec_plists.erl +++ b/src/ec_plists.erl @@ -30,7 +30,7 @@ %%% most list operations parallel. It can operate on each element in %%% parallel, for IO-bound operations, on sublists in parallel, for %%% taking advantage of multi-core machines with CPU-bound operations, -%%% and across erlang nodes, for parallizing inside a cluster. It +%%% and across erlang nodes, for parallelizing inside a cluster. It %%% handles errors and node failures. It can be configured, tuned, and %%% tweaked to get optimal performance while minimizing overhead. %%% @@ -38,7 +38,7 @@ %%% lists, returning exactly the same result, and having both a form %%% with an identical syntax that operates on each element in parallel %%% and a form which takes an optional "malt", a specification for how -%%% to parallize the operation. +%%% to parallelize the operation. %%% %%% fold is the one exception, parallel fold is different from linear %%% fold. This module also include a simple mapreduce implementation, @@ -169,7 +169,7 @@ %%% processes. If one of them does a non-normal exit, plists receives %%% the 'DOWN' message believing it to be from one of its own %%% processes. The error propagation system goes into effect, which -%%% results in the error occuring in the calling process. +%%% results in the error occurring in the calling process. %%% -module(ec_plists). @@ -330,14 +330,14 @@ fold(Fun, Fuse, InitAcc, List, Malt) -> end, runmany(Fun2, Fuse, List, Malt). -%% @doc Similiar to foreach in module +%% @doc Similar to foreach in module %% lists %% except it makes no guarantee about the order it processes list elements. -spec foreach(fun(), list()) -> ok. foreach(Fun, List) -> foreach(Fun, List, 1). -%% @doc Similiar to foreach in module +%% @doc Similar to foreach in module %% lists %% except it makes no guarantee about the order it processes list elements. -spec foreach(fun(), list(), malt()) -> ok. @@ -432,8 +432,8 @@ sort(Fun, List) -> %% %% sort splits the list into sublists and sorts them, and it merges the %% sorted lists together. These are done in parallel. Each sublist is -%% sorted in a seperate process, and each merging of results is done in a -%% seperate process. Malt defaults to 100, causing the list to be split into +%% sorted in a separate process, and each merging of results is done in a +%% separate process. Malt defaults to 100, causing the list to be split into %% 100-element sublists. -spec sort(fun(), list(), malt()) -> list(). sort(Fun, List, Malt) -> @@ -464,11 +464,11 @@ usort(Fun, List) -> %% %% usort splits the list into sublists and sorts them, and it merges the %% sorted lists together. These are done in parallel. Each sublist is -%% sorted in a seperate process, and each merging of results is done in a -%% seperate process. Malt defaults to 100, causing the list to be split into +%% sorted in a separate process, and each merging of results is done in a +%% separate process. Malt defaults to 100, causing the list to be split into %% 100-element sublists. %% -%% usort removes duplicate elments while it sorts. +%% usort removes duplicate elements while it sorts. -spec usort(fun(), list(), malt()) -> list(). usort(Fun, List, Malt) -> Fun2 = fun (L) -> @@ -514,7 +514,7 @@ mapreduce(MapFunc, List, MapMalt) -> %% reducer's final state. %% %% MapMalt is the malt for the mapping operation, with a default value of 1, -%% meaning each element of the list is mapped by a seperate process. +%% meaning each element of the list is mapped by a separate process. %% %% mapreduce requires OTP R11B, or it may leave monitoring messages in the %% message queue. @@ -586,7 +586,7 @@ add_key(Dict, Key, Value) -> end. %% @doc Like below, but assumes a Malt of 1, -%% meaning each element of the list is processed by a seperate process. +%% meaning each element of the list is processed by a separate process. -spec runmany(fun(), fuse(), list()) -> term(). runmany(Fun, Fuse, List) -> runmany(Fun, Fuse, List, 1). @@ -615,7 +615,7 @@ runmany(Fun, Fuse, List) -> %% continues fusing pairs of results until it is down to one. %% %% Recursive fuse is down in parallel with processing the sublists, and a -%% process is spawned to fuse each pair of results. It is a parallized +%% process is spawned to fuse each pair of results. It is a parallelized %% algorithm. Linear fuse is done after all results of processing sublists %% have been collected, and can only run in a single process. %% @@ -691,7 +691,7 @@ runmany(Fun, {recursive, Fuse}, List, local, Split, []) -> %% or {nodes, NodeList}. Degenerates recursive fuse into linear fuse. runmany(Fun, Fuse, List, local, Split, []); runmany(Fun, Fuse, List, Nodes, no_split, []) -> - %% by default, operate on each element seperately + %% by default, operate on each element separately runmany(Fun, Fuse, List, Nodes, 1, []); runmany(Fun, Fuse, List, local, Split, []) -> List2 = splitmany(List, Split), diff --git a/src/ec_rbdict.erl b/src/ec_rbdict.erl index 60e337f..9f3b617 100644 --- a/src/ec_rbdict.erl +++ b/src/ec_rbdict.erl @@ -32,7 +32,7 @@ %%% representation of a dictionary, where a red-black tree is used to %%% store the keys and values. %%% -%%% This module implents exactly the same interface as the module +%%% This module implements exactly the same interface as the module %%% ec_dictionary but with a defined representation. One difference is %%% that while dict considers two keys as different if they do not %%% match (=:=), this module considers two keys as different if and @@ -296,7 +296,7 @@ to_list(empty, List) -> List; to_list({_, A, Xk, Xv, B}, List) -> to_list(A, [{Xk, Xv} | to_list(B, List)]). -%% Balance a tree afer (possibly) adding a node to the left/right. +%% Balance a tree after (possibly) adding a node to the left/right. -spec lbalance(color(), dictionary(K, V), ec_dictionary:key(K), ec_dictionary:value(V), dictionary(K, V)) -> diff --git a/src/ec_semver.erl b/src/ec_semver.erl index 8141065..493466f 100644 --- a/src/ec_semver.erl +++ b/src/ec_semver.erl @@ -202,13 +202,13 @@ pes(VsnA, VsnB) -> %%%=================================================================== %%% Friend Functions %%%=================================================================== -%% @doc helper function for the peg grammer to parse the iolist into a semver +%% @doc helper function for the peg grammar to parse the iolist into a semver -spec internal_parse_version(iolist()) -> semver(). internal_parse_version([MMP, AlphaPart, BuildPart, _]) -> {parse_major_minor_patch_minpatch(MMP), {parse_alpha_part(AlphaPart), parse_alpha_part(BuildPart)}}. -%% @doc helper function for the peg grammer to parse the iolist into a major_minor_patch +%% @doc helper function for the peg grammar to parse the iolist into a major_minor_patch -spec parse_major_minor_patch_minpatch(iolist()) -> major_minor_patch_minpatch(). parse_major_minor_patch_minpatch([MajVsn, [], [], []]) -> strip_maj_version(MajVsn); @@ -224,7 +224,7 @@ parse_major_minor_patch_minpatch([MajVsn, [<<".">>, MinPatch]]) -> {strip_maj_version(MajVsn), MinVsn, PatchVsn, MinPatch}. -%% @doc helper function for the peg grammer to parse the iolist into an alpha part +%% @doc helper function for the peg grammar to parse the iolist into an alpha part -spec parse_alpha_part(iolist()) -> [alpha_part()]. parse_alpha_part([]) -> []; diff --git a/src/ec_talk.erl b/src/ec_talk.erl index 454b1f8..7cacf2a 100644 --- a/src/ec_talk.erl +++ b/src/ec_talk.erl @@ -75,7 +75,7 @@ ask(Prompt) -> ask_default(Prompt, Default) -> ask_convert(Prompt, fun get_string/1, string, Default). -%% @doc Asks the user to respond to the prompt. Trys to return the +%% @doc Asks the user to respond to the prompt. Tries to return the %% value in the format specified by 'Type'. -spec ask(prompt(), type()) -> supported(). ask(Prompt, boolean) -> @@ -85,7 +85,7 @@ ask(Prompt, number) -> ask(Prompt, string) -> ask_convert(Prompt, fun get_string/1, string, none). -%% @doc Asks the user to respond to the prompt. Trys to return the +%% @doc Asks the user to respond to the prompt. Tries to return the %% value in the format specified by 'Type'. -spec ask_default(prompt(), type(), supported()) -> supported(). ask_default(Prompt, boolean, Default) -> @@ -145,7 +145,7 @@ ask_convert(Prompt, TransFun, Type, Default) -> Ret end. -%% @doc Trys to translate the result into a boolean +%% @doc Tries to translate the result into a boolean -spec get_boolean(string()) -> boolean(). get_boolean([]) -> no_data; @@ -172,7 +172,7 @@ get_boolean([$N | _]) -> get_boolean(_) -> no_clue. -%% @doc Trys to translate the result into an integer +%% @doc Tries to translate the result into an integer -spec get_integer(string()) -> integer(). get_integer([]) -> no_data; From d5183f5336c42095d4e19ca0e7682cec740986cc Mon Sep 17 00:00:00 2001 From: Ariel Date: Sat, 9 Dec 2023 14:07:13 +0100 Subject: [PATCH 32/70] Removed redudant clauses in local parse/3 --- src/ec_date.erl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 918845f..dd15b6d 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -197,17 +197,6 @@ parse([Day,X,Month,X,Year,Hour,$:,Min,$:,Sec,$., Ms | PAM], _Now, _Opts) andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}, {Ms}}; -parse([Year,X,Month,X,Day,Hour,$:,Min,$:,Sec,$., Ms], _Now, _Opts) - when (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso ?is_year(Year) -> - {{Year, Month, Day}, {hour(Hour,[]), Min, Sec}, {Ms}}; -parse([Month,X,Day,X,Year,Hour,$:,Min,$:,Sec,$., Ms], _Now, _Opts) - when ?is_us_sep(X) andalso ?is_month(Month) -> - {{Year, Month, Day}, {hour(Hour, []), Min, Sec}, {Ms}}; -parse([Day,X,Month,X,Year,Hour,$:,Min,$:,Sec,$., Ms ], _Now, _Opts) - when ?is_world_sep(X) andalso ?is_month(Month) -> - {{Year, Month, Day}, {hour(Hour, []), Min, Sec}, {Ms}}; - %% Date/Times Dec 1st, 2012 6:25 PM parse([Month,Day,Year,Hour,$:,Min,$:,Sec | PAM], _Now, _Opts) when ?is_meridian(PAM) andalso ?is_hinted_month(Month) andalso ?is_day(Day) -> From 378b88587cc039ff926380f426974a61f7b0ce62 Mon Sep 17 00:00:00 2001 From: Ariel Date: Sun, 10 Dec 2023 01:04:46 +0100 Subject: [PATCH 33/70] Fix for https://github.com/erlware/erlware_commons/issues/138 --- src/ec_plists.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ec_plists.erl b/src/ec_plists.erl index 27715e3..846049d 100644 --- a/src/ec_plists.erl +++ b/src/ec_plists.erl @@ -875,7 +875,7 @@ cluster_runmany(_, _, [_Non|_Empty], []=_Nodes, []=_Running, _) -> -ifdef(fun_stacktrace). runmany_wrap(Fun, Parent) -> try - Fun + Fun() catch exit:siblingdied -> ok; @@ -889,7 +889,7 @@ runmany_wrap(Fun, Parent) -> -else. runmany_wrap(Fun, Parent) -> try - Fun + Fun() catch exit:siblingdied -> ok; From ad4b944fc624281ee08a1c19ed981d6ed396ed06 Mon Sep 17 00:00:00 2001 From: Ariel Date: Sun, 10 Dec 2023 17:08:41 +0100 Subject: [PATCH 34/70] Removed other redudant clauses; same behaviour than https://github.com/erlware/erlware_commons/pull/162 --- src/ec_date.erl | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index dd15b6d..c894e83 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -208,14 +208,6 @@ parse([Month,Day,Year,Hour | PAM], _Now, _Opts) when ?is_meridian(PAM) andalso ?is_hinted_month(Month) andalso ?is_day(Day) -> {{Year, Month, Day}, {hour(Hour, PAM), 0, 0}}; -%% Date/Times Dec 1st, 2012 18:25:15 (no AM/PM) -parse([Month,Day,Year,Hour,$:,Min,$:,Sec], _Now, _Opts) - when ?is_hinted_month(Month) andalso ?is_day(Day) -> - {{Year, Month, Day}, {hour(Hour, []), Min, Sec}}; -parse([Month,Day,Year,Hour,$:,Min], _Now, _Opts) - when ?is_hinted_month(Month) andalso ?is_day(Day) -> - {{Year, Month, Day}, {hour(Hour, []), Min, 0}}; - %% Date/Times Fri Nov 21 14:55:26 +0000 2014 (Twitter format) parse([Month, Day, Hour,$:,Min,$:,Sec, Year], _Now, _Opts) when ?is_hinted_month(Month), ?is_day(Day), ?is_year(Year) -> From 62a985b9371dcd79718699d2b64ff35a25d58448 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 20:29:17 +0100 Subject: [PATCH 35/70] Instead of three colons, syntax is highlighted with three backticks --- doc/signatures.md | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/doc/signatures.md b/doc/signatures.md index 8279622..2e7c60d 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -39,17 +39,20 @@ directly. There are a few ways you can approximate it. One way is to pass the Module name to the calling functions along with the data that it is going to be called on. - :::erlang - add(ModuleToUse, Key, Value, DictData) -> - ModuleToUse:add(Key, Value, DictData). + ```erlang + add(ModuleToUse, Key, Value, DictData) -> + ModuleToUse:add(Key, Value, DictData). + ``` This works, and you can vary how you want to pass the data. For example, you could easily use a tuple to contain the data. That is, you could pass in `{ModuleToUse, DictData}` and that would make it a bit cleaner. - :::erlang + + ```erlang add(Key, Value, {ModuleToUse, DictData}) -> - ModuleToUse:add(Key, Value, DictData). + ModuleToUse:add(Key, Value, DictData). + ``` Either way, there are a few problems with this approach. One of the biggest is that you lose code locality, by looking at this bit of code @@ -75,9 +78,10 @@ name. So what we actually want to do is something mole like this: - :::erlang + ```erlang add(Key, Value, DictData) -> dictionary:add(Key, Value, DictData). + ``` Doing this we retain the locality. We can easily look up the `dictionary` Module. We immediately have a good idea what a @@ -97,7 +101,7 @@ a [Behaviour](http://metajack.im/2008/10/29/custom-behaviors-in-erlang/) for our functionality. To continue our example we will define a Behaviour for dictionaries. That Behaviour looks like this: - :::erlang + ```erlang -module(ec_dictionary). -export([behaviour_info/1]). @@ -115,6 +119,7 @@ Behaviour for dictionaries. That Behaviour looks like this: {keys, 1}]; behaviour_info(_) -> undefined. + ``` So we have our Behaviour now. Unfortunately, this doesn't give us much @@ -124,7 +129,7 @@ dictionaries in an abstract way in our code. To do that we need to add a bit of functionality. We do that by actually implementing our own behaviour, starting with `new/1`. - :::erlang + ```erlang %% @doc create a new dictionary object from the specified module. The %% module should implement the dictionary behaviour. %% @@ -132,6 +137,7 @@ behaviour, starting with `new/1`. -spec new(module()) -> dictionary(_K, _V). new(ModuleName) when is_atom(ModuleName) -> #dict_t{callback = ModuleName, data = ModuleName:new()}. + ``` This code creates a new dictionary for us. Or to be more specific it actually creates a new dictionary Signature record, that will be used @@ -148,7 +154,7 @@ dictionary and another that just retrieves data. The first we will look at is the one that updates the dictionary by adding a value. - :::erlang + ```erlang %% @doc add a new value to the existing dictionary. Return a new %% dictionary containing the value. %% @@ -158,6 +164,7 @@ adding a value. -spec add(key(K), value(V), dictionary(K, V)) -> dictionary(K, V). add(Key, Value, #dict_t{callback = Mod, data = Data} = Dict) -> Dict#dict_t{data = Mod:add(Key, Value, Data)}. + ``` There are two key things here. @@ -173,7 +180,7 @@ implementation to do the work itself. Now lets do a data retrieval function. In this case, the `get` function of the dictionary Signature. - :::erlang + ```erlang %% @doc given a key return that key from the dictionary. If the key is %% not found throw a 'not_found' exception. %% @@ -183,6 +190,7 @@ of the dictionary Signature. -spec get(key(K), dictionary(K, V)) -> value(V). get(Key, #dict_t{callback = Mod, data = Data}) -> Mod:get(Key, Data). + ``` In this case, you can see a very similar approach to deconstructing the dict record. We still need to pull out the callback module and the @@ -236,7 +244,7 @@ We will take a look at one of the functions we have already seen. The semantics as any of the functions in the dict module. So a bit of translation needs to be done. We do that in the ec_dict module `get` function. - :::erlang + ```erlang -spec get(ec_dictionary:key(K), Object::dictionary(K, V)) -> ec_dictionary:value(V). get(Key, Data) -> @@ -246,6 +254,7 @@ translation needs to be done. We do that in the ec_dict module `get` function. error -> throw(not_found) end. + ``` So the ec_dict module's purpose for existence is to help the preexisting dict module implement the Behaviour defined by the @@ -267,12 +276,13 @@ create a couple of functions that create dictionaries for each type we want to test. The first we want to time is the Signature Wrapper, so `dict` vs `ec_dict` called as a Signature. - :::erlang + ```erlang create_dict() -> lists:foldl(fun(El, Dict) -> dict:store(El, El, Dict) end, dict:new(), lists:seq(1,100)). + ``` The only thing we do here is create a sequence of numbers 1 to 100, and then add each of those to the dict as an entry. We aren't too @@ -283,13 +293,14 @@ of the dictionaries themselves. We need to create a similar function for our Signature based dictionary `ec_dict`. - :::erlang + ```erlang create_dictionary(Type) -> lists:foldl(fun(El, Dict) -> ec_dictionary:add(El, El, Dict) end, ec_dictionary:new(Type), lists:seq(1,100)). + ``` Here we actually create everything using the Signature. So we don't need one function for each type. We can have one function that can @@ -302,7 +313,7 @@ data and one that returns data, just to get good coverage. For our dictionaries we are going to use the `size` function as well as the `add` function. - :::erlang + ```erlang time_direct_vs_signature_dict() -> io:format("Timing dict~n"), Dict = create_dict(), @@ -312,6 +323,7 @@ the `add` function. 1000000), io:format("Timing ec_dict implementation of ec_dictionary~n"), time_dict_type(ec_dict). + ``` The `test_avg` function runs the provided function the number of times specified in the second argument and collects timing information. We @@ -323,7 +335,7 @@ we don't have to hard code the calls for the Signature implementations. Lets take a look at the `time_dict_type` function. - :::erlang + ```erlang time_dict_type(Type) -> io:format("Testing ~p~n", [Type]), Dict = create_dictionary(Type), @@ -331,6 +343,7 @@ implementations. Lets take a look at the `time_dict_type` function. ec_dictionary:size(ec_dictionary:add(some_key, some_value, Dict)) end, 1000000). + ``` As you can see we take the type as an argument (we need it for `dict` creation) and call our create function. Then we run the same timings @@ -343,7 +356,7 @@ work for anything that implements that Signature. So we have our tests, what was the result. Well on my laptop this is what it looked like. - :::sh + ```sh Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.2 (abort with ^G) @@ -359,6 +372,7 @@ what it looked like. Median: 3 mics Average: 4 mics 2> + ``` So for the direct dict call, we average about 3 mics per call, while for the Signature Wrapper we average around 4. That's a 25% cost for @@ -373,12 +387,13 @@ Signature, but it is not a Signature Wrapper. It is a native implementation of the Signature. To use `ec_rbdict` directly we have to create a creation helper just like we did for dict. - :::erlang + ```erlang create_rbdict() -> lists:foldl(fun(El, Dict) -> ec_rbdict:add(El, El, Dict) end, ec_rbdict:new(), lists:seq(1,100)). + ``` This is exactly the same as `create_dict` with the exception that dict is replaced by `ec_rbdict`. @@ -387,7 +402,7 @@ The timing function itself looks very similar as well. Again notice that we have to hard code the concrete name for the concrete implementation, but we don't for the ec_dictionary test. - :::erlang + ```erlang time_direct_vs_signature_rbdict() -> io:format("Timing rbdict~n"), Dict = create_rbdict(), @@ -397,6 +412,7 @@ implementation, but we don't for the ec_dictionary test. 1000000), io:format("Timing ec_dict implementation of ec_dictionary~n"), time_dict_type(ec_rbdict). + ``` And there we have our test. What do the results look like? @@ -406,7 +422,7 @@ The main thing we are timing here is the additional cost of the dictionary Signature itself. Keep that in mind as we look at the results. - :::sh + ```sh Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.2 (abort with ^G) @@ -422,6 +438,7 @@ results. Median: 7 mics Average: 7 mics 2> + ``` So no difference it time. Well the reality is that there is a difference in timing, there must be, but we don't have enough From 6d4c471ff619602decf1c566b59d3da4e37cdb92 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 20:31:49 +0100 Subject: [PATCH 36/70] Fixed typos and misnamings --- doc/signatures.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/signatures.md b/doc/signatures.md index 2e7c60d..24315f7 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -2,10 +2,10 @@ Signatures ========== It often occurs in coding that we need a library, a set of -functionally. Often there are several algorithms that could provide -this functionality. However, the code that uses it, either doesn't +functionalities. Often there are several algorithms that could provide +each of these functionalities. However, the code that uses it, either doesn't care about the individual algorithm or wishes to delegate choosing -that algorithm to some higher level. Lets take the concrete example of +that algorithm to some higher level. Let's take the concrete example of dictionaries. A dictionary provides the ability to access a value via a key (other things as well but primarily this). There are may ways to implement a dictionary. Just a few are: @@ -234,15 +234,15 @@ purpose is to help a preexisting module implement the Behaviour defined by a Signature. A good example of this in our current example is the [erlware_commons/ec_dict](https://github.com/ericbmerritt/erlware_commons/blob/types/src/ec_dict.erl) -module. It implements the ec_dictionary Behaviour, but all the +module. It implements the `ec_dictionary` Behaviour, but all the functionality is provided by the [stdlib/dict](http://www.erlang.org/doc/man/dict.html) module itself. Let's take a look at one example to see how this is done. We will take a look at one of the functions we have already seen. The -`get` function in ec_dictionary doesn't have quite the same -semantics as any of the functions in the dict module. So a bit of -translation needs to be done. We do that in the ec_dict module `get` function. +`get` function in `ec_dictionary` doesn't have quite the same +semantics as any of the functions in the `dict` module. So a bit of +translation needs to be done. We do that in the `ec_dict:get/2` function. ```erlang -spec get(ec_dictionary:key(K), Object::dictionary(K, V)) -> @@ -256,8 +256,8 @@ translation needs to be done. We do that in the ec_dict module `get` function. end. ``` -So the ec_dict module's purpose for existence is to help the -preexisting dict module implement the Behaviour defined by the +So the `ec_dict` module's purpose for existence is to help the +preexisting `dict` module implement the Behaviour defined by the Signature. @@ -285,7 +285,7 @@ want to test. The first we want to time is the Signature Wrapper, so ``` The only thing we do here is create a sequence of numbers 1 to 100, -and then add each of those to the dict as an entry. We aren't too +and then add each of those to the `dict` as an entry. We aren't too worried about replicating real data in the dictionary. We care about timing the function call overhead of Signatures, not the performance of the dictionaries themselves. @@ -305,8 +305,8 @@ dictionary `ec_dict`. Here we actually create everything using the Signature. So we don't need one function for each type. We can have one function that can create anything that implements the Signature. That is the magic of -Signatures. Otherwise, this does the exact same thing as the dict -`create_dict/1`. +Signatures. Otherwise, this does the exact same thing as the dictionary +given by `create_dict/0`. We are going to use two function calls in our timing. One that updates data and one that returns data, just to get good coverage. For our @@ -347,7 +347,7 @@ implementations. Lets take a look at the `time_dict_type` function. As you can see we take the type as an argument (we need it for `dict` creation) and call our create function. Then we run the same timings -that we did for ec dict. In this case though, the type of dictionary +that we did for `ec_dict`. In this case though, the type of dictionary is never specified, we only ever call ec_dictionary, so this test will work for anything that implements that Signature. @@ -374,7 +374,7 @@ what it looked like. 2> ``` -So for the direct dict call, we average about 3 mics per call, while +So for the direct `dict` call, we average about 3 mics per call, while for the Signature Wrapper we average around 4. That's a 25% cost for Signature Wrappers in this example, for a very small number of calls. Depending on what you are doing that is going to be greater or @@ -400,7 +400,7 @@ is replaced by `ec_rbdict`. The timing function itself looks very similar as well. Again notice that we have to hard code the concrete name for the concrete -implementation, but we don't for the ec_dictionary test. +implementation, but we don't for the `ec_dictionary` test. ```erlang time_direct_vs_signature_rbdict() -> From 5c5c2642418bc208311c1bbc96660b034aa0dfc7 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 20:33:17 +0100 Subject: [PATCH 37/70] For formulae, used Markdown math mode --- doc/signatures.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/signatures.md b/doc/signatures.md index 24315f7..3142c37 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -17,13 +17,13 @@ implement a dictionary. Just a few are: * Many, many more .... Each of these approaches has their own performance characteristics, -memory footprints etc. For example, a table of size n with open -addressing has no collisions and holds up to n elements, with a single -comparison for successful lookup, and a table of size n with chaining -and k keys has the minimum max(0, k-n) collisions and O(1 + k/n) +memory footprints, etc. For example, a table of size $n$ with open +addressing has no collisions and holds up to $n$ elements, with a single +comparison for successful lookup, and a table of size $n$_ with chaining +and $k$ keys has the minimum $\max(0, k-n)$ collisions and $\mathcal{O}(1 + k/n)$ comparisons for lookup. While for skip lists the performance characteristics are about as good as that of randomly-built binary -search trees - namely (O log n). So the choice of which to select +search trees - namely ($\mathcal{O}(\log n)$). So the choice of which to select depends very much on memory available, insert/read characteristics, etc. So delegating the choice to a single point in your code is a very good idea. Unfortunately, in Erlang that's so easy to do at the moment. From cd88825861250533858d6aff56a87a5ab9cecda1 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 20:59:57 +0100 Subject: [PATCH 38/70] Syntax highlighted in code snippets --- doc/signatures.md | 286 +++++++++++++++++++++++----------------------- 1 file changed, 143 insertions(+), 143 deletions(-) diff --git a/doc/signatures.md b/doc/signatures.md index 3142c37..3e32c11 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -39,20 +39,20 @@ directly. There are a few ways you can approximate it. One way is to pass the Module name to the calling functions along with the data that it is going to be called on. - ```erlang - add(ModuleToUse, Key, Value, DictData) -> - ModuleToUse:add(Key, Value, DictData). - ``` +```erlang +add(ModuleToUse, Key, Value, DictData) -> + ModuleToUse:add(Key, Value, DictData). +``` This works, and you can vary how you want to pass the data. For example, you could easily use a tuple to contain the data. That is, you could pass in `{ModuleToUse, DictData}` and that would make it a bit cleaner. - ```erlang - add(Key, Value, {ModuleToUse, DictData}) -> - ModuleToUse:add(Key, Value, DictData). - ``` +```erlang +add(Key, Value, {ModuleToUse, DictData}) -> + ModuleToUse:add(Key, Value, DictData). +``` Either way, there are a few problems with this approach. One of the biggest is that you lose code locality, by looking at this bit of code @@ -78,10 +78,10 @@ name. So what we actually want to do is something mole like this: - ```erlang - add(Key, Value, DictData) -> - dictionary:add(Key, Value, DictData). - ``` +```erlang +add(Key, Value, DictData) -> + dictionary:add(Key, Value, DictData). +``` Doing this we retain the locality. We can easily look up the `dictionary` Module. We immediately have a good idea what a @@ -101,25 +101,25 @@ a [Behaviour](http://metajack.im/2008/10/29/custom-behaviors-in-erlang/) for our functionality. To continue our example we will define a Behaviour for dictionaries. That Behaviour looks like this: - ```erlang - -module(ec_dictionary). +```erlang +-module(ec_dictionary). - -export([behaviour_info/1]). +-export([behaviour_info/1]). - behaviour_info(callbacks) -> - [{new, 0}, - {has_key, 2}, - {get, 2}, - {add, 3}, - {remove, 2}, - {has_value, 2}, - {size, 1}, - {to_list, 1}, - {from_list, 1}, - {keys, 1}]; - behaviour_info(_) -> - undefined. - ``` +behaviour_info(callbacks) -> + [{new, 0}, + {has_key, 2}, + {get, 2}, + {add, 3}, + {remove, 2}, + {has_value, 2}, + {size, 1}, + {to_list, 1}, + {from_list, 1}, + {keys, 1}]; +behaviour_info(_) -> + undefined. +``` So we have our Behaviour now. Unfortunately, this doesn't give us much @@ -129,15 +129,15 @@ dictionaries in an abstract way in our code. To do that we need to add a bit of functionality. We do that by actually implementing our own behaviour, starting with `new/1`. - ```erlang - %% @doc create a new dictionary object from the specified module. The - %% module should implement the dictionary behaviour. - %% - %% @param ModuleName The module name. - -spec new(module()) -> dictionary(_K, _V). - new(ModuleName) when is_atom(ModuleName) -> - #dict_t{callback = ModuleName, data = ModuleName:new()}. - ``` +```erlang +%% @doc create a new dictionary object from the specified module. The +%% module should implement the dictionary behaviour. +%% +%% @param ModuleName The module name. +-spec new(module()) -> dictionary(_K, _V). +new(ModuleName) when is_atom(ModuleName) -> + #dict_t{callback = ModuleName, data = ModuleName:new()}. +``` This code creates a new dictionary for us. Or to be more specific it actually creates a new dictionary Signature record, that will be used @@ -154,17 +154,17 @@ dictionary and another that just retrieves data. The first we will look at is the one that updates the dictionary by adding a value. - ```erlang - %% @doc add a new value to the existing dictionary. Return a new - %% dictionary containing the value. - %% - %% @param Dict the dictionary object to add too - %% @param Key the key to add - %% @param Value the value to add - -spec add(key(K), value(V), dictionary(K, V)) -> dictionary(K, V). - add(Key, Value, #dict_t{callback = Mod, data = Data} = Dict) -> - Dict#dict_t{data = Mod:add(Key, Value, Data)}. - ``` +```erlang +%% @doc add a new value to the existing dictionary. Return a new +%% dictionary containing the value. +%% +%% @param Dict the dictionary object to add too +%% @param Key the key to add +%% @param Value the value to add +-spec add(key(K), value(V), dictionary(K, V)) -> dictionary(K, V). +add(Key, Value, #dict_t{callback = Mod, data = Data} = Dict) -> + Dict#dict_t{data = Mod:add(Key, Value, Data)}. +``` There are two key things here. @@ -180,17 +180,17 @@ implementation to do the work itself. Now lets do a data retrieval function. In this case, the `get` function of the dictionary Signature. - ```erlang - %% @doc given a key return that key from the dictionary. If the key is - %% not found throw a 'not_found' exception. - %% - %% @param Dict The dictionary object to return the value from - %% @param Key The key requested - %% @throws not_found when the key does not exist - -spec get(key(K), dictionary(K, V)) -> value(V). - get(Key, #dict_t{callback = Mod, data = Data}) -> - Mod:get(Key, Data). - ``` +```erlang +%% @doc given a key return that key from the dictionary. If the key is +%% not found throw a 'not_found' exception. +%% +%% @param Dict The dictionary object to return the value from +%% @param Key The key requested +%% @throws not_found when the key does not exist +-spec get(key(K), dictionary(K, V)) -> value(V). +get(Key, #dict_t{callback = Mod, data = Data}) -> + Mod:get(Key, Data). +``` In this case, you can see a very similar approach to deconstructing the dict record. We still need to pull out the callback module and the @@ -244,17 +244,17 @@ We will take a look at one of the functions we have already seen. The semantics as any of the functions in the `dict` module. So a bit of translation needs to be done. We do that in the `ec_dict:get/2` function. - ```erlang - -spec get(ec_dictionary:key(K), Object::dictionary(K, V)) -> - ec_dictionary:value(V). - get(Key, Data) -> - case dict:find(Key, Data) of - {ok, Value} -> - Value; +```erlang +-spec get(ec_dictionary:key(K), Object::dictionary(K, V)) -> + ec_dictionary:value(V). +get(Key, Data) -> + case dict:find(Key, Data) of + {ok, Value} -> + Value; error -> - throw(not_found) - end. - ``` + throw(not_found) + end. +``` So the `ec_dict` module's purpose for existence is to help the preexisting `dict` module implement the Behaviour defined by the @@ -276,13 +276,13 @@ create a couple of functions that create dictionaries for each type we want to test. The first we want to time is the Signature Wrapper, so `dict` vs `ec_dict` called as a Signature. - ```erlang - create_dict() -> - lists:foldl(fun(El, Dict) -> - dict:store(El, El, Dict) +```erlang +create_dict() -> + lists:foldl(fun(El, Dict) -> + dict:store(El, El, Dict) end, dict:new(), lists:seq(1,100)). - ``` +``` The only thing we do here is create a sequence of numbers 1 to 100, and then add each of those to the `dict` as an entry. We aren't too @@ -293,14 +293,14 @@ of the dictionaries themselves. We need to create a similar function for our Signature based dictionary `ec_dict`. - ```erlang - create_dictionary(Type) -> - lists:foldl(fun(El, Dict) -> - ec_dictionary:add(El, El, Dict) +```erlang +create_dictionary(Type) -> + lists:foldl(fun(El, Dict) -> + ec_dictionary:add(El, El, Dict) end, ec_dictionary:new(Type), lists:seq(1,100)). - ``` +``` Here we actually create everything using the Signature. So we don't need one function for each type. We can have one function that can @@ -313,17 +313,17 @@ data and one that returns data, just to get good coverage. For our dictionaries we are going to use the `size` function as well as the `add` function. - ```erlang - time_direct_vs_signature_dict() -> - io:format("Timing dict~n"), - Dict = create_dict(), - test_avg(fun() -> +```erlang +time_direct_vs_signature_dict() -> + io:format("Timing dict~n"), + Dict = create_dict(), + test_avg(fun() -> dict:size(dict:store(some_key, some_value, Dict)) end, 1000000), - io:format("Timing ec_dict implementation of ec_dictionary~n"), - time_dict_type(ec_dict). - ``` + io:format("Timing ec_dict implementation of ec_dictionary~n"), + time_dict_type(ec_dict). +``` The `test_avg` function runs the provided function the number of times specified in the second argument and collects timing information. We @@ -335,15 +335,15 @@ we don't have to hard code the calls for the Signature implementations. Lets take a look at the `time_dict_type` function. - ```erlang - time_dict_type(Type) -> - io:format("Testing ~p~n", [Type]), - Dict = create_dictionary(Type), - test_avg(fun() -> - ec_dictionary:size(ec_dictionary:add(some_key, some_value, Dict)) - end, - 1000000). - ``` +```erlang +time_dict_type(Type) -> + io:format("Testing ~p~n", [Type]), + Dict = create_dictionary(Type), + test_avg(fun() -> + ec_dictionary:size(ec_dictionary:add(some_key, some_value, Dict)) + end, + 1000000). +``` As you can see we take the type as an argument (we need it for `dict` creation) and call our create function. Then we run the same timings @@ -356,23 +356,23 @@ work for anything that implements that Signature. So we have our tests, what was the result. Well on my laptop this is what it looked like. - ```sh - Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] +```sh +Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] - Eshell V5.8.2 (abort with ^G) +Eshell V5.8.2 (abort with ^G) - 1> ec_timing:time_direct_vs_signature_dict(). - Timing dict - Range: 2 - 5621 mics - Median: 3 mics - Average: 3 mics - Timing ec_dict implementation of ec_dictionary - Testing ec_dict - Range: 3 - 6097 mics - Median: 3 mics - Average: 4 mics - 2> - ``` +1> ec_timing:time_direct_vs_signature_dict(). +Timing dict +Range: 2 - 5621 mics +Median: 3 mics +Average: 3 mics +Timing ec_dict implementation of ec_dictionary +Testing ec_dict +Range: 3 - 6097 mics +Median: 3 mics +Average: 4 mics +2> +``` So for the direct `dict` call, we average about 3 mics per call, while for the Signature Wrapper we average around 4. That's a 25% cost for @@ -387,13 +387,13 @@ Signature, but it is not a Signature Wrapper. It is a native implementation of the Signature. To use `ec_rbdict` directly we have to create a creation helper just like we did for dict. - ```erlang - create_rbdict() -> - lists:foldl(fun(El, Dict) -> - ec_rbdict:add(El, El, Dict) +```erlang +create_rbdict() -> + lists:foldl(fun(El, Dict) -> + ec_rbdict:add(El, El, Dict) end, ec_rbdict:new(), lists:seq(1,100)). - ``` +``` This is exactly the same as `create_dict` with the exception that dict is replaced by `ec_rbdict`. @@ -402,17 +402,17 @@ The timing function itself looks very similar as well. Again notice that we have to hard code the concrete name for the concrete implementation, but we don't for the `ec_dictionary` test. - ```erlang - time_direct_vs_signature_rbdict() -> - io:format("Timing rbdict~n"), - Dict = create_rbdict(), - test_avg(fun() -> - ec_rbdict:size(ec_rbdict:add(some_key, some_value, Dict)) - end, - 1000000), - io:format("Timing ec_dict implementation of ec_dictionary~n"), - time_dict_type(ec_rbdict). - ``` +```erlang +time_direct_vs_signature_rbdict() -> + io:format("Timing rbdict~n"), + Dict = create_rbdict(), + test_avg(fun() -> + ec_rbdict:size(ec_rbdict:add(some_key, some_value, Dict)) + end, + 1000000), + io:format("Timing ec_dict implementation of ec_dictionary~n"), + time_dict_type(ec_rbdict). +``` And there we have our test. What do the results look like? @@ -422,23 +422,23 @@ The main thing we are timing here is the additional cost of the dictionary Signature itself. Keep that in mind as we look at the results. - ```sh - Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] +```sh +Erlang R14B01 (erts-5.8.2) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:false] - Eshell V5.8.2 (abort with ^G) +Eshell V5.8.2 (abort with ^G) - 1> ec_timing:time_direct_vs_signature_rbdict(). - Timing rbdict - Range: 6 - 15070 mics - Median: 7 mics - Average: 7 mics - Timing ec_dict implementation of ec_dictionary - Testing ec_rbdict - Range: 6 - 6013 mics - Median: 7 mics - Average: 7 mics - 2> - ``` +1> ec_timing:time_direct_vs_signature_rbdict(). +Timing rbdict +Range: 6 - 15070 mics +Median: 7 mics +Average: 7 mics +Timing ec_dict implementation of ec_dictionary +Testing ec_rbdict +Range: 6 - 6013 mics +Median: 7 mics +Average: 7 mics +2> +``` So no difference it time. Well the reality is that there is a difference in timing, there must be, but we don't have enough From 68e9bbcd0fd782eb906932de9654a9a29d8a786b Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 21:00:25 +0100 Subject: [PATCH 39/70] Typo in math mode --- doc/signatures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/signatures.md b/doc/signatures.md index 3e32c11..07db33a 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -19,7 +19,7 @@ implement a dictionary. Just a few are: Each of these approaches has their own performance characteristics, memory footprints, etc. For example, a table of size $n$ with open addressing has no collisions and holds up to $n$ elements, with a single -comparison for successful lookup, and a table of size $n$_ with chaining +comparison for successful lookup, and a table of size $n$ with chaining and $k$ keys has the minimum $\max(0, k-n)$ collisions and $\mathcal{O}(1 + k/n)$ comparisons for lookup. While for skip lists the performance characteristics are about as good as that of randomly-built binary From 7e69d4949e1615b9fd15c27d2597765dbeb39615 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 21:19:24 +0100 Subject: [PATCH 40/70] Replaced tab by four spaces --- doc/signatures.md | 118 +++++++++++++++++++++++----------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/doc/signatures.md b/doc/signatures.md index 07db33a..4ad716f 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -41,7 +41,7 @@ it is going to be called on. ```erlang add(ModuleToUse, Key, Value, DictData) -> - ModuleToUse:add(Key, Value, DictData). + ModuleToUse:add(Key, Value, DictData). ``` This works, and you can vary how you want to pass the data. For @@ -51,7 +51,7 @@ bit cleaner. ```erlang add(Key, Value, {ModuleToUse, DictData}) -> - ModuleToUse:add(Key, Value, DictData). + ModuleToUse:add(Key, Value, DictData). ``` Either way, there are a few problems with this approach. One of the @@ -80,7 +80,7 @@ So what we actually want to do is something mole like this: ```erlang add(Key, Value, DictData) -> - dictionary:add(Key, Value, DictData). + dictionary:add(Key, Value, DictData). ``` Doing this we retain the locality. We can easily look up the @@ -107,18 +107,18 @@ Behaviour for dictionaries. That Behaviour looks like this: -export([behaviour_info/1]). behaviour_info(callbacks) -> - [{new, 0}, - {has_key, 2}, - {get, 2}, - {add, 3}, - {remove, 2}, - {has_value, 2}, - {size, 1}, - {to_list, 1}, - {from_list, 1}, - {keys, 1}]; + [{new, 0}, + {has_key, 2}, + {get, 2}, + {add, 3}, + {remove, 2}, + {has_value, 2}, + {size, 1}, + {to_list, 1}, + {from_list, 1}, + {keys, 1}]; behaviour_info(_) -> - undefined. + undefined. ``` @@ -136,7 +136,7 @@ behaviour, starting with `new/1`. %% @param ModuleName The module name. -spec new(module()) -> dictionary(_K, _V). new(ModuleName) when is_atom(ModuleName) -> - #dict_t{callback = ModuleName, data = ModuleName:new()}. + #dict_t{callback = ModuleName, data = ModuleName:new()}. ``` This code creates a new dictionary for us. Or to be more specific it @@ -163,7 +163,7 @@ adding a value. %% @param Value the value to add -spec add(key(K), value(V), dictionary(K, V)) -> dictionary(K, V). add(Key, Value, #dict_t{callback = Mod, data = Data} = Dict) -> - Dict#dict_t{data = Mod:add(Key, Value, Data)}. + Dict#dict_t{data = Mod:add(Key, Value, Data)}. ``` There are two key things here. @@ -189,7 +189,7 @@ of the dictionary Signature. %% @throws not_found when the key does not exist -spec get(key(K), dictionary(K, V)) -> value(V). get(Key, #dict_t{callback = Mod, data = Data}) -> - Mod:get(Key, Data). + Mod:get(Key, Data). ``` In this case, you can see a very similar approach to deconstructing @@ -246,14 +246,14 @@ translation needs to be done. We do that in the `ec_dict:get/2` function. ```erlang -spec get(ec_dictionary:key(K), Object::dictionary(K, V)) -> - ec_dictionary:value(V). + ec_dictionary:value(V). get(Key, Data) -> - case dict:find(Key, Data) of - {ok, Value} -> - Value; - error -> - throw(not_found) - end. + case dict:find(Key, Data) of + {ok, Value} -> + Value; + error -> + throw(not_found) + end. ``` So the `ec_dict` module's purpose for existence is to help the @@ -278,10 +278,10 @@ want to test. The first we want to time is the Signature Wrapper, so ```erlang create_dict() -> - lists:foldl(fun(El, Dict) -> - dict:store(El, El, Dict) - end, dict:new(), - lists:seq(1,100)). + lists:foldl(fun(El, Dict) -> + dict:store(El, El, Dict) + end, dict:new(), + lists:seq(1,100)). ``` The only thing we do here is create a sequence of numbers 1 to 100, @@ -295,11 +295,11 @@ dictionary `ec_dict`. ```erlang create_dictionary(Type) -> - lists:foldl(fun(El, Dict) -> - ec_dictionary:add(El, El, Dict) - end, - ec_dictionary:new(Type), - lists:seq(1,100)). + lists:foldl(fun(El, Dict) -> + ec_dictionary:add(El, El, Dict) + end, + ec_dictionary:new(Type), + lists:seq(1,100)). ``` Here we actually create everything using the Signature. So we don't @@ -315,14 +315,14 @@ the `add` function. ```erlang time_direct_vs_signature_dict() -> - io:format("Timing dict~n"), - Dict = create_dict(), - test_avg(fun() -> - dict:size(dict:store(some_key, some_value, Dict)) - end, - 1000000), - io:format("Timing ec_dict implementation of ec_dictionary~n"), - time_dict_type(ec_dict). + io:format("Timing dict~n"), + Dict = create_dict(), + test_avg(fun() -> + dict:size(dict:store(some_key, some_value, Dict)) + end, + 1000000), + io:format("Timing ec_dict implementation of ec_dictionary~n"), + time_dict_type(ec_dict). ``` The `test_avg` function runs the provided function the number of times @@ -337,12 +337,12 @@ implementations. Lets take a look at the `time_dict_type` function. ```erlang time_dict_type(Type) -> - io:format("Testing ~p~n", [Type]), - Dict = create_dictionary(Type), - test_avg(fun() -> - ec_dictionary:size(ec_dictionary:add(some_key, some_value, Dict)) - end, - 1000000). + io:format("Testing ~p~n", [Type]), + Dict = create_dictionary(Type), + test_avg(fun() -> + ec_dictionary:size(ec_dictionary:add(some_key, some_value, Dict)) + end, + 1000000). ``` As you can see we take the type as an argument (we need it for `dict` @@ -389,10 +389,10 @@ to create a creation helper just like we did for dict. ```erlang create_rbdict() -> - lists:foldl(fun(El, Dict) -> - ec_rbdict:add(El, El, Dict) - end, ec_rbdict:new(), - lists:seq(1,100)). + lists:foldl(fun(El, Dict) -> + ec_rbdict:add(El, El, Dict) + end, ec_rbdict:new(), + lists:seq(1,100)). ``` This is exactly the same as `create_dict` with the exception that dict @@ -404,14 +404,14 @@ implementation, but we don't for the `ec_dictionary` test. ```erlang time_direct_vs_signature_rbdict() -> - io:format("Timing rbdict~n"), - Dict = create_rbdict(), - test_avg(fun() -> - ec_rbdict:size(ec_rbdict:add(some_key, some_value, Dict)) - end, - 1000000), - io:format("Timing ec_dict implementation of ec_dictionary~n"), - time_dict_type(ec_rbdict). + io:format("Timing rbdict~n"), + Dict = create_rbdict(), + test_avg(fun() -> + ec_rbdict:size(ec_rbdict:add(some_key, some_value, Dict)) + end, + 1000000), + io:format("Timing ec_dict implementation of ec_dictionary~n"), + time_dict_type(ec_rbdict). ``` And there we have our test. What do the results look like? From 6e9c1b0a22064d52231e65064d91d748a025e4d5 Mon Sep 17 00:00:00 2001 From: Ariel Date: Mon, 18 Dec 2023 21:26:06 +0100 Subject: [PATCH 41/70] Another typo in math mode --- doc/signatures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/signatures.md b/doc/signatures.md index 4ad716f..cc5fd32 100644 --- a/doc/signatures.md +++ b/doc/signatures.md @@ -23,7 +23,7 @@ comparison for successful lookup, and a table of size $n$ with chaining and $k$ keys has the minimum $\max(0, k-n)$ collisions and $\mathcal{O}(1 + k/n)$ comparisons for lookup. While for skip lists the performance characteristics are about as good as that of randomly-built binary -search trees - namely ($\mathcal{O}(\log n)$). So the choice of which to select +search trees - namely $\mathcal{O}(\log n)$. So the choice of which to select depends very much on memory available, insert/read characteristics, etc. So delegating the choice to a single point in your code is a very good idea. Unfortunately, in Erlang that's so easy to do at the moment. From bbdbbf313f120f5dee841d73b64c408c462cd3ef Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Wed, 20 Dec 2023 23:56:27 +0100 Subject: [PATCH 42/70] Typos, and replaced 'checksum' with 'digest': more consistent with Erlang documentation --- src/ec_file.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index e310336..d3498b8 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -139,14 +139,14 @@ try_write_owner(To, #file_info{uid=OwnerId}) -> try_write_group(To, #file_info{gid=OwnerId}) -> file:write_file_info(To, #file_info{gid=OwnerId}). -%% @doc return an md5 checksum string or a binary. Same as unix utility of -%% same name. +%% @doc return the MD5 digest of a string or a binary, +%% named after the UNIX utility. -spec md5sum(string() | binary()) -> string(). md5sum(Value) -> hex(binary_to_list(erlang:md5(Value))). -%% @doc return an sha1sum checksum string or a binary. Same as unix utility of -%% same name. +%% @doc return the SHA-1 digest of a string or a binary, +%% named after the UNIX utility. -ifdef(deprecated_crypto). -spec sha1sum(string() | binary()) -> string(). sha1sum(Value) -> From 6781f1ba6a5ba48dc07b4f727d1420199fecebd5 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Wed, 20 Dec 2023 23:58:44 +0100 Subject: [PATCH 43/70] Factorized digest-to-hex transform; used in three functions --- src/ec_file.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index d3498b8..c5bbb91 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -143,20 +143,23 @@ try_write_group(To, #file_info{gid=OwnerId}) -> %% named after the UNIX utility. -spec md5sum(string() | binary()) -> string(). md5sum(Value) -> - hex(binary_to_list(erlang:md5(Value))). + turn_digest_into_hex(erlang:md5(Value)). %% @doc return the SHA-1 digest of a string or a binary, %% named after the UNIX utility. -ifdef(deprecated_crypto). -spec sha1sum(string() | binary()) -> string(). sha1sum(Value) -> - hex(binary_to_list(crypto:sha(Value))). + turn_digest_into_hex(crypto:sha(Value)). -else. -spec sha1sum(string() | binary()) -> string(). sha1sum(Value) -> - hex(binary_to_list(crypto:hash(sha, Value))). + turn_digest_into_hex(crypto:hash(sha, Value)). -endif. +turn_digest_into_hex(Digest) -> + hex(binary_to_list(Digest)). + %% @doc delete a file. Use the recursive option for directories. %%
 %% Example: remove("./tmp_dir", [recursive]).

From fc69b3630cc6a4af074039cf54f21eae4660f2ac Mon Sep 17 00:00:00 2001
From: Ariel Otilibili 
Date: Thu, 21 Dec 2023 08:43:36 +0100
Subject: [PATCH 44/70] turn_digest_into_hex/1 renamed as bin_to_hex/1

---
 src/ec_file.erl | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/ec_file.erl b/src/ec_file.erl
index c5bbb91..76e10fe 100644
--- a/src/ec_file.erl
+++ b/src/ec_file.erl
@@ -143,22 +143,22 @@ try_write_group(To, #file_info{gid=OwnerId}) ->
 %%      named after the UNIX utility.
 -spec md5sum(string() | binary()) -> string().
 md5sum(Value) ->
-    turn_digest_into_hex(erlang:md5(Value)).
+    bin_to_hex(erlang:md5(Value)).
 
 %% @doc return the SHA-1 digest of a string or a binary,
 %%      named after the UNIX utility.
 -ifdef(deprecated_crypto).
 -spec sha1sum(string() | binary()) -> string().
 sha1sum(Value) ->
-    turn_digest_into_hex(crypto:sha(Value)).
+    bin_to_hex(crypto:sha(Value)).
 -else.
 -spec sha1sum(string() | binary()) -> string().
 sha1sum(Value) ->
-    turn_digest_into_hex(crypto:hash(sha, Value)).
+    bin_to_hex(crypto:hash(sha, Value)).
 -endif.
 
-turn_digest_into_hex(Digest) ->
-    hex(binary_to_list(Digest)).
+bin_to_hex(Bin) ->
+    hex(binary_to_list(Bin)).
 
 %% @doc delete a file. Use the recursive option for directories.
 %% 

From 685f08621b91f4286057786bd7a8be361f1c5cbe Mon Sep 17 00:00:00 2001
From: Ariel Otilibili 
Date: Fri, 12 Jan 2024 19:46:27 +0100
Subject: [PATCH 45/70] Removed conditions on deprecated cryptos and rand
 modoles

* CI/CD lowest OTP version is 19.3
* from 19.3, `rand` module exists (https://www.erlang.org/docs/19/man/rand)
* from 19.3, `crypto:hash/1` exists (https://www.erlang.org/docs/19/man/crypto).
---
 rebar.config    | 9 +++------
 src/ec_file.erl | 8 +-------
 2 files changed, 4 insertions(+), 13 deletions(-)

diff --git a/rebar.config b/rebar.config
index e269132..97e689c 100644
--- a/rebar.config
+++ b/rebar.config
@@ -9,12 +9,9 @@
 
 %% Compiler Options ============================================================
 {erl_opts,
- [{platform_define, "^[0-9]+", namespaced_types},
-  {platform_define, "^[0-9]+", have_callback_support},
-  {platform_define, "^R1[4|5]", deprecated_crypto},
-  {platform_define, "^1[8|9]", rand_module},
-  {platform_define, "^2", rand_module},
-  {platform_define, "^2", unicode_str},
+ [{platform_define, "^[0-9]+",   namespaced_types},
+  {platform_define, "^[0-9]+",   have_callback_support},
+  {platform_define, "^2",        unicode_str},
   {platform_define, "^(R|1|20)", fun_stacktrace},
   debug_info,
   warnings_as_errors]}.
diff --git a/src/ec_file.erl b/src/ec_file.erl
index 76e10fe..7ace960 100644
--- a/src/ec_file.erl
+++ b/src/ec_file.erl
@@ -143,19 +143,13 @@ try_write_group(To, #file_info{gid=OwnerId}) ->
 %%      named after the UNIX utility.
 -spec md5sum(string() | binary()) -> string().
 md5sum(Value) ->
-    bin_to_hex(erlang:md5(Value)).
+    bin_to_hex(crypto:hash(md5, Value)).
 
 %% @doc return the SHA-1 digest of a string or a binary,
 %%      named after the UNIX utility.
--ifdef(deprecated_crypto).
--spec sha1sum(string() | binary()) -> string().
-sha1sum(Value) ->
-    bin_to_hex(crypto:sha(Value)).
--else.
 -spec sha1sum(string() | binary()) -> string().
 sha1sum(Value) ->
     bin_to_hex(crypto:hash(sha, Value)).
--endif.
 
 bin_to_hex(Bin) ->
     hex(binary_to_list(Bin)).

From 5118421f6ff1b358a3b76b7dfb02b81645386e31 Mon Sep 17 00:00:00 2001
From: Ariel Otilibili 
Date: Fri, 12 Jan 2024 19:50:54 +0100
Subject: [PATCH 46/70] Removed checks on deprecated random module

---
 src/ec_file.erl | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/src/ec_file.erl b/src/ec_file.erl
index 7ace960..cc3d1c4 100644
--- a/src/ec_file.erl
+++ b/src/ec_file.erl
@@ -376,14 +376,8 @@ sub_files(From) ->
     {ok, SubFiles} = file:list_dir(From),
     [filename:join(From, SubFile) || SubFile <- SubFiles].
 
--ifdef(rand_module).
 random_uniform() ->
     rand:uniform().
--else.
-random_uniform() ->
-    random:seed(os:timestamp()),
-    random:uniform().
--endif.
 
 %%%===================================================================
 %%% Test Functions

From eca2d2129c52d6cc777390d8944e5ed0dab28807 Mon Sep 17 00:00:00 2001
From: Ariel Otilibili 
Date: Sun, 4 Feb 2024 12:29:03 +0100
Subject: [PATCH 47/70] Removed have_callback_support

* introduced by 95f723e1e043eaa948807c9e5572d9f948ad3585
* mentioned as well in 47bcbd49b6fdef020cf190c20a13856dd86c0577
* introduced for compatibility with OTP R14
* CI/CD now tests from R19 and onwards.
---
 rebar.config          |  1 -
 src/ec_dictionary.erl | 23 -----------------------
 src/ec_vsn.erl        | 15 ---------------
 3 files changed, 39 deletions(-)

diff --git a/rebar.config b/rebar.config
index 97e689c..dcdc11e 100644
--- a/rebar.config
+++ b/rebar.config
@@ -10,7 +10,6 @@
 %% Compiler Options ============================================================
 {erl_opts,
  [{platform_define, "^[0-9]+",   namespaced_types},
-  {platform_define, "^[0-9]+",   have_callback_support},
   {platform_define, "^2",        unicode_str},
   {platform_define, "^(R|1|20)", fun_stacktrace},
   debug_info,
diff --git a/src/ec_dictionary.erl b/src/ec_dictionary.erl
index 423914a..ea7fdc9 100644
--- a/src/ec_dictionary.erl
+++ b/src/ec_dictionary.erl
@@ -42,8 +42,6 @@
 -type key(T) :: T.
 -type value(T) :: T.
 
--ifdef(have_callback_support).
-
 -callback new() -> any().
 -callback has_key(key(any()), any()) -> boolean().
 -callback get(key(any()), any()) -> any().
@@ -55,27 +53,6 @@
 -callback from_list([{key(any()), value(any())}]) -> any().
 -callback keys(any()) -> [key(any())].
 
--else.
-
-%% In the case where R14 or lower is being used to compile the system
-%% we need to export a behaviour info
--export([behaviour_info/1]).
--spec behaviour_info(atom()) -> [{atom(), arity()}] | undefined.
-behaviour_info(callbacks) ->
-    [{new, 0},
-     {has_key, 2},
-     {get, 2},
-     {add, 3},
-     {remove, 2},
-     {has_value, 2},
-     {size, 1},
-     {to_list, 1},
-     {from_list, 1},
-     {keys, 1}];
-behaviour_info(_Other) ->
-    undefined.
--endif.
-
 %%%===================================================================
 %%% API
 %%%===================================================================
diff --git a/src/ec_vsn.erl b/src/ec_vsn.erl
index 2f38090..e407b9f 100644
--- a/src/ec_vsn.erl
+++ b/src/ec_vsn.erl
@@ -27,24 +27,9 @@
 %% however you should not rely on the internal representation here
 -type t() :: #t{}.
 
--ifdef(have_callback_support).
-
 -callback new() -> any().
 -callback vsn(any()) -> {ok, string()} | {error, Reason::any()}.
 
--else.
-
-%% In the case where R14 or lower is being used to compile the system
-%% we need to export a behaviour info
--export([behaviour_info/1]).
--spec behaviour_info(atom()) -> [{atom(), arity()}] | undefined.
-behaviour_info(callbacks) ->
-    [{new, 0},
-     {vsn, 1}];
-behaviour_info(_Other) ->
-    undefined.
--endif.
-
 %%%===================================================================
 %%% API
 %%%===================================================================

From 2286a6ed9b9a9b2870ec2cbb5a304c8e9334c5d5 Mon Sep 17 00:00:00 2001
From: Ariel Otilibili 
Date: Sat, 17 Feb 2024 21:59:32 +0100
Subject: [PATCH 48/70] Removed namespaced_types

* introduced for handling deprecated types existing before R17
* introduced in 523a66ad7448cf4b49c6b803f1198e5b3e2c7960
* CI/CD handles R19 up to R24
* R19 and onwards have dict:dict/0 [1,2]

[1] https://www.erlang.org/docs/19/man/dict
[2] https://www.erlang.org/docs/24/man/dict#type-dict
---
 rebar.config      |  3 +--
 src/ec_dict.erl   |  4 ----
 src/ec_plists.erl | 14 --------------
 3 files changed, 1 insertion(+), 20 deletions(-)

diff --git a/rebar.config b/rebar.config
index dcdc11e..aecc6a1 100644
--- a/rebar.config
+++ b/rebar.config
@@ -9,8 +9,7 @@
 
 %% Compiler Options ============================================================
 {erl_opts,
- [{platform_define, "^[0-9]+",   namespaced_types},
-  {platform_define, "^2",        unicode_str},
+ [{platform_define, "^2",        unicode_str},
   {platform_define, "^(R|1|20)", fun_stacktrace},
   debug_info,
   warnings_as_errors]}.
diff --git a/src/ec_dict.erl b/src/ec_dict.erl
index 0c0b998..3e9418e 100644
--- a/src/ec_dict.erl
+++ b/src/ec_dict.erl
@@ -34,11 +34,7 @@
 %%%===================================================================
 %% This should be opaque, but that kills dialyzer so for now we export it
 %% however you should not rely on the internal representation here
--ifdef(namespaced_types).
 -type dictionary(_K, _V) :: dict:dict().
--else.
--type dictionary(_K, _V) :: dict().
--endif.
 
 %%%===================================================================
 %%% API
diff --git a/src/ec_plists.erl b/src/ec_plists.erl
index 846049d..cd9b915 100644
--- a/src/ec_plists.erl
+++ b/src/ec_plists.erl
@@ -480,16 +480,9 @@ usort(Fun, List, Malt) ->
     runmany(Fun2, {recursive, Fuse}, List, Malt).
 
 %% @doc Like below, assumes default MapMalt of 1.
--ifdef(namespaced_types).
 -spec mapreduce(MapFunc, list()) -> dict:dict() when
       MapFunc ::  fun((term()) -> DeepListOfKeyValuePairs),
       DeepListOfKeyValuePairs :: [DeepListOfKeyValuePairs] | {Key::term(), Value::term()}.
--else.
--spec mapreduce(MapFunc, list()) -> dict() when
-      MapFunc ::  fun((term()) -> DeepListOfKeyValuePairs),
-      DeepListOfKeyValuePairs :: [DeepListOfKeyValuePairs] | {Key::term(), Value::term()}.
--endif.
-
 
 mapreduce(MapFunc, List) ->
     mapreduce(MapFunc, List, 1).
@@ -518,17 +511,10 @@ mapreduce(MapFunc, List, MapMalt) ->
 %%
 %% mapreduce requires OTP R11B, or it may leave monitoring messages in the
 %% message queue.
--ifdef(namespaced_types).
 -spec mapreduce(MapFunc, list(), InitState::term(), ReduceFunc, malt()) -> dict:dict() when
       MapFunc :: fun((term()) -> DeepListOfKeyValuePairs),
       DeepListOfKeyValuePairs :: [DeepListOfKeyValuePairs] | {Key::term(), Value::term()},
       ReduceFunc :: fun((OldState::term(), Key::term(), Value::term()) -> NewState::term()).
--else.
--spec mapreduce(MapFunc, list(), InitState::term(), ReduceFunc, malt()) -> dict() when
-      MapFunc :: fun((term()) -> DeepListOfKeyValuePairs),
-      DeepListOfKeyValuePairs :: [DeepListOfKeyValuePairs] | {Key::term(), Value::term()},
-      ReduceFunc :: fun((OldState::term(), Key::term(), Value::term()) -> NewState::term()).
--endif.
 mapreduce(MapFunc, List, InitState, ReduceFunc, MapMalt) ->
     Parent = self(),
     {Reducer, ReducerRef} =

From 17e6f890783c18381fc39ad48779b3d09e7f0ffb Mon Sep 17 00:00:00 2001
From: Ariel Otilibili 
Date: Tue, 20 Feb 2024 23:25:04 +0100
Subject: [PATCH 49/70] Bumped OTP versions

* aligned OTP versions with Debian
* keeps versions from bullseye (old stable) to sid (unstable).

```
$ curl --silent https://packages.debian.org/search?keywords=erlang | \
  sed -ne '/Package erlang/{/<\/a>/p; /br/p}' | \
  sed -e 's/\n/$1/g; s/
([^\s]+)/$1/g; s/\+.*(?=\n)//g' | \ sed -e '1i name otp-version' | \ column -t name otp-version buster 1:22.2.7 bullseye 1:23.2.6 bookworm 1:25.2.3 trixie 1:25.3.2.8 sid 1:25.3.2.8 experimental 1:26.2.1 $ curl -I https://packages.debian.org/search?keywords=erlang HTTP/2 200 date: Tue, 20 Feb 2024 22:21:53 GMT server: Apache last-modified: Tue, 20 Feb 2024 21:50:40 GMT vary: Accept-Encoding,negotiate,accept-language x-clacks-overhead: GNU Terry Pratchett expires: Wed, 21 Feb 2024 09:04:02 +0000 x-content-type-options: nosniff x-frame-options: sameorigin referrer-policy: no-referrer x-xss-protection: 1 permissions-policy: interest-cohort=() strict-transport-security: max-age=15552000 age: 1873 content-length: 184625 content-type: text/html; charset=UTF-8 ``` --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4b80ea1..2472afd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - otp_version: ['24.0', '23.3', '22.3', '21.3', '20.3', '19.3'] + otp_version: ['25.3', '25.2', '23.2'] os: [ubuntu-latest] steps: From c0a02892cdbd6f64a88d5415e873f00a75423be8 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Sun, 25 Feb 2024 13:22:43 +0100 Subject: [PATCH 50/70] Removed fun_stacktrace * introduced in ad2d57d8b62b86ec1f3d03441f7defa595f2c59c * CI/CD uses R23 and onwards * erlang:get_stacktrace/0 removed in R23. [1] https://www.erlang.org/doc/general_info/removed#functions-removed-in-otp-23 --- rebar.config | 6 +----- src/ec_plists.erl | 16 ---------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/rebar.config b/rebar.config index aecc6a1..017e3fe 100644 --- a/rebar.config +++ b/rebar.config @@ -8,11 +8,7 @@ {erl_first_files, ["ec_dictionary", "ec_vsn"]}. %% Compiler Options ============================================================ -{erl_opts, - [{platform_define, "^2", unicode_str}, - {platform_define, "^(R|1|20)", fun_stacktrace}, - debug_info, - warnings_as_errors]}. +{erl_opts, [debug_info, warnings_as_errors]}. %% EUnit ======================================================================= {eunit_opts, [verbose, diff --git a/src/ec_plists.erl b/src/ec_plists.erl index cd9b915..221075b 100644 --- a/src/ec_plists.erl +++ b/src/ec_plists.erl @@ -858,21 +858,6 @@ cluster_runmany(_, _, [_Non|_Empty], []=_Nodes, []=_Running, _) -> %% We have data, but no nodes either available or occupied erlang:exit(allnodescrashed). --ifdef(fun_stacktrace). -runmany_wrap(Fun, Parent) -> - try - Fun() - catch - exit:siblingdied -> - ok; - exit:Reason -> - Parent ! {erlang:self(), error, Reason}; - error:R -> - Parent ! {erlang:self(), error, {R, erlang:get_stacktrace()}}; - throw:R -> - Parent ! {erlang:self(), error, {{nocatch, R}, erlang:get_stacktrace()}} - end. --else. runmany_wrap(Fun, Parent) -> try Fun() @@ -886,7 +871,6 @@ runmany_wrap(Fun, Parent) -> throw:R:Stacktrace -> Parent ! {erlang:self(), error, {{nocatch, R}, Stacktrace}} end. --endif. delete_running(Pid, [{Pid, Node, List}|Running], Acc) -> {Running ++ Acc, Node, List}; From f378d3ec46c0b8df39827dce153adf5c5ba74167 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Sun, 25 Feb 2024 13:27:27 +0100 Subject: [PATCH 51/70] Removed unicode_str * introduced in f8f72b7cc5946131743f47454ee87494806deed8 * introduced for working around compile warning starting from R20 * CI/CD uses R23 and onwards. --- src/ec_date.erl | 8 +------- src/ec_git_vsn.erl | 15 ++------------- src/ec_talk.erl | 10 +--------- 3 files changed, 4 insertions(+), 29 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index c894e83..49266b1 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -101,7 +101,7 @@ parse(Date, Now) -> do_parse(Date, Now, []). do_parse(Date, Now, Opts) -> - case filter_hints(parse(tokenise(uppercase(Date), []), Now, Opts)) of + case filter_hints(parse(tokenise(string:uppercase(Date), []), Now, Opts)) of {error, bad_date} -> erlang:throw({?MODULE, {bad_date, Date}}); {D1, T1} = {{Y, M, D}, {H, M1, S}} @@ -709,12 +709,6 @@ pad6(X) when is_integer(X) -> ltoi(X) -> list_to_integer(X). --ifdef(unicode_str). -uppercase(Str) -> string:uppercase(Str). --else. -uppercase(Str) -> string:to_upper(Str). --endif. - %%%=================================================================== %%% Tests %%%=================================================================== diff --git a/src/ec_git_vsn.erl b/src/ec_git_vsn.erl index 8c2376f..d690d1a 100644 --- a/src/ec_git_vsn.erl +++ b/src/ec_git_vsn.erl @@ -94,22 +94,11 @@ parse_tags(Pattern) -> "fatal: " ++ _ -> {undefined, ""}; _ -> - Vsn = slice(Tag, len(Pattern)), - Vsn1 = trim(trim(Vsn, left, "v"), right, "\n"), + Vsn = string:slice(Tag, string:length(Pattern)), + Vsn1 = string:trim(string:trim(Vsn, leading, "v"), trailing, "\n"), {Tag, Vsn1} end. --ifdef(unicode_str). -len(Str) -> string:length(Str). -trim(Str, right, Chars) -> string:trim(Str, trailing, Chars); -trim(Str, left, Chars) -> string:trim(Str, leading, Chars). -slice(Str, Len) -> string:slice(Str, Len). --else. -len(Str) -> string:len(Str). -trim(Str, Dir, [Chars|_]) -> string:strip(Str, Dir, Chars). -slice(Str, Len) -> string:substr(Str, Len + 1). --endif. - -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). diff --git a/src/ec_talk.erl b/src/ec_talk.erl index 7cacf2a..9b9a71a 100644 --- a/src/ec_talk.erl +++ b/src/ec_talk.erl @@ -127,7 +127,7 @@ ask_convert(Prompt, TransFun, Type, Default) -> Default -> [" (", io_lib:format("~p", [Default]) , ")"] end, "> "])), - Data = trim(trim(io:get_line(NewPrompt)), both, [$\n]), + Data = string:trim(string:trim(io:get_line(NewPrompt)), both, [$\n]), Ret = TransFun(Data), case Ret of no_data -> @@ -197,14 +197,6 @@ get_string(String) -> no_clue end. --ifdef(unicode_str). -trim(Str) -> string:trim(Str). -trim(Str, both, Chars) -> string:trim(Str, both, Chars). --else. -trim(Str) -> string:strip(Str). -trim(Str, Dir, [Chars|_]) -> string:strip(Str, Dir, Chars). --endif. - %%%==================================================================== %%% tests %%%==================================================================== From 63b1798b1e7e10649d04cd04456cd8a1f39c6d78 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 18 Mar 2024 00:49:53 +0100 Subject: [PATCH 52/70] OTP bump * sequel of 17e6f890783c18381fc39ad48779b3d09e7f0ffb * added R26 in CI/CD, and cleared out dialyzer warnings * from R26, by default, `-Wno_unknown` suppresses warnings [1] * in R25, it was the reverse behavior: `-Wunknown` allows warnings [2]. [1] https://www.erlang.org/doc/man/dialyzer.html#warning_options [2] https://www.erlang.org/docs/25/man/dialyzer#format_warning-1 --- .github/workflows/main.yml | 2 +- rebar.config.script | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2472afd..aba4921 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - otp_version: ['25.3', '25.2', '23.2'] + otp_version: ['26.2', '25.3', '25.2', '23.2'] os: [ubuntu-latest] steps: diff --git a/rebar.config.script b/rebar.config.script index 9c1ed91..0f7c22b 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -10,8 +10,15 @@ Rebar2Deps = [ {cf, ".*", {git, "https://github.com/project-fifo/cf", {tag, "0.2.2"}}} ], +NoDialWarns = {dialyzer, [{warnings, [no_unknown]}]}, +OTPRelease = erlang:list_to_integer(erlang:system_info(otp_release)), +WarnsRemoved = case OTPRelease<26 of + true -> fun(Config) -> Config end; + false -> fun(Config) -> lists:keystore(dialyzer, 1, Config, NoDialWarns) end + end, + case IsRebar3 of - true -> CONFIG; + true -> WarnsRemoved(CONFIG); false -> - lists:keyreplace(deps, 1, CONFIG, {deps, Rebar2Deps}) + lists:keyreplace(deps, 1, WarnsRemoved(CONFIG), {deps, Rebar2Deps}) end. From f9ffd1ce6b7f179d4272305a88a332ff45c13c63 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Wed, 15 May 2024 22:20:43 +0200 Subject: [PATCH 53/70] Removed rebar2 case in rebar.config.script * introduced by 505d35996dc29bea806861dcb318b5624fb279c1 * rebar2 is now deprecated [1]. [1] https://github.com/rebar/rebar --- rebar.config.script | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/rebar.config.script b/rebar.config.script index 0f7c22b..cc054a8 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -1,24 +1,7 @@ -IsRebar3 = case application:get_key(rebar, vsn) of - {ok, Vsn} -> - [MajorVersion|_] = string:tokens(Vsn, "."), - (list_to_integer(MajorVersion) >= 3); - undefined -> - false - end, +NoDialWarns = {dialyzer, [{warnings, [no_unknown]}]}, +OTPRelease = erlang:list_to_integer(erlang:system_info(otp_release)), -Rebar2Deps = [ - {cf, ".*", {git, "https://github.com/project-fifo/cf", {tag, "0.2.2"}}} - ], - -NoDialWarns = {dialyzer, [{warnings, [no_unknown]}]}, -OTPRelease = erlang:list_to_integer(erlang:system_info(otp_release)), -WarnsRemoved = case OTPRelease<26 of - true -> fun(Config) -> Config end; - false -> fun(Config) -> lists:keystore(dialyzer, 1, Config, NoDialWarns) end - end, - -case IsRebar3 of - true -> WarnsRemoved(CONFIG); - false -> - lists:keyreplace(deps, 1, WarnsRemoved(CONFIG), {deps, Rebar2Deps}) +case OTPRelease<26 of + true -> CONFIG; + false -> lists:keystore(dialyzer, 1, CONFIG, NoDialWarns) end. From 2636b5e21d79f033e062609d70e5e83b86ab1d3d Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 27 May 2024 12:05:51 +0200 Subject: [PATCH 54/70] Renaming in GitHub actions --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aba4921..b79af5f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: EUnit +name: Integration tests on: pull_request: @@ -10,7 +10,7 @@ on: jobs: build: - name: Test on OTP ${{ matrix.otp_version }} and ${{ matrix.os }} + name: OTP ${{ matrix.otp_version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} container: image: erlang:${{matrix.otp_version}} @@ -25,7 +25,7 @@ jobs: - name: Compile run: rebar3 compile - - name: Dialyze + - name: Dialyzer run: rebar3 as test dialyzer - - name: EUnit tests + - name: EUnit run: TERM=xterm rebar3 eunit From ab7eb3874fecd3037e9527a2223a1deaf7f05f8a Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 27 May 2024 12:06:23 +0200 Subject: [PATCH 55/70] OTP bump ``` curl --silent https://packages.debian.org/search?keywords=erlang | \ sed -ne '/Package erlang/{/<\/a>/p; /br/p}' | \ sed -e 's/\n/$1/g; s/
([^\s]+)/$1/g; s/\+.*(?=\n)//g' | \ sed -e '1i name otp-version' | \ column -t name otp-version buster 1:22.2.7 bullseye 1:23.2.6 bookworm 1:25.2.3 trixie 1:25.3.2.12 sid 1:25.3.2.12 experimental 1:27.0 ``` --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b79af5f..2178442 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - otp_version: ['26.2', '25.3', '25.2', '23.2'] + otp_version: ['27.0', '25.3', '25.2', '23.2'] os: [ubuntu-latest] steps: From 5f40d8f06118d1a6db89b479e38e3f780bb3cdb4 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 3 Jun 2024 23:32:17 +0200 Subject: [PATCH 56/70] Resolved dialyzer warning on colorize/4 Fixes #173. --- src/ec_cmd_log.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index ba616f4..4eb83df 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -40,6 +40,7 @@ warn/3, log_level/1, atom_log_level/1, + colorize/4, format/1]). -include("ec_cmd_log.hrl"). From ccc1be32bebfe131e0aae60b7beb3e20d8aea2a7 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 3 Jun 2024 23:33:10 +0200 Subject: [PATCH 57/70] Changed type annotation Used 'intensity' in declaration of 'state_t'. --- src/ec_cmd_log.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index 4eb83df..b41443e 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -56,7 +56,7 @@ -record(state_t, {log_level=0 :: int_log_level(), caller=api :: caller(), - intensity=low :: none | low | high}). + intensity=low :: intensity()}). %%============================================================================ %% types From cdd9240142b433976486cb2524a5a4d141bb04ea Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Fri, 14 Jun 2024 21:42:36 +0200 Subject: [PATCH 58/70] Use thousand separators for large digits ``` $ git grep -nP '\d{4,}[^a-zA-Z\"]' | grep \.erl | grep -vi copyright > /dev/null; echo $? 0 ``` --- src/ec_date.erl | 78 ++++++++++++++++++++++++------------------------- src/ec_talk.erl | 6 ++-- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 49266b1..747b246 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -44,7 +44,7 @@ -define( is_month(X), ( (is_integer(X) andalso X =< 12) orelse ?is_hinted_month(X) ) ). -define( is_tz_offset(H1,H2,M1,M2), (?is_num(H1) andalso ?is_num(H2) andalso ?is_num(M1) andalso ?is_num(M2)) ). --define(GREGORIAN_SECONDS_1970, 62167219200). +-define(GREGORIAN_SECONDS_1970, 62_167_219_200). -define(ISO_8601_DATETIME_FORMAT, "Y-m-dTH:i:sZ"). -define(ISO_8601_DATETIME_WITH_MS_FORMAT, "Y-m-dTH:i:s.fZ"). @@ -54,7 +54,7 @@ -type hour() :: 0..23. -type minute() :: 0..59. -type second() :: 0..59. --type microsecond() :: 0..999999. +-type microsecond() :: 0..999_999. -type daynum() :: 1..7. -type date() :: {year(),month(),day()}. @@ -138,11 +138,11 @@ nparse(Date) -> {DateS, {H, M, S, Ms} } -> GSeconds = calendar:datetime_to_gregorian_seconds({DateS, {H, M, S} }), ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, - {ESeconds div 1000000, ESeconds rem 1000000, Ms}; + {ESeconds div 1_000_000, ESeconds rem 1_000_000, Ms}; DateTime -> GSeconds = calendar:datetime_to_gregorian_seconds(DateTime), ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, - {ESeconds div 1000000, ESeconds rem 1000000, 0} + {ESeconds div 1_000_000, ESeconds rem 1_000_000, 0} end. %% @@ -151,7 +151,7 @@ nparse(Date) -> parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $., Micros, $Z ], _Now, _Opts) when ?is_world_sep(X) - andalso (Micros >= 0 andalso Micros < 1000000) + andalso (Micros >= 0 andalso Micros < 1_000_000) andalso Year > 31 -> {{Year, Month, Day}, {hour(Hour, []), Min, Sec}, {Micros}}; @@ -162,7 +162,7 @@ parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $Z ], _Now, _Opts) parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $., Micros, $+, Off | _Rest ], _Now, _Opts) when (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso (Micros >= 0 andalso Micros < 1000000) + andalso (Micros >= 0 andalso Micros < 1_000_000) andalso Year > 31 -> {{Year, Month, Day}, {hour(Hour, []) - Off, Min, Sec}, {Micros}}; @@ -173,7 +173,7 @@ parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $+, Off | _Rest ], _Now, parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $., Micros, $-, Off | _Rest ], _Now, _Opts) when (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso (Micros >= 0 andalso Micros < 1000000) + andalso (Micros >= 0 andalso Micros < 1_000_000) andalso Year > 31 -> {{Year, Month, Day}, {hour(Hour, []) + Off, Min, Sec}, {Micros}}; @@ -316,11 +316,11 @@ tokenise([$., N1, N2, N3, N4 | Rest], Acc) when ?is_num(N1), ?is_num(N2), ?is_num(N3), ?is_num(N4) -> tokenise(Rest, [ ltoi([N1, N2, N3, N4]) * 100, $. | Acc]); tokenise([$., N1, N2, N3 | Rest], Acc) when ?is_num(N1), ?is_num(N2), ?is_num(N3) -> - tokenise(Rest, [ ltoi([N1, N2, N3]) * 1000, $. | Acc]); + tokenise(Rest, [ ltoi([N1, N2, N3]) * 1_000, $. | Acc]); tokenise([$., N1, N2 | Rest], Acc) when ?is_num(N1), ?is_num(N2) -> - tokenise(Rest, [ ltoi([N1, N2]) * 10000, $. | Acc]); + tokenise(Rest, [ ltoi([N1, N2]) * 10_000, $. | Acc]); tokenise([$., N1 | Rest], Acc) when ?is_num(N1) -> - tokenise(Rest, [ ltoi([N1]) * 100000, $. | Acc]); + tokenise(Rest, [ ltoi([N1]) * 100_000, $. | Acc]); tokenise([N1, N2, N3, N4, N5, N6 | Rest], Acc) when ?is_num(N1), ?is_num(N2), ?is_num(N3), ?is_num(N4), ?is_num(N5), ?is_num(N6) -> @@ -718,7 +718,7 @@ ltoi(X) -> -define(DATE, {{2001,3,10},{17,16,17}}). --define(DATEMS, {{2001,3,10},{17,16,17,123456}}). +-define(DATEMS, {{2001,3,10},{17,16,17,123_456}}). -define(DATE_NOON, {{2001,3,10},{12,0,0}}). -define(DATE_MIDNIGHT, {{2001,3,10},{0,0,0}}). -define(ISO, "o \\WW"). @@ -955,7 +955,7 @@ ms_test_() -> Now=os:timestamp(), [ ?_assertEqual({{2012,12,12}, {12,12,12,1234}}, parse("2012-12-12T12:12:12.001234")), - ?_assertEqual({{2012,12,12}, {12,12,12,123000}}, parse("2012-12-12T12:12:12.123")), + ?_assertEqual({{2012,12,12}, {12,12,12,123_000}}, parse("2012-12-12T12:12:12.123")), ?_assertEqual(format("H:m:s.f \\m \\i\\s \\m\\o\\n\\t\\h",?DATEMS), "17:03:17.123456 m is month"), ?_assertEqual(format("Y-m-d\\TH:i:s.f",?DATEMS), @@ -994,21 +994,21 @@ format_iso8601_test_() -> ?_assertEqual("2001-03-10T17:16:17.000000Z", format_iso8601({{2001,3,10},{17,16,17,0}})), ?_assertEqual("2001-03-10T17:16:17.100000Z", - format_iso8601({{2001,3,10},{17,16,17,100000}})), + format_iso8601({{2001,3,10},{17,16,17,100_000}})), ?_assertEqual("2001-03-10T17:16:17.120000Z", - format_iso8601({{2001,3,10},{17,16,17,120000}})), + format_iso8601({{2001,3,10},{17,16,17,120_000}})), ?_assertEqual("2001-03-10T17:16:17.123000Z", - format_iso8601({{2001,3,10},{17,16,17,123000}})), + format_iso8601({{2001,3,10},{17,16,17,123_000}})), ?_assertEqual("2001-03-10T17:16:17.123400Z", - format_iso8601({{2001,3,10},{17,16,17,123400}})), + format_iso8601({{2001,3,10},{17,16,17,123_400}})), ?_assertEqual("2001-03-10T17:16:17.123450Z", - format_iso8601({{2001,3,10},{17,16,17,123450}})), + format_iso8601({{2001,3,10},{17,16,17,123_450}})), ?_assertEqual("2001-03-10T17:16:17.123456Z", - format_iso8601({{2001,3,10},{17,16,17,123456}})), + format_iso8601({{2001,3,10},{17,16,17,123_456}})), ?_assertEqual("2001-03-10T17:16:17.023456Z", - format_iso8601({{2001,3,10},{17,16,17,23456}})), + format_iso8601({{2001,3,10},{17,16,17,23_456}})), ?_assertEqual("2001-03-10T17:16:17.003456Z", - format_iso8601({{2001,3,10},{17,16,17,3456}})), + format_iso8601({{2001,3,10},{17,16,17,3_456}})), ?_assertEqual("2001-03-10T17:16:17.000456Z", format_iso8601({{2001,3,10},{17,16,17,456}})), ?_assertEqual("2001-03-10T17:16:17.000056Z", @@ -1020,21 +1020,21 @@ format_iso8601_test_() -> ?_assertEqual("2001-03-10T07:16:17.000000Z", format_iso8601({{2001,3,10},{07,16,17,0}})), ?_assertEqual("2001-03-10T07:16:17.100000Z", - format_iso8601({{2001,3,10},{07,16,17,100000}})), + format_iso8601({{2001,3,10},{07,16,17,100_000}})), ?_assertEqual("2001-03-10T07:16:17.120000Z", - format_iso8601({{2001,3,10},{07,16,17,120000}})), + format_iso8601({{2001,3,10},{07,16,17,120_000}})), ?_assertEqual("2001-03-10T07:16:17.123000Z", - format_iso8601({{2001,3,10},{07,16,17,123000}})), + format_iso8601({{2001,3,10},{07,16,17,123_000}})), ?_assertEqual("2001-03-10T07:16:17.123400Z", - format_iso8601({{2001,3,10},{07,16,17,123400}})), + format_iso8601({{2001,3,10},{07,16,17,123_400}})), ?_assertEqual("2001-03-10T07:16:17.123450Z", - format_iso8601({{2001,3,10},{07,16,17,123450}})), + format_iso8601({{2001,3,10},{07,16,17,123_450}})), ?_assertEqual("2001-03-10T07:16:17.123456Z", - format_iso8601({{2001,3,10},{07,16,17,123456}})), + format_iso8601({{2001,3,10},{07,16,17,123_456}})), ?_assertEqual("2001-03-10T07:16:17.023456Z", - format_iso8601({{2001,3,10},{07,16,17,23456}})), + format_iso8601({{2001,3,10},{07,16,17,23_456}})), ?_assertEqual("2001-03-10T07:16:17.003456Z", - format_iso8601({{2001,3,10},{07,16,17,3456}})), + format_iso8601({{2001,3,10},{07,16,17,3_456}})), ?_assertEqual("2001-03-10T07:16:17.000456Z", format_iso8601({{2001,3,10},{07,16,17,456}})), ?_assertEqual("2001-03-10T07:16:17.000056Z", @@ -1051,31 +1051,31 @@ parse_iso8601_test_() -> parse("2001-03-10T17:16:17.000Z")), ?_assertEqual({{2001,3,10},{17,16,17,0}}, parse("2001-03-10T17:16:17.000000Z")), - ?_assertEqual({{2001,3,10},{17,16,17,100000}}, + ?_assertEqual({{2001,3,10},{17,16,17,100_000}}, parse("2001-03-10T17:16:17.1Z")), - ?_assertEqual({{2001,3,10},{17,16,17,120000}}, + ?_assertEqual({{2001,3,10},{17,16,17,120_000}}, parse("2001-03-10T17:16:17.12Z")), - ?_assertEqual({{2001,3,10},{17,16,17,123000}}, + ?_assertEqual({{2001,3,10},{17,16,17,123_000}}, parse("2001-03-10T17:16:17.123Z")), - ?_assertEqual({{2001,3,10},{17,16,17,123400}}, + ?_assertEqual({{2001,3,10},{17,16,17,123_400}}, parse("2001-03-10T17:16:17.1234Z")), - ?_assertEqual({{2001,3,10},{17,16,17,123450}}, + ?_assertEqual({{2001,3,10},{17,16,17,123_450}}, parse("2001-03-10T17:16:17.12345Z")), - ?_assertEqual({{2001,3,10},{17,16,17,123456}}, + ?_assertEqual({{2001,3,10},{17,16,17,123_456}}, parse("2001-03-10T17:16:17.123456Z")), - ?_assertEqual({{2001,3,10},{15,16,17,100000}}, + ?_assertEqual({{2001,3,10},{15,16,17,100_000}}, parse("2001-03-10T16:16:17.1+01:00")), - ?_assertEqual({{2001,3,10},{15,16,17,123456}}, + ?_assertEqual({{2001,3,10},{15,16,17,123_456}}, parse("2001-03-10T16:16:17.123456+01:00")), - ?_assertEqual({{2001,3,10},{17,16,17,100000}}, + ?_assertEqual({{2001,3,10},{17,16,17,100_000}}, parse("2001-03-10T16:16:17.1-01:00")), - ?_assertEqual({{2001,3,10},{17,16,17,123456}}, + ?_assertEqual({{2001,3,10},{17,16,17,123_456}}, parse("2001-03-10T16:16:17.123456-01:00")), ?_assertEqual({{2001,3,10},{17,16,17,456}}, parse("2001-03-10T17:16:17.000456Z")), - ?_assertEqual({{2001,3,10},{17,16,17,123000}}, + ?_assertEqual({{2001,3,10},{17,16,17,123_000}}, parse("2001-03-10T17:16:17.123000Z")) ]. diff --git a/src/ec_talk.erl b/src/ec_talk.erl index 9b9a71a..54d18f6 100644 --- a/src/ec_talk.erl +++ b/src/ec_talk.erl @@ -205,9 +205,9 @@ get_string(String) -> general_test_() -> [?_test(42 == get_integer("42")), - ?_test(500211 == get_integer("500211")), - ?_test(1234567890 == get_integer("1234567890")), - ?_test(12345678901234567890 == get_integer("12345678901234567890")), + ?_test(500_211 == get_integer("500211")), + ?_test(1_234_567_890 == get_integer("1234567890")), + ?_test(12_345_678_901_234_567_890 == get_integer("12345678901234567890")), ?_test(true == get_boolean("true")), ?_test(false == get_boolean("false")), ?_test(true == get_boolean("Ok")), From 15126e0048e55c84419d84f03edffa3fb8dff4aa Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Thu, 13 Jun 2024 21:08:43 +0200 Subject: [PATCH 59/70] Replaced random_uniform/1 by rand:uniform/1 * leftover of 5118421f6ff1b358a3b76b7dfb02b81645386e31 * code base contains only one occurence of random_uniform/1 ``` $ git grep random_uniform src/ec_file.erl: UniqueNumber = erlang:integer_to_list(erlang:trunc(random_uniform() * 1000000000000)), src/ec_file.erl:random_uniform() -> ``` --- src/ec_file.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index cc3d1c4..4d19958 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -223,7 +223,7 @@ real_dir_path(Path) -> %% function of the same name. -spec insecure_mkdtemp() -> TmpDirPath::file:name() | {error, term()}. insecure_mkdtemp() -> - UniqueNumber = erlang:integer_to_list(erlang:trunc(random_uniform() * 1000000000000)), + UniqueNumber = erlang:integer_to_list(erlang:trunc(rand:uniform() * 1_000_000_000_000)), TmpDirPath = filename:join([tmp(), lists:flatten([".tmp_dir", UniqueNumber])]), @@ -376,9 +376,6 @@ sub_files(From) -> {ok, SubFiles} = file:list_dir(From), [filename:join(From, SubFile) || SubFile <- SubFiles]. -random_uniform() -> - rand:uniform(). - %%%=================================================================== %%% Test Functions %%%=================================================================== From 515df6b21eb0f08460ffd88f250ea4709a5c288e Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Wed, 3 Jul 2024 07:28:46 +0200 Subject: [PATCH 60/70] Updated CI/CD and hex badges --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f1fbfba..3e9f193 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Erlware Commons Current Status -------------- -![Tests](https://github.com/erlware/erlware_commons/workflows/EUnit/badge.svg) +[![Hex.pm](https://img.shields.io/hexpm/v/erlware_commons)](https://hex.pm/packages/erlware_commons) +[![Tests](https://github.com/erlware/erlware_commons/workflows/EUnit/badge.svg)](https://github.com/erlware/erlware_commons/actions) Introduction ------------ From d24ad720347b72b745c9e3492e61e3f4d0829501 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Thu, 18 Jul 2024 23:18:07 +0200 Subject: [PATCH 61/70] Moved `ec_git_vsn` tests into separate file --- src/ec_git_vsn.erl | 20 +++++++------------- test/ec_git_vsn_tests.erl | 13 +++++++++++++ 2 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 test/ec_git_vsn_tests.erl diff --git a/src/ec_git_vsn.erl b/src/ec_git_vsn.erl index d690d1a..e67d8e4 100644 --- a/src/ec_git_vsn.erl +++ b/src/ec_git_vsn.erl @@ -17,6 +17,13 @@ -export([new/0, vsn/1]). +-ifdef(TEST). +-export([parse_tags/1, + get_patch_count/1, + collect_default_refcount/1 + ]). +-endif. + -export_type([t/0]). %%%=================================================================== @@ -98,16 +105,3 @@ parse_tags(Pattern) -> Vsn1 = string:trim(string:trim(Vsn, leading, "v"), trailing, "\n"), {Tag, Vsn1} end. - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -parse_tags_test() -> - ?assertEqual({undefined, ""}, parse_tags("a.b.c")). - -get_patch_count_test() -> - ?assertEqual(0, get_patch_count("a.b.c")). - -collect_default_refcount_test() -> - ?assertMatch({"", _, _}, collect_default_refcount("a.b.c")). --endif. diff --git a/test/ec_git_vsn_tests.erl b/test/ec_git_vsn_tests.erl new file mode 100644 index 0000000..0d2efe1 --- /dev/null +++ b/test/ec_git_vsn_tests.erl @@ -0,0 +1,13 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_git_vsn_tests). + +-include_lib("eunit/include/eunit.hrl"). + +parse_tags_test() -> + ?assertEqual({undefined, ""}, ec_git_vsn:parse_tags("a.b.c")). + +get_patch_count_test() -> + ?assertEqual(0, ec_git_vsn:get_patch_count("a.b.c")). + +collect_default_refcount_test() -> + ?assertMatch({"", _, _}, ec_git_vsn:collect_default_refcount("a.b.c")). From ca7581cbb0b9fb13f137d22c2da7ca90cb961863 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 12 Aug 2024 19:57:08 +0200 Subject: [PATCH 62/70] Moved `ec_cmd_log` tests into separate file * moved colour macros into `src/ec_cmd_log.hrl` * moved `ec_cmd_log` tests into `test/ec_cmd_log_tests.erl`. Signed-off-by: Ariel Otilibili --- src/ec_cmd_log.erl | 51 ++------------------------------------- src/ec_cmd_log.hrl | 7 ++++++ test/ec_cmd_log_tests.erl | 39 ++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 49 deletions(-) create mode 100644 src/ec_cmd_log.hrl create mode 100644 test/ec_cmd_log_tests.erl diff --git a/src/ec_cmd_log.erl b/src/ec_cmd_log.erl index b41443e..a1f2713 100644 --- a/src/ec_cmd_log.erl +++ b/src/ec_cmd_log.erl @@ -43,14 +43,8 @@ colorize/4, format/1]). --include("ec_cmd_log.hrl"). - --define(RED, $r). --define(GREEN, $g). --define(YELLOW, $y). --define(BLUE, $b). --define(MAGENTA, $m). --define(CYAN, $c). +-include("include/ec_cmd_log.hrl"). +-include("src/ec_cmd_log.hrl"). -define(PREFIX, "===> "). @@ -261,44 +255,3 @@ colorize(#state_t{caller=command_line, intensity = low}, lists:flatten(cf:format("~!" ++ [Color] ++"~ts~!!~ts", [?PREFIX, Msg])); colorize(_LogState, _Color, _Bold, Msg) -> Msg. - -%%%=================================================================== -%%% Test Functions -%%%=================================================================== - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -should_test() -> - ErrorLogState = new(error), - ?assertMatch(true, should(ErrorLogState, ?EC_ERROR)), - ?assertMatch(true, not should(ErrorLogState, ?EC_INFO)), - ?assertMatch(true, not should(ErrorLogState, ?EC_DEBUG)), - ?assertEqual(?EC_ERROR, log_level(ErrorLogState)), - ?assertEqual(error, atom_log_level(ErrorLogState)), - - InfoLogState = new(info), - ?assertMatch(true, should(InfoLogState, ?EC_ERROR)), - ?assertMatch(true, should(InfoLogState, ?EC_INFO)), - ?assertMatch(true, not should(InfoLogState, ?EC_DEBUG)), - ?assertEqual(?EC_INFO, log_level(InfoLogState)), - ?assertEqual(info, atom_log_level(InfoLogState)), - - DebugLogState = new(debug), - ?assertMatch(true, should(DebugLogState, ?EC_ERROR)), - ?assertMatch(true, should(DebugLogState, ?EC_INFO)), - ?assertMatch(true, should(DebugLogState, ?EC_DEBUG)), - ?assertEqual(?EC_DEBUG, log_level(DebugLogState)), - ?assertEqual(debug, atom_log_level(DebugLogState)). - - -no_color_test() -> - LogState = new(debug, command_line, none), - ?assertEqual("test", - colorize(LogState, ?RED, true, "test")). - -color_test() -> - LogState = new(debug, command_line, high), - ?assertEqual("\e[1;31m===> test\e[0m", - colorize(LogState, ?RED, true, "test")). --endif. diff --git a/src/ec_cmd_log.hrl b/src/ec_cmd_log.hrl new file mode 100644 index 0000000..428fd74 --- /dev/null +++ b/src/ec_cmd_log.hrl @@ -0,0 +1,7 @@ +%%% @copyright 2024 Erlware, LLC. +-define(RED, $r). +-define(GREEN, $g). +-define(YELLOW, $y). +-define(BLUE, $b). +-define(MAGENTA, $m). +-define(CYAN, $c). diff --git a/test/ec_cmd_log_tests.erl b/test/ec_cmd_log_tests.erl new file mode 100644 index 0000000..f1d1181 --- /dev/null +++ b/test/ec_cmd_log_tests.erl @@ -0,0 +1,39 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_cmd_log_tests). + +-include("include/ec_cmd_log.hrl"). +-include("src/ec_cmd_log.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +should_test() -> + ErrorLogState = ec_cmd_log:new(error), + ?assertMatch(true, ec_cmd_log:should(ErrorLogState, ?EC_ERROR)), + ?assertMatch(true, not ec_cmd_log:should(ErrorLogState, ?EC_INFO)), + ?assertMatch(true, not ec_cmd_log:should(ErrorLogState, ?EC_DEBUG)), + ?assertEqual(?EC_ERROR, ec_cmd_log:log_level(ErrorLogState)), + ?assertEqual(error, ec_cmd_log:atom_log_level(ErrorLogState)), + + InfoLogState = ec_cmd_log:new(info), + ?assertMatch(true, ec_cmd_log:should(InfoLogState, ?EC_ERROR)), + ?assertMatch(true, ec_cmd_log:should(InfoLogState, ?EC_INFO)), + ?assertMatch(true, not ec_cmd_log:should(InfoLogState, ?EC_DEBUG)), + ?assertEqual(?EC_INFO, ec_cmd_log:log_level(InfoLogState)), + ?assertEqual(info, ec_cmd_log:atom_log_level(InfoLogState)), + + DebugLogState = ec_cmd_log:new(debug), + ?assertMatch(true, ec_cmd_log:should(DebugLogState, ?EC_ERROR)), + ?assertMatch(true, ec_cmd_log:should(DebugLogState, ?EC_INFO)), + ?assertMatch(true, ec_cmd_log:should(DebugLogState, ?EC_DEBUG)), + ?assertEqual(?EC_DEBUG, ec_cmd_log:log_level(DebugLogState)), + ?assertEqual(debug, ec_cmd_log:atom_log_level(DebugLogState)). + + +no_color_test() -> + LogState = ec_cmd_log:new(debug, command_line, none), + ?assertEqual("test", + ec_cmd_log:colorize(LogState, ?RED, true, "test")). + +color_test() -> + LogState = ec_cmd_log:new(debug, command_line, high), + ?assertEqual("\e[1;31m===> test\e[0m", + ec_cmd_log:colorize(LogState, ?RED, true, "test")). From 677984e96171eab96741ec1b74e167b8dd7b2dc8 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 12 Aug 2024 20:01:51 +0200 Subject: [PATCH 63/70] Moved `ec_env` tests into a separate file Signed-off-by: Ariel Otilibili --- src/ec_cnv.erl | 33 --------------------------------- test/ec_cnv_tests.erl | 28 ++++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 33 deletions(-) create mode 100644 test/ec_cnv_tests.erl diff --git a/src/ec_cnv.erl b/src/ec_cnv.erl index 72700fb..bc3b3f3 100644 --- a/src/ec_cnv.erl +++ b/src/ec_cnv.erl @@ -212,36 +212,3 @@ to_atom(X) erlang:list_to_existing_atom(X); to_atom(X) -> to_atom(to_list(X)). - -%%%=================================================================== -%%% Tests -%%%=================================================================== - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -to_integer_test() -> - ?assertError(badarg, to_integer(1.5, strict)). - -to_float_test() -> - ?assertError(badarg, to_float(10, strict)). - -to_atom_test() -> - ?assertMatch(true, to_atom("true")), - ?assertMatch(true, to_atom(<<"true">>)), - ?assertMatch(false, to_atom(<<"false">>)), - ?assertMatch(false, to_atom(false)), - ?assertError(badarg, to_atom("hello_foo_bar_baz")), - - S = erlang:list_to_atom("1"), - ?assertMatch(S, to_atom(1)). - -to_boolean_test()-> - ?assertMatch(true, to_boolean(<<"true">>)), - ?assertMatch(true, to_boolean("true")), - ?assertMatch(true, to_boolean(true)), - ?assertMatch(false, to_boolean(<<"false">>)), - ?assertMatch(false, to_boolean("false")), - ?assertMatch(false, to_boolean(false)). - --endif. diff --git a/test/ec_cnv_tests.erl b/test/ec_cnv_tests.erl new file mode 100644 index 0000000..6bbad6e --- /dev/null +++ b/test/ec_cnv_tests.erl @@ -0,0 +1,28 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_cnv_tests). + +-include_lib("eunit/include/eunit.hrl"). + +to_integer_test() -> + ?assertError(badarg, ec_cnv:to_integer(1.5, strict)). + +to_float_test() -> + ?assertError(badarg, ec_cnv:to_float(10, strict)). + +to_atom_test() -> + ?assertMatch(true, ec_cnv:to_atom("true")), + ?assertMatch(true, ec_cnv:to_atom(<<"true">>)), + ?assertMatch(false, ec_cnv:to_atom(<<"false">>)), + ?assertMatch(false, ec_cnv:to_atom(false)), + ?assertError(badarg, ec_cnv:to_atom("hello_foo_bar_baz")), + + S = erlang:list_to_atom("1"), + ?assertMatch(S, ec_cnv:to_atom(1)). + +to_boolean_test()-> + ?assertMatch(true, ec_cnv:to_boolean(<<"true">>)), + ?assertMatch(true, ec_cnv:to_boolean("true")), + ?assertMatch(true, ec_cnv:to_boolean(true)), + ?assertMatch(false, ec_cnv:to_boolean(<<"false">>)), + ?assertMatch(false, ec_cnv:to_boolean("false")), + ?assertMatch(false, ec_cnv:to_boolean(false)). From 5305348899d5a6ed41e3d77163acec4bc2820ae2 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Sun, 22 Sep 2024 13:17:53 +0200 Subject: [PATCH 64/70] Moved `ec_talk` tests into separate file Part of #179. Signed-off-by: Ariel Otilibili --- src/ec_talk.erl | 28 +++++----------------------- test/ec_talk_tests.erl | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 test/ec_talk_tests.erl diff --git a/src/ec_talk.erl b/src/ec_talk.erl index 54d18f6..8c3a105 100644 --- a/src/ec_talk.erl +++ b/src/ec_talk.erl @@ -39,6 +39,11 @@ say/1, say/2]). +-ifdef(TEST). +-export([get_boolean/1, + get_integer/1]). +-endif. + -export_type([prompt/0, type/0, supported/0]). @@ -196,26 +201,3 @@ get_string(String) -> false -> no_clue end. - -%%%==================================================================== -%%% tests -%%%==================================================================== --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -general_test_() -> - [?_test(42 == get_integer("42")), - ?_test(500_211 == get_integer("500211")), - ?_test(1_234_567_890 == get_integer("1234567890")), - ?_test(12_345_678_901_234_567_890 == get_integer("12345678901234567890")), - ?_test(true == get_boolean("true")), - ?_test(false == get_boolean("false")), - ?_test(true == get_boolean("Ok")), - ?_test(true == get_boolean("ok")), - ?_test(true == get_boolean("Y")), - ?_test(true == get_boolean("y")), - ?_test(false == get_boolean("False")), - ?_test(false == get_boolean("No")), - ?_test(false == get_boolean("no"))]. - --endif. diff --git a/test/ec_talk_tests.erl b/test/ec_talk_tests.erl new file mode 100644 index 0000000..9b7bd07 --- /dev/null +++ b/test/ec_talk_tests.erl @@ -0,0 +1,19 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_talk_tests). + +-include_lib("eunit/include/eunit.hrl"). + +general_test_() -> + [?_test(42 == ec_talk:get_integer("42")), + ?_test(500_211 == ec_talk:get_integer("500211")), + ?_test(1_234_567_890 == ec_talk:get_integer("1234567890")), + ?_test(12_345_678_901_234_567_890 == ec_talk:get_integer("12345678901234567890")), + ?_test(true == ec_talk:get_boolean("true")), + ?_test(false == ec_talk:get_boolean("false")), + ?_test(true == ec_talk:get_boolean("Ok")), + ?_test(true == ec_talk:get_boolean("ok")), + ?_test(true == ec_talk:get_boolean("Y")), + ?_test(true == ec_talk:get_boolean("y")), + ?_test(false == ec_talk:get_boolean("False")), + ?_test(false == ec_talk:get_boolean("No")), + ?_test(false == ec_talk:get_boolean("no"))]. From 23b00904c8359637090cacbd015a1671d8c1de04 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Fri, 4 Oct 2024 18:16:27 +0200 Subject: [PATCH 65/70] Moved `ec_semver` tests in separate file. Part of #179. Signed-off-by: Ariel Otilibili --- src/ec_semver.erl | 452 --------------------------------------- test/ec_semver_tests.erl | 447 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 447 insertions(+), 452 deletions(-) create mode 100644 test/ec_semver_tests.erl diff --git a/src/ec_semver.erl b/src/ec_semver.erl index 493466f..3ffd591 100644 --- a/src/ec_semver.erl +++ b/src/ec_semver.erl @@ -309,455 +309,3 @@ internal_pes(VsnA, {{LM, LMI, LP, LMP}, Alpha}) lt(VsnA, {{LM, LMI, LP + 1, 0}, {[], []}}); internal_pes(Vsn, LVsn) -> gte(Vsn, LVsn). - -%%%=================================================================== -%%% Test Functions -%%%=================================================================== - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -eql_test() -> - ?assertMatch(true, eql("1.0.0-alpha", - "1.0.0-alpha")), - ?assertMatch(true, eql("v1.0.0-alpha", - "1.0.0-alpha")), - ?assertMatch(true, eql("1", - "1.0.0")), - ?assertMatch(true, eql("v1", - "v1.0.0")), - ?assertMatch(true, eql("1.0", - "1.0.0")), - ?assertMatch(true, eql("1.0.0", - "1")), - ?assertMatch(true, eql("1.0.0.0", - "1")), - ?assertMatch(true, eql("1.0+alpha.1", - "1.0.0+alpha.1")), - ?assertMatch(true, eql("1.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1")), - ?assertMatch(true, eql("1.0-alpha.1+build.1", - "1.0.0.0-alpha.1+build.1")), - ?assertMatch(true, eql("1.0-alpha.1+build.1", - "v1.0.0.0-alpha.1+build.1")), - ?assertMatch(true, eql("1.0-pre-alpha.1", - "1.0.0-pre-alpha.1")), - ?assertMatch(true, eql("aa", "aa")), - ?assertMatch(true, eql("AA.BB", "AA.BB")), - ?assertMatch(true, eql("BBB-super", "BBB-super")), - ?assertMatch(true, not eql("1.0.0", - "1.0.1")), - ?assertMatch(true, not eql("1.0.0-alpha", - "1.0.1+alpha")), - ?assertMatch(true, not eql("1.0.0+build.1", - "1.0.1+build.2")), - ?assertMatch(true, not eql("1.0.0.0+build.1", - "1.0.0.1+build.2")), - ?assertMatch(true, not eql("FFF", "BBB")), - ?assertMatch(true, not eql("1", "1BBBB")). - -gt_test() -> - ?assertMatch(true, gt("1.0.0-alpha.1", - "1.0.0-alpha")), - ?assertMatch(true, gt("1.0.0.1-alpha.1", - "1.0.0.1-alpha")), - ?assertMatch(true, gt("1.0.0.4-alpha.1", - "1.0.0.2-alpha")), - ?assertMatch(true, gt("1.0.0.0-alpha.1", - "1.0.0-alpha")), - ?assertMatch(true, gt("1.0.0-beta.2", - "1.0.0-alpha.1")), - ?assertMatch(true, gt("1.0.0-beta.11", - "1.0.0-beta.2")), - ?assertMatch(true, gt("1.0.0-pre-alpha.14", - "1.0.0-pre-alpha.3")), - ?assertMatch(true, gt("1.0.0-beta.11", - "1.0.0.0-beta.2")), - ?assertMatch(true, gt("1.0.0-rc.1", "1.0.0-beta.11")), - ?assertMatch(true, gt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), - ?assertMatch(true, gt("1.0.0", "1.0.0-rc.1+build.1")), - ?assertMatch(true, gt("1.0.0+0.3.7", "1.0.0")), - ?assertMatch(true, gt("1.3.7+build", "1.0.0+0.3.7")), - ?assertMatch(true, gt("1.3.7+build.2.b8f12d7", - "1.3.7+build")), - ?assertMatch(true, gt("1.3.7+build.2.b8f12d7", - "1.3.7.0+build")), - ?assertMatch(true, gt("1.3.7+build.11.e0f985a", - "1.3.7+build.2.b8f12d7")), - ?assertMatch(true, gt("aa.cc", - "aa.bb")), - ?assertMatch(true, not gt("1.0.0-alpha", - "1.0.0-alpha.1")), - ?assertMatch(true, not gt("1.0.0-alpha", - "1.0.0.0-alpha.1")), - ?assertMatch(true, not gt("1.0.0-alpha.1", - "1.0.0-beta.2")), - ?assertMatch(true, not gt("1.0.0-beta.2", - "1.0.0-beta.11")), - ?assertMatch(true, not gt("1.0.0-beta.11", - "1.0.0-rc.1")), - ?assertMatch(true, not gt("1.0.0-pre-alpha.3", - "1.0.0-pre-alpha.14")), - ?assertMatch(true, not gt("1.0.0-rc.1", - "1.0.0-rc.1+build.1")), - ?assertMatch(true, not gt("1.0.0-rc.1+build.1", - "1.0.0")), - ?assertMatch(true, not gt("1.0.0", - "1.0.0+0.3.7")), - ?assertMatch(true, not gt("1.0.0+0.3.7", - "1.3.7+build")), - ?assertMatch(true, not gt("1.3.7+build", - "1.3.7+build.2.b8f12d7")), - ?assertMatch(true, not gt("1.3.7+build.2.b8f12d7", - "1.3.7+build.11.e0f985a")), - ?assertMatch(true, not gt("1.0.0-alpha", - "1.0.0-alpha")), - ?assertMatch(true, not gt("1", - "1.0.0")), - ?assertMatch(true, not gt("aa.bb", - "aa.bb")), - ?assertMatch(true, not gt("aa.cc", - "aa.dd")), - ?assertMatch(true, not gt("1.0", - "1.0.0")), - ?assertMatch(true, not gt("1.0.0", - "1")), - ?assertMatch(true, not gt("1.0+alpha.1", - "1.0.0+alpha.1")), - ?assertMatch(true, not gt("1.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1")). - -lt_test() -> - ?assertMatch(true, lt("1.0.0-alpha", - "1.0.0-alpha.1")), - ?assertMatch(true, lt("1.0.0-alpha", - "1.0.0.0-alpha.1")), - ?assertMatch(true, lt("1.0.0-alpha.1", - "1.0.0-beta.2")), - ?assertMatch(true, lt("1.0.0-beta.2", - "1.0.0-beta.11")), - ?assertMatch(true, lt("1.0.0-pre-alpha.3", - "1.0.0-pre-alpha.14")), - ?assertMatch(true, lt("1.0.0-beta.11", - "1.0.0-rc.1")), - ?assertMatch(true, lt("1.0.0.1-beta.11", - "1.0.0.1-rc.1")), - ?assertMatch(true, lt("1.0.0-rc.1", - "1.0.0-rc.1+build.1")), - ?assertMatch(true, lt("1.0.0-rc.1+build.1", - "1.0.0")), - ?assertMatch(true, lt("1.0.0", - "1.0.0+0.3.7")), - ?assertMatch(true, lt("1.0.0+0.3.7", - "1.3.7+build")), - ?assertMatch(true, lt("1.3.7+build", - "1.3.7+build.2.b8f12d7")), - ?assertMatch(true, lt("1.3.7+build.2.b8f12d7", - "1.3.7+build.11.e0f985a")), - ?assertMatch(true, not lt("1.0.0-alpha", - "1.0.0-alpha")), - ?assertMatch(true, not lt("1", - "1.0.0")), - ?assertMatch(true, lt("1", - "1.0.0.1")), - ?assertMatch(true, lt("AA.DD", - "AA.EE")), - ?assertMatch(true, not lt("1.0", - "1.0.0")), - ?assertMatch(true, not lt("1.0.0.0", - "1")), - ?assertMatch(true, not lt("1.0+alpha.1", - "1.0.0+alpha.1")), - ?assertMatch(true, not lt("AA.DD", "AA.CC")), - ?assertMatch(true, not lt("1.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1")), - ?assertMatch(true, not lt("1.0.0-alpha.1", - "1.0.0-alpha")), - ?assertMatch(true, not lt("1.0.0-beta.2", - "1.0.0-alpha.1")), - ?assertMatch(true, not lt("1.0.0-beta.11", - "1.0.0-beta.2")), - ?assertMatch(true, not lt("1.0.0-pre-alpha.14", - "1.0.0-pre-alpha.3")), - ?assertMatch(true, not lt("1.0.0-rc.1", "1.0.0-beta.11")), - ?assertMatch(true, not lt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), - ?assertMatch(true, not lt("1.0.0", "1.0.0-rc.1+build.1")), - ?assertMatch(true, not lt("1.0.0+0.3.7", "1.0.0")), - ?assertMatch(true, not lt("1.3.7+build", "1.0.0+0.3.7")), - ?assertMatch(true, not lt("1.3.7+build.2.b8f12d7", - "1.3.7+build")), - ?assertMatch(true, not lt("1.3.7+build.11.e0f985a", - "1.3.7+build.2.b8f12d7")). - -gte_test() -> - ?assertMatch(true, gte("1.0.0-alpha", - "1.0.0-alpha")), - - ?assertMatch(true, gte("1", - "1.0.0")), - - ?assertMatch(true, gte("1.0", - "1.0.0")), - - ?assertMatch(true, gte("1.0.0", - "1")), - - ?assertMatch(true, gte("1.0.0.0", - "1")), - - ?assertMatch(true, gte("1.0+alpha.1", - "1.0.0+alpha.1")), - - ?assertMatch(true, gte("1.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1")), - - ?assertMatch(true, gte("1.0.0-alpha.1+build.1", - "1.0.0.0-alpha.1+build.1")), - ?assertMatch(true, gte("1.0.0-alpha.1", - "1.0.0-alpha")), - ?assertMatch(true, gte("1.0.0-pre-alpha.2", - "1.0.0-pre-alpha")), - ?assertMatch(true, gte("1.0.0-beta.2", - "1.0.0-alpha.1")), - ?assertMatch(true, gte("1.0.0-beta.11", - "1.0.0-beta.2")), - ?assertMatch(true, gte("aa.bb", "aa.bb")), - ?assertMatch(true, gte("dd", "aa")), - ?assertMatch(true, gte("1.0.0-rc.1", "1.0.0-beta.11")), - ?assertMatch(true, gte("1.0.0-rc.1+build.1", "1.0.0-rc.1")), - ?assertMatch(true, gte("1.0.0", "1.0.0-rc.1+build.1")), - ?assertMatch(true, gte("1.0.0+0.3.7", "1.0.0")), - ?assertMatch(true, gte("1.3.7+build", "1.0.0+0.3.7")), - ?assertMatch(true, gte("1.3.7+build.2.b8f12d7", - "1.3.7+build")), - ?assertMatch(true, gte("1.3.7+build.11.e0f985a", - "1.3.7+build.2.b8f12d7")), - ?assertMatch(true, not gte("1.0.0-alpha", - "1.0.0-alpha.1")), - ?assertMatch(true, not gte("1.0.0-pre-alpha", - "1.0.0-pre-alpha.1")), - ?assertMatch(true, not gte("CC", "DD")), - ?assertMatch(true, not gte("1.0.0-alpha.1", - "1.0.0-beta.2")), - ?assertMatch(true, not gte("1.0.0-beta.2", - "1.0.0-beta.11")), - ?assertMatch(true, not gte("1.0.0-beta.11", - "1.0.0-rc.1")), - ?assertMatch(true, not gte("1.0.0-rc.1", - "1.0.0-rc.1+build.1")), - ?assertMatch(true, not gte("1.0.0-rc.1+build.1", - "1.0.0")), - ?assertMatch(true, not gte("1.0.0", - "1.0.0+0.3.7")), - ?assertMatch(true, not gte("1.0.0+0.3.7", - "1.3.7+build")), - ?assertMatch(true, not gte("1.0.0", - "1.0.0+build.1")), - ?assertMatch(true, not gte("1.3.7+build", - "1.3.7+build.2.b8f12d7")), - ?assertMatch(true, not gte("1.3.7+build.2.b8f12d7", - "1.3.7+build.11.e0f985a")). -lte_test() -> - ?assertMatch(true, lte("1.0.0-alpha", - "1.0.0-alpha.1")), - ?assertMatch(true, lte("1.0.0-alpha.1", - "1.0.0-beta.2")), - ?assertMatch(true, lte("1.0.0-beta.2", - "1.0.0-beta.11")), - ?assertMatch(true, lte("1.0.0-pre-alpha.2", - "1.0.0-pre-alpha.11")), - ?assertMatch(true, lte("1.0.0-beta.11", - "1.0.0-rc.1")), - ?assertMatch(true, lte("1.0.0-rc.1", - "1.0.0-rc.1+build.1")), - ?assertMatch(true, lte("1.0.0-rc.1+build.1", - "1.0.0")), - ?assertMatch(true, lte("1.0.0", - "1.0.0+0.3.7")), - ?assertMatch(true, lte("1.0.0+0.3.7", - "1.3.7+build")), - ?assertMatch(true, lte("1.3.7+build", - "1.3.7+build.2.b8f12d7")), - ?assertMatch(true, lte("1.3.7+build.2.b8f12d7", - "1.3.7+build.11.e0f985a")), - ?assertMatch(true, lte("1.0.0-alpha", - "1.0.0-alpha")), - ?assertMatch(true, lte("1", - "1.0.0")), - ?assertMatch(true, lte("1.0", - "1.0.0")), - ?assertMatch(true, lte("1.0.0", - "1")), - ?assertMatch(true, lte("1.0+alpha.1", - "1.0.0+alpha.1")), - ?assertMatch(true, lte("1.0.0.0+alpha.1", - "1.0.0+alpha.1")), - ?assertMatch(true, lte("1.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1")), - ?assertMatch(true, lte("aa","cc")), - ?assertMatch(true, lte("cc","cc")), - ?assertMatch(true, not lte("1.0.0-alpha.1", - "1.0.0-alpha")), - ?assertMatch(true, not lte("1.0.0-pre-alpha.2", - "1.0.0-pre-alpha")), - ?assertMatch(true, not lte("cc", "aa")), - ?assertMatch(true, not lte("1.0.0-beta.2", - "1.0.0-alpha.1")), - ?assertMatch(true, not lte("1.0.0-beta.11", - "1.0.0-beta.2")), - ?assertMatch(true, not lte("1.0.0-rc.1", "1.0.0-beta.11")), - ?assertMatch(true, not lte("1.0.0-rc.1+build.1", "1.0.0-rc.1")), - ?assertMatch(true, not lte("1.0.0", "1.0.0-rc.1+build.1")), - ?assertMatch(true, not lte("1.0.0+0.3.7", "1.0.0")), - ?assertMatch(true, not lte("1.3.7+build", "1.0.0+0.3.7")), - ?assertMatch(true, not lte("1.3.7+build.2.b8f12d7", - "1.3.7+build")), - ?assertMatch(true, not lte("1.3.7+build.11.e0f985a", - "1.3.7+build.2.b8f12d7")). - -between_test() -> - ?assertMatch(true, between("1.0.0-alpha", - "1.0.0-alpha.3", - "1.0.0-alpha.2")), - ?assertMatch(true, between("1.0.0-alpha.1", - "1.0.0-beta.2", - "1.0.0-alpha.25")), - ?assertMatch(true, between("1.0.0-beta.2", - "1.0.0-beta.11", - "1.0.0-beta.7")), - ?assertMatch(true, between("1.0.0-pre-alpha.2", - "1.0.0-pre-alpha.11", - "1.0.0-pre-alpha.7")), - ?assertMatch(true, between("1.0.0-beta.11", - "1.0.0-rc.3", - "1.0.0-rc.1")), - ?assertMatch(true, between("1.0.0-rc.1", - "1.0.0-rc.1+build.3", - "1.0.0-rc.1+build.1")), - - ?assertMatch(true, between("1.0.0.0-rc.1", - "1.0.0-rc.1+build.3", - "1.0.0-rc.1+build.1")), - ?assertMatch(true, between("1.0.0-rc.1+build.1", - "1.0.0", - "1.0.0-rc.33")), - ?assertMatch(true, between("1.0.0", - "1.0.0+0.3.7", - "1.0.0+0.2")), - ?assertMatch(true, between("1.0.0+0.3.7", - "1.3.7+build", - "1.2")), - ?assertMatch(true, between("1.3.7+build", - "1.3.7+build.2.b8f12d7", - "1.3.7+build.1")), - ?assertMatch(true, between("1.3.7+build.2.b8f12d7", - "1.3.7+build.11.e0f985a", - "1.3.7+build.10.a36faa")), - ?assertMatch(true, between("1.0.0-alpha", - "1.0.0-alpha", - "1.0.0-alpha")), - ?assertMatch(true, between("1", - "1.0.0", - "1.0.0")), - ?assertMatch(true, between("1.0", - "1.0.0", - "1.0.0")), - - ?assertMatch(true, between("1.0", - "1.0.0.0", - "1.0.0.0")), - ?assertMatch(true, between("1.0.0", - "1", - "1")), - ?assertMatch(true, between("1.0+alpha.1", - "1.0.0+alpha.1", - "1.0.0+alpha.1")), - ?assertMatch(true, between("1.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1", - "1.0.0-alpha.1+build.1")), - ?assertMatch(true, between("aaa", - "ddd", - "cc")), - ?assertMatch(true, not between("1.0.0-alpha.1", - "1.0.0-alpha.22", - "1.0.0")), - ?assertMatch(true, not between("1.0.0-pre-alpha.1", - "1.0.0-pre-alpha.22", - "1.0.0")), - ?assertMatch(true, not between("1.0.0", - "1.0.0-alpha.1", - "2.0")), - ?assertMatch(true, not between("1.0.0-beta.1", - "1.0.0-beta.11", - "1.0.0-alpha")), - ?assertMatch(true, not between("1.0.0-beta.11", "1.0.0-rc.1", - "1.0.0-rc.22")), - ?assertMatch(true, not between("aaa", "ddd", "zzz")). - -pes_test() -> - ?assertMatch(true, pes("1.0.0-rc.0", "1.0.0-rc.0")), - ?assertMatch(true, pes("1.0.0-rc.1", "1.0.0-rc.0")), - ?assertMatch(true, pes("1.0.0", "1.0.0-rc.0")), - ?assertMatch(false, pes("1.0.0-rc.0", "1.0.0-rc.1")), - ?assertMatch(true, pes("2.6.0", "2.6")), - ?assertMatch(true, pes("2.7", "2.6")), - ?assertMatch(true, pes("2.8", "2.6")), - ?assertMatch(true, pes("2.9", "2.6")), - ?assertMatch(true, pes("A.B", "A.A")), - ?assertMatch(true, not pes("3.0.0", "2.6")), - ?assertMatch(true, not pes("2.5", "2.6")), - ?assertMatch(true, pes("2.6.5", "2.6.5")), - ?assertMatch(true, pes("2.6.6", "2.6.5")), - ?assertMatch(true, pes("2.6.7", "2.6.5")), - ?assertMatch(true, pes("2.6.8", "2.6.5")), - ?assertMatch(true, pes("2.6.9", "2.6.5")), - ?assertMatch(true, pes("2.6.0.9", "2.6.0.5")), - ?assertMatch(true, not pes("2.7", "2.6.5")), - ?assertMatch(true, not pes("2.1.7", "2.1.6.5")), - ?assertMatch(true, not pes("A.A", "A.B")), - ?assertMatch(true, not pes("2.5", "2.6.5")). - -parse_test() -> - ?assertEqual({1, {[],[]}}, parse(<<"1">>)), - ?assertEqual({{1,2,34},{[],[]}}, parse(<<"1.2.34">>)), - ?assertEqual({<<"a">>, {[],[]}}, parse(<<"a">>)), - ?assertEqual({{<<"a">>,<<"b">>}, {[],[]}}, parse(<<"a.b">>)), - ?assertEqual({1, {[],[]}}, parse(<<"1">>)), - ?assertEqual({{1,2}, {[],[]}}, parse(<<"1.2">>)), - ?assertEqual({{1,2,2}, {[],[]}}, parse(<<"1.2.2">>)), - ?assertEqual({{1,99,2}, {[],[]}}, parse(<<"1.99.2">>)), - ?assertEqual({{1,99,2}, {[<<"alpha">>],[]}}, parse(<<"1.99.2-alpha">>)), - ?assertEqual({{1,99,2}, {[<<"alpha">>,1], []}}, parse(<<"1.99.2-alpha.1">>)), - ?assertEqual({{1,99,2}, {[<<"pre-alpha">>,1], []}}, parse(<<"1.99.2-pre-alpha.1">>)), - ?assertEqual({{1,99,2}, {[], [<<"build">>, 1, <<"a36">>]}}, - parse(<<"1.99.2+build.1.a36">>)), - ?assertEqual({{1,99,2,44}, {[], [<<"build">>, 1, <<"a36">>]}}, - parse(<<"1.99.2.44+build.1.a36">>)), - ?assertEqual({{1,99,2}, {[<<"alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}, - parse("1.99.2-alpha.1+build.1.a36")), - ?assertEqual({{1,99,2}, {[<<"pre-alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}, - parse("1.99.2-pre-alpha.1+build.1.a36")). - -version_format_test() -> - ?assertEqual(["1", [], []], format({1, {[],[]}})), - ?assertEqual(["1", ".", "2", ".", "34", [], []], format({{1,2,34},{[],[]}})), - ?assertEqual(<<"a">>, erlang:iolist_to_binary(format({<<"a">>, {[],[]}}))), - ?assertEqual(<<"a.b">>, erlang:iolist_to_binary(format({{<<"a">>,<<"b">>}, {[],[]}}))), - ?assertEqual(<<"1">>, erlang:iolist_to_binary(format({1, {[],[]}}))), - ?assertEqual(<<"1.2">>, erlang:iolist_to_binary(format({{1,2}, {[],[]}}))), - ?assertEqual(<<"1.2.2">>, erlang:iolist_to_binary(format({{1,2,2}, {[],[]}}))), - ?assertEqual(<<"1.99.2">>, erlang:iolist_to_binary(format({{1,99,2}, {[],[]}}))), - ?assertEqual(<<"1.99.2-alpha">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"alpha">>],[]}}))), - ?assertEqual(<<"1.99.2-alpha.1">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"alpha">>,1], []}}))), - ?assertEqual(<<"1.99.2-pre-alpha.1">>, erlang:iolist_to_binary(format({{1,99,2}, {[<<"pre-alpha">>,1], []}}))), - ?assertEqual(<<"1.99.2+build.1.a36">>, - erlang:iolist_to_binary(format({{1,99,2}, {[], [<<"build">>, 1, <<"a36">>]}}))), - ?assertEqual(<<"1.99.2.44+build.1.a36">>, - erlang:iolist_to_binary(format({{1,99,2,44}, {[], [<<"build">>, 1, <<"a36">>]}}))), - ?assertEqual(<<"1.99.2-alpha.1+build.1.a36">>, - erlang:iolist_to_binary(format({{1,99,2}, {[<<"alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}))), - ?assertEqual(<<"1.99.2-pre-alpha.1+build.1.a36">>, - erlang:iolist_to_binary(format({{1,99,2}, {[<<"pre-alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}))), - ?assertEqual(<<"1">>, erlang:iolist_to_binary(format({1, {[],[]}}))). - --endif. diff --git a/test/ec_semver_tests.erl b/test/ec_semver_tests.erl new file mode 100644 index 0000000..0d3a18a --- /dev/null +++ b/test/ec_semver_tests.erl @@ -0,0 +1,447 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_semver_tests). + +-include_lib("eunit/include/eunit.hrl"). + +eql_test() -> + ?assertMatch(true, ec_semver:eql("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:eql("v1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:eql("1", + "1.0.0")), + ?assertMatch(true, ec_semver:eql("v1", + "v1.0.0")), + ?assertMatch(true, ec_semver:eql("1.0", + "1.0.0")), + ?assertMatch(true, ec_semver:eql("1.0.0", + "1")), + ?assertMatch(true, ec_semver:eql("1.0.0.0", + "1")), + ?assertMatch(true, ec_semver:eql("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, ec_semver:eql("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, ec_semver:eql("1.0-alpha.1+build.1", + "1.0.0.0-alpha.1+build.1")), + ?assertMatch(true, ec_semver:eql("1.0-alpha.1+build.1", + "v1.0.0.0-alpha.1+build.1")), + ?assertMatch(true, ec_semver:eql("1.0-pre-alpha.1", + "1.0.0-pre-alpha.1")), + ?assertMatch(true, ec_semver:eql("aa", "aa")), + ?assertMatch(true, ec_semver:eql("AA.BB", "AA.BB")), + ?assertMatch(true, ec_semver:eql("BBB-super", "BBB-super")), + ?assertMatch(true, not ec_semver:eql("1.0.0", + "1.0.1")), + ?assertMatch(true, not ec_semver:eql("1.0.0-alpha", + "1.0.1+alpha")), + ?assertMatch(true, not ec_semver:eql("1.0.0+build.1", + "1.0.1+build.2")), + ?assertMatch(true, not ec_semver:eql("1.0.0.0+build.1", + "1.0.0.1+build.2")), + ?assertMatch(true, not ec_semver:eql("FFF", "BBB")), + ?assertMatch(true, not ec_semver:eql("1", "1BBBB")). + +gt_test() -> + ?assertMatch(true, ec_semver:gt("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:gt("1.0.0.1-alpha.1", + "1.0.0.1-alpha")), + ?assertMatch(true, ec_semver:gt("1.0.0.4-alpha.1", + "1.0.0.2-alpha")), + ?assertMatch(true, ec_semver:gt("1.0.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:gt("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, ec_semver:gt("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, ec_semver:gt("1.0.0-pre-alpha.14", + "1.0.0-pre-alpha.3")), + ?assertMatch(true, ec_semver:gt("1.0.0-beta.11", + "1.0.0.0-beta.2")), + ?assertMatch(true, ec_semver:gt("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, ec_semver:gt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, ec_semver:gt("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, ec_semver:gt("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, ec_semver:gt("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, ec_semver:gt("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, ec_semver:gt("1.3.7+build.2.b8f12d7", + "1.3.7.0+build")), + ?assertMatch(true, ec_semver:gt("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, ec_semver:gt("aa.cc", + "aa.bb")), + ?assertMatch(true, not ec_semver:gt("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, not ec_semver:gt("1.0.0-alpha", + "1.0.0.0-alpha.1")), + ?assertMatch(true, not ec_semver:gt("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, not ec_semver:gt("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, not ec_semver:gt("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, not ec_semver:gt("1.0.0-pre-alpha.3", + "1.0.0-pre-alpha.14")), + ?assertMatch(true, not ec_semver:gt("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, not ec_semver:gt("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, not ec_semver:gt("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, not ec_semver:gt("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, not ec_semver:gt("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not ec_semver:gt("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")), + ?assertMatch(true, not ec_semver:gt("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, not ec_semver:gt("1", + "1.0.0")), + ?assertMatch(true, not ec_semver:gt("aa.bb", + "aa.bb")), + ?assertMatch(true, not ec_semver:gt("aa.cc", + "aa.dd")), + ?assertMatch(true, not ec_semver:gt("1.0", + "1.0.0")), + ?assertMatch(true, not ec_semver:gt("1.0.0", + "1")), + ?assertMatch(true, not ec_semver:gt("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, not ec_semver:gt("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")). + +lt_test() -> + ?assertMatch(true, ec_semver:lt("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, ec_semver:lt("1.0.0-alpha", + "1.0.0.0-alpha.1")), + ?assertMatch(true, ec_semver:lt("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, ec_semver:lt("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, ec_semver:lt("1.0.0-pre-alpha.3", + "1.0.0-pre-alpha.14")), + ?assertMatch(true, ec_semver:lt("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, ec_semver:lt("1.0.0.1-beta.11", + "1.0.0.1-rc.1")), + ?assertMatch(true, ec_semver:lt("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, ec_semver:lt("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, ec_semver:lt("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, ec_semver:lt("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, ec_semver:lt("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, ec_semver:lt("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")), + ?assertMatch(true, not ec_semver:lt("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, not ec_semver:lt("1", + "1.0.0")), + ?assertMatch(true, ec_semver:lt("1", + "1.0.0.1")), + ?assertMatch(true, ec_semver:lt("AA.DD", + "AA.EE")), + ?assertMatch(true, not ec_semver:lt("1.0", + "1.0.0")), + ?assertMatch(true, not ec_semver:lt("1.0.0.0", + "1")), + ?assertMatch(true, not ec_semver:lt("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, not ec_semver:lt("AA.DD", "AA.CC")), + ?assertMatch(true, not ec_semver:lt("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, not ec_semver:lt("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, not ec_semver:lt("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, not ec_semver:lt("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, not ec_semver:lt("1.0.0-pre-alpha.14", + "1.0.0-pre-alpha.3")), + ?assertMatch(true, not ec_semver:lt("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, not ec_semver:lt("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, not ec_semver:lt("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, not ec_semver:lt("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, not ec_semver:lt("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, not ec_semver:lt("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, not ec_semver:lt("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")). + +gte_test() -> + ?assertMatch(true, ec_semver:gte("1.0.0-alpha", + "1.0.0-alpha")), + + ?assertMatch(true, ec_semver:gte("1", + "1.0.0")), + + ?assertMatch(true, ec_semver:gte("1.0", + "1.0.0")), + + ?assertMatch(true, ec_semver:gte("1.0.0", + "1")), + + ?assertMatch(true, ec_semver:gte("1.0.0.0", + "1")), + + ?assertMatch(true, ec_semver:gte("1.0+alpha.1", + "1.0.0+alpha.1")), + + ?assertMatch(true, ec_semver:gte("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + + ?assertMatch(true, ec_semver:gte("1.0.0-alpha.1+build.1", + "1.0.0.0-alpha.1+build.1")), + ?assertMatch(true, ec_semver:gte("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:gte("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha")), + ?assertMatch(true, ec_semver:gte("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, ec_semver:gte("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, ec_semver:gte("aa.bb", "aa.bb")), + ?assertMatch(true, ec_semver:gte("dd", "aa")), + ?assertMatch(true, ec_semver:gte("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, ec_semver:gte("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, ec_semver:gte("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, ec_semver:gte("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, ec_semver:gte("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, ec_semver:gte("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, ec_semver:gte("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not ec_semver:gte("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, not ec_semver:gte("1.0.0-pre-alpha", + "1.0.0-pre-alpha.1")), + ?assertMatch(true, not ec_semver:gte("CC", "DD")), + ?assertMatch(true, not ec_semver:gte("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, not ec_semver:gte("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, not ec_semver:gte("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, not ec_semver:gte("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, not ec_semver:gte("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, not ec_semver:gte("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, not ec_semver:gte("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, not ec_semver:gte("1.0.0", + "1.0.0+build.1")), + ?assertMatch(true, not ec_semver:gte("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, not ec_semver:gte("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")). +lte_test() -> + ?assertMatch(true, ec_semver:lte("1.0.0-alpha", + "1.0.0-alpha.1")), + ?assertMatch(true, ec_semver:lte("1.0.0-alpha.1", + "1.0.0-beta.2")), + ?assertMatch(true, ec_semver:lte("1.0.0-beta.2", + "1.0.0-beta.11")), + ?assertMatch(true, ec_semver:lte("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha.11")), + ?assertMatch(true, ec_semver:lte("1.0.0-beta.11", + "1.0.0-rc.1")), + ?assertMatch(true, ec_semver:lte("1.0.0-rc.1", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, ec_semver:lte("1.0.0-rc.1+build.1", + "1.0.0")), + ?assertMatch(true, ec_semver:lte("1.0.0", + "1.0.0+0.3.7")), + ?assertMatch(true, ec_semver:lte("1.0.0+0.3.7", + "1.3.7+build")), + ?assertMatch(true, ec_semver:lte("1.3.7+build", + "1.3.7+build.2.b8f12d7")), + ?assertMatch(true, ec_semver:lte("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a")), + ?assertMatch(true, ec_semver:lte("1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:lte("1", + "1.0.0")), + ?assertMatch(true, ec_semver:lte("1.0", + "1.0.0")), + ?assertMatch(true, ec_semver:lte("1.0.0", + "1")), + ?assertMatch(true, ec_semver:lte("1.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, ec_semver:lte("1.0.0.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, ec_semver:lte("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, ec_semver:lte("aa","cc")), + ?assertMatch(true, ec_semver:lte("cc","cc")), + ?assertMatch(true, not ec_semver:lte("1.0.0-alpha.1", + "1.0.0-alpha")), + ?assertMatch(true, not ec_semver:lte("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha")), + ?assertMatch(true, not ec_semver:lte("cc", "aa")), + ?assertMatch(true, not ec_semver:lte("1.0.0-beta.2", + "1.0.0-alpha.1")), + ?assertMatch(true, not ec_semver:lte("1.0.0-beta.11", + "1.0.0-beta.2")), + ?assertMatch(true, not ec_semver:lte("1.0.0-rc.1", "1.0.0-beta.11")), + ?assertMatch(true, not ec_semver:lte("1.0.0-rc.1+build.1", "1.0.0-rc.1")), + ?assertMatch(true, not ec_semver:lte("1.0.0", "1.0.0-rc.1+build.1")), + ?assertMatch(true, not ec_semver:lte("1.0.0+0.3.7", "1.0.0")), + ?assertMatch(true, not ec_semver:lte("1.3.7+build", "1.0.0+0.3.7")), + ?assertMatch(true, not ec_semver:lte("1.3.7+build.2.b8f12d7", + "1.3.7+build")), + ?assertMatch(true, not ec_semver:lte("1.3.7+build.11.e0f985a", + "1.3.7+build.2.b8f12d7")). + +between_test() -> + ?assertMatch(true, ec_semver:between("1.0.0-alpha", + "1.0.0-alpha.3", + "1.0.0-alpha.2")), + ?assertMatch(true, ec_semver:between("1.0.0-alpha.1", + "1.0.0-beta.2", + "1.0.0-alpha.25")), + ?assertMatch(true, ec_semver:between("1.0.0-beta.2", + "1.0.0-beta.11", + "1.0.0-beta.7")), + ?assertMatch(true, ec_semver:between("1.0.0-pre-alpha.2", + "1.0.0-pre-alpha.11", + "1.0.0-pre-alpha.7")), + ?assertMatch(true, ec_semver:between("1.0.0-beta.11", + "1.0.0-rc.3", + "1.0.0-rc.1")), + ?assertMatch(true, ec_semver:between("1.0.0-rc.1", + "1.0.0-rc.1+build.3", + "1.0.0-rc.1+build.1")), + + ?assertMatch(true, ec_semver:between("1.0.0.0-rc.1", + "1.0.0-rc.1+build.3", + "1.0.0-rc.1+build.1")), + ?assertMatch(true, ec_semver:between("1.0.0-rc.1+build.1", + "1.0.0", + "1.0.0-rc.33")), + ?assertMatch(true, ec_semver:between("1.0.0", + "1.0.0+0.3.7", + "1.0.0+0.2")), + ?assertMatch(true, ec_semver:between("1.0.0+0.3.7", + "1.3.7+build", + "1.2")), + ?assertMatch(true, ec_semver:between("1.3.7+build", + "1.3.7+build.2.b8f12d7", + "1.3.7+build.1")), + ?assertMatch(true, ec_semver:between("1.3.7+build.2.b8f12d7", + "1.3.7+build.11.e0f985a", + "1.3.7+build.10.a36faa")), + ?assertMatch(true, ec_semver:between("1.0.0-alpha", + "1.0.0-alpha", + "1.0.0-alpha")), + ?assertMatch(true, ec_semver:between("1", + "1.0.0", + "1.0.0")), + ?assertMatch(true, ec_semver:between("1.0", + "1.0.0", + "1.0.0")), + + ?assertMatch(true, ec_semver:between("1.0", + "1.0.0.0", + "1.0.0.0")), + ?assertMatch(true, ec_semver:between("1.0.0", + "1", + "1")), + ?assertMatch(true, ec_semver:between("1.0+alpha.1", + "1.0.0+alpha.1", + "1.0.0+alpha.1")), + ?assertMatch(true, ec_semver:between("1.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1", + "1.0.0-alpha.1+build.1")), + ?assertMatch(true, ec_semver:between("aaa", + "ddd", + "cc")), + ?assertMatch(true, not ec_semver:between("1.0.0-alpha.1", + "1.0.0-alpha.22", + "1.0.0")), + ?assertMatch(true, not ec_semver:between("1.0.0-pre-alpha.1", + "1.0.0-pre-alpha.22", + "1.0.0")), + ?assertMatch(true, not ec_semver:between("1.0.0", + "1.0.0-alpha.1", + "2.0")), + ?assertMatch(true, not ec_semver:between("1.0.0-beta.1", + "1.0.0-beta.11", + "1.0.0-alpha")), + ?assertMatch(true, not ec_semver:between("1.0.0-beta.11", "1.0.0-rc.1", + "1.0.0-rc.22")), + ?assertMatch(true, not ec_semver:between("aaa", "ddd", "zzz")). + +pes_test() -> + ?assertMatch(true, ec_semver:pes("1.0.0-rc.0", "1.0.0-rc.0")), + ?assertMatch(true, ec_semver:pes("1.0.0-rc.1", "1.0.0-rc.0")), + ?assertMatch(true, ec_semver:pes("1.0.0", "1.0.0-rc.0")), + ?assertMatch(false, ec_semver:pes("1.0.0-rc.0", "1.0.0-rc.1")), + ?assertMatch(true, ec_semver:pes("2.6.0", "2.6")), + ?assertMatch(true, ec_semver:pes("2.7", "2.6")), + ?assertMatch(true, ec_semver:pes("2.8", "2.6")), + ?assertMatch(true, ec_semver:pes("2.9", "2.6")), + ?assertMatch(true, ec_semver:pes("A.B", "A.A")), + ?assertMatch(true, not ec_semver:pes("3.0.0", "2.6")), + ?assertMatch(true, not ec_semver:pes("2.5", "2.6")), + ?assertMatch(true, ec_semver:pes("2.6.5", "2.6.5")), + ?assertMatch(true, ec_semver:pes("2.6.6", "2.6.5")), + ?assertMatch(true, ec_semver:pes("2.6.7", "2.6.5")), + ?assertMatch(true, ec_semver:pes("2.6.8", "2.6.5")), + ?assertMatch(true, ec_semver:pes("2.6.9", "2.6.5")), + ?assertMatch(true, ec_semver:pes("2.6.0.9", "2.6.0.5")), + ?assertMatch(true, not ec_semver:pes("2.7", "2.6.5")), + ?assertMatch(true, not ec_semver:pes("2.1.7", "2.1.6.5")), + ?assertMatch(true, not ec_semver:pes("A.A", "A.B")), + ?assertMatch(true, not ec_semver:pes("2.5", "2.6.5")). + +parse_test() -> + ?assertEqual({1, {[],[]}}, ec_semver:parse(<<"1">>)), + ?assertEqual({{1,2,34},{[],[]}}, ec_semver:parse(<<"1.2.34">>)), + ?assertEqual({<<"a">>, {[],[]}}, ec_semver:parse(<<"a">>)), + ?assertEqual({{<<"a">>,<<"b">>}, {[],[]}}, ec_semver:parse(<<"a.b">>)), + ?assertEqual({1, {[],[]}}, ec_semver:parse(<<"1">>)), + ?assertEqual({{1,2}, {[],[]}}, ec_semver:parse(<<"1.2">>)), + ?assertEqual({{1,2,2}, {[],[]}}, ec_semver:parse(<<"1.2.2">>)), + ?assertEqual({{1,99,2}, {[],[]}}, ec_semver:parse(<<"1.99.2">>)), + ?assertEqual({{1,99,2}, {[<<"alpha">>],[]}}, ec_semver:parse(<<"1.99.2-alpha">>)), + ?assertEqual({{1,99,2}, {[<<"alpha">>,1], []}}, ec_semver:parse(<<"1.99.2-alpha.1">>)), + ?assertEqual({{1,99,2}, {[<<"pre-alpha">>,1], []}}, ec_semver:parse(<<"1.99.2-pre-alpha.1">>)), + ?assertEqual({{1,99,2}, {[], [<<"build">>, 1, <<"a36">>]}}, + ec_semver:parse(<<"1.99.2+build.1.a36">>)), + ?assertEqual({{1,99,2,44}, {[], [<<"build">>, 1, <<"a36">>]}}, + ec_semver:parse(<<"1.99.2.44+build.1.a36">>)), + ?assertEqual({{1,99,2}, {[<<"alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}, + ec_semver:parse("1.99.2-alpha.1+build.1.a36")), + ?assertEqual({{1,99,2}, {[<<"pre-alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}, + ec_semver:parse("1.99.2-pre-alpha.1+build.1.a36")). + +version_format_test() -> + ?assertEqual(["1", [], []], ec_semver:format({1, {[],[]}})), + ?assertEqual(["1", ".", "2", ".", "34", [], []], ec_semver:format({{1,2,34},{[],[]}})), + ?assertEqual(<<"a">>, erlang:iolist_to_binary(ec_semver:format({<<"a">>, {[],[]}}))), + ?assertEqual(<<"a.b">>, erlang:iolist_to_binary(ec_semver:format({{<<"a">>,<<"b">>}, {[],[]}}))), + ?assertEqual(<<"1">>, erlang:iolist_to_binary(ec_semver:format({1, {[],[]}}))), + ?assertEqual(<<"1.2">>, erlang:iolist_to_binary(ec_semver:format({{1,2}, {[],[]}}))), + ?assertEqual(<<"1.2.2">>, erlang:iolist_to_binary(ec_semver:format({{1,2,2}, {[],[]}}))), + ?assertEqual(<<"1.99.2">>, erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[],[]}}))), + ?assertEqual(<<"1.99.2-alpha">>, erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[<<"alpha">>],[]}}))), + ?assertEqual(<<"1.99.2-alpha.1">>, erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[<<"alpha">>,1], []}}))), + ?assertEqual(<<"1.99.2-pre-alpha.1">>, erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[<<"pre-alpha">>,1], []}}))), + ?assertEqual(<<"1.99.2+build.1.a36">>, + erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[], [<<"build">>, 1, <<"a36">>]}}))), + ?assertEqual(<<"1.99.2.44+build.1.a36">>, + erlang:iolist_to_binary(ec_semver:format({{1,99,2,44}, {[], [<<"build">>, 1, <<"a36">>]}}))), + ?assertEqual(<<"1.99.2-alpha.1+build.1.a36">>, + erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[<<"alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}))), + ?assertEqual(<<"1.99.2-pre-alpha.1+build.1.a36">>, + erlang:iolist_to_binary(ec_semver:format({{1,99,2}, {[<<"pre-alpha">>, 1], [<<"build">>, 1, <<"a36">>]}}))), + ?assertEqual(<<"1">>, erlang:iolist_to_binary(ec_semver:format({1, {[],[]}}))). From 8de367f996c7e420d672d4c7c60f15d12ad7ed51 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Tue, 5 Nov 2024 22:32:48 +0100 Subject: [PATCH 66/70] Moved `ec_file` tests into separate file Signed-off-by: Ariel Otilibili --- src/ec_file.erl | 89 ------------------------------------------ test/ec_file_tests.erl | 84 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 89 deletions(-) create mode 100644 test/ec_file_tests.erl diff --git a/src/ec_file.erl b/src/ec_file.erl index 4d19958..ddbee40 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -375,92 +375,3 @@ hex0(I) -> $0 + I. sub_files(From) -> {ok, SubFiles} = file:list_dir(From), [filename:join(From, SubFile) || SubFile <- SubFiles]. - -%%%=================================================================== -%%% Test Functions -%%%=================================================================== - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -setup_test() -> - Dir = insecure_mkdtemp(), - mkdir_path(Dir), - ?assertMatch(false, is_symlink(Dir)), - ?assertMatch(true, filelib:is_dir(Dir)). - -md5sum_test() -> - ?assertMatch("cfcd208495d565ef66e7dff9f98764da", md5sum("0")). - -sha1sum_test() -> - ?assertMatch("b6589fc6ab0dc82cf12099d1c2d40ab994e8410c", sha1sum("0")). - -file_test() -> - Dir = insecure_mkdtemp(), - TermFile = filename:join(Dir, "ec_file/dir/file.term"), - TermFileCopy = filename:join(Dir, "ec_file/dircopy/file.term"), - filelib:ensure_dir(TermFile), - filelib:ensure_dir(TermFileCopy), - write_term(TermFile, "term"), - ?assertMatch({ok, <<"\"term\". ">>}, read(TermFile)), - copy(filename:dirname(TermFile), - filename:dirname(TermFileCopy), - [recursive]). - -teardown_test() -> - Dir = insecure_mkdtemp(), - remove(Dir, [recursive]), - ?assertMatch(false, filelib:is_dir(Dir)). - -setup_base_and_target() -> - BaseDir = insecure_mkdtemp(), - DummyContents = <<"This should be deleted">>, - SourceDir = filename:join([BaseDir, "source"]), - ok = file:make_dir(SourceDir), - Name1 = filename:join([SourceDir, "fileone"]), - Name2 = filename:join([SourceDir, "filetwo"]), - Name3 = filename:join([SourceDir, "filethree"]), - NoName = filename:join([SourceDir, "noname"]), - - ok = file:write_file(Name1, DummyContents), - ok = file:write_file(Name2, DummyContents), - ok = file:write_file(Name3, DummyContents), - ok = file:write_file(NoName, DummyContents), - {BaseDir, SourceDir, {Name1, Name2, Name3, NoName}}. - -exists_test() -> - BaseDir = insecure_mkdtemp(), - SourceDir = filename:join([BaseDir, "source1"]), - NoName = filename:join([SourceDir, "noname"]), - ok = file:make_dir(SourceDir), - Name1 = filename:join([SourceDir, "fileone"]), - ok = file:write_file(Name1, <<"Testn">>), - ?assertMatch(true, exists(Name1)), - ?assertMatch(false, exists(NoName)). - -real_path_test() -> - BaseDir = "foo", - Dir = filename:absname(filename:join(BaseDir, "source1")), - LinkDir = filename:join([BaseDir, "link"]), - ok = mkdir_p(Dir), - file:make_symlink(Dir, LinkDir), - ?assertEqual(Dir, real_dir_path(LinkDir)), - ?assertEqual(directory, type(Dir)), - ?assertEqual(symlink, type(LinkDir)), - TermFile = filename:join(BaseDir, "test_file"), - ok = write_term(TermFile, foo), - ?assertEqual(file, type(TermFile)), - ?assertEqual(true, is_symlink(LinkDir)), - ?assertEqual(false, is_symlink(Dir)). - -find_test() -> - %% Create a directory in /tmp for the test. Clean everything afterwards - {BaseDir, _SourceDir, {Name1, Name2, Name3, _NoName}} = setup_base_and_target(), - Result = find(BaseDir, "file[a-z]+\$"), - ?assertMatch(3, erlang:length(Result)), - ?assertEqual(true, lists:member(Name1, Result)), - ?assertEqual(true, lists:member(Name2, Result)), - ?assertEqual(true, lists:member(Name3, Result)), - remove(BaseDir, [recursive]). - --endif. diff --git a/test/ec_file_tests.erl b/test/ec_file_tests.erl new file mode 100644 index 0000000..885f3dc --- /dev/null +++ b/test/ec_file_tests.erl @@ -0,0 +1,84 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_file_tests). + +-include_lib("eunit/include/eunit.hrl"). + +setup_test() -> + Dir = ec_file:insecure_mkdtemp(), + ec_file:mkdir_path(Dir), + ?assertMatch(false, ec_file:is_symlink(Dir)), + ?assertMatch(true, filelib:is_dir(Dir)). + +md5sum_test() -> + ?assertMatch("cfcd208495d565ef66e7dff9f98764da", ec_file:md5sum("0")). + +sha1sum_test() -> + ?assertMatch("b6589fc6ab0dc82cf12099d1c2d40ab994e8410c", ec_file:sha1sum("0")). + +file_test() -> + Dir = ec_file:insecure_mkdtemp(), + TermFile = filename:join(Dir, "ec_file/dir/file.term"), + TermFileCopy = filename:join(Dir, "ec_file/dircopy/file.term"), + filelib:ensure_dir(TermFile), + filelib:ensure_dir(TermFileCopy), + ec_file:write_term(TermFile, "term"), + ?assertMatch({ok, <<"\"term\". ">>}, ec_file:read(TermFile)), + ec_file:copy(filename:dirname(TermFile), + filename:dirname(TermFileCopy), + [recursive]). + +teardown_test() -> + Dir = ec_file:insecure_mkdtemp(), + ec_file:remove(Dir, [recursive]), + ?assertMatch(false, filelib:is_dir(Dir)). + +setup_base_and_target() -> + BaseDir = ec_file:insecure_mkdtemp(), + DummyContents = <<"This should be deleted">>, + SourceDir = filename:join([BaseDir, "source"]), + ok = file:make_dir(SourceDir), + Name1 = filename:join([SourceDir, "fileone"]), + Name2 = filename:join([SourceDir, "filetwo"]), + Name3 = filename:join([SourceDir, "filethree"]), + NoName = filename:join([SourceDir, "noname"]), + + ok = file:write_file(Name1, DummyContents), + ok = file:write_file(Name2, DummyContents), + ok = file:write_file(Name3, DummyContents), + ok = file:write_file(NoName, DummyContents), + {BaseDir, SourceDir, {Name1, Name2, Name3, NoName}}. + +exists_test() -> + BaseDir = ec_file:insecure_mkdtemp(), + SourceDir = filename:join([BaseDir, "source1"]), + NoName = filename:join([SourceDir, "noname"]), + ok = file:make_dir(SourceDir), + Name1 = filename:join([SourceDir, "fileone"]), + ok = file:write_file(Name1, <<"Testn">>), + ?assertMatch(true, ec_file:exists(Name1)), + ?assertMatch(false, ec_file:exists(NoName)). + +real_path_test() -> + BaseDir = "foo", + Dir = filename:absname(filename:join(BaseDir, "source1")), + LinkDir = filename:join([BaseDir, "link"]), + ok = ec_file:mkdir_p(Dir), + file:make_symlink(Dir, LinkDir), + ?assertEqual(Dir, ec_file:real_dir_path(LinkDir)), + ?assertEqual(directory, ec_file:type(Dir)), + ?assertEqual(symlink, ec_file:type(LinkDir)), + TermFile = filename:join(BaseDir, "test_file"), + ok = ec_file:write_term(TermFile, foo), + ?assertEqual(file, ec_file:type(TermFile)), + ?assertEqual(true, ec_file:is_symlink(LinkDir)), + ?assertEqual(false, ec_file:is_symlink(Dir)). + +find_test() -> + %% Create a directory in /tmp for the test. Clean everything afterwards + {BaseDir, _SourceDir, {Name1, Name2, Name3, _NoName}} = setup_base_and_target(), + Result = ec_file:find(BaseDir, "file[a-z]+\$"), + ?assertMatch(3, erlang:length(Result)), + ?assertEqual(true, lists:member(Name1, Result)), + ?assertEqual(true, lists:member(Name2, Result)), + ?assertEqual(true, lists:member(Name3, Result)), + ec_file:remove(BaseDir, [recursive]). From fa25b703e59edf0f24da3cfdd35c6250e9fe85b5 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Tue, 5 Nov 2024 22:33:20 +0100 Subject: [PATCH 67/70] Bumped actions/checkout to v4 https://github.com/actions/checkout/releases/tag/v4.2.2 Signed-off-by: Ariel Otilibili --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2178442..5e621b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: os: [ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Compile run: rebar3 compile From 47f7a5540ca9ffc2ebd82fe0a222b7e1f5f62672 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 18 Nov 2024 21:09:03 +0100 Subject: [PATCH 68/70] Moved `ec_lists` tests into separate file Part of #179 Signed-off-by: Ariel Otilibili --- src/ec_lists.erl | 181 ---------------------------------------- test/ec_lists_tests.erl | 172 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 181 deletions(-) create mode 100644 test/ec_lists_tests.erl diff --git a/src/ec_lists.erl b/src/ec_lists.erl index 0ae5204..fed76d0 100644 --- a/src/ec_lists.erl +++ b/src/ec_lists.erl @@ -63,184 +63,3 @@ fetch(Fun, List) when is_list(List), is_function(Fun) -> error -> throw(not_found) end. - -%%%=================================================================== -%%% Test Functions -%%%=================================================================== - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -find1_test() -> - TestData = [1, 2, 3, 4, 5, 6], - Result = find(fun(5) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch({ok, 5}, Result), - - Result2 = find(fun(37) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch(error, Result2). - -find2_test() -> - TestData = ["one", "two", "three", "four", "five", "six"], - Result = find(fun("five") -> - true; - (_) -> - false - end, - TestData), - ?assertMatch({ok, "five"}, Result), - - Result2 = find(fun(super_duper) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch(error, Result2). - - - -find3_test() -> - TestData = [{"one", 1}, {"two", 2}, {"three", 3}, {"four", 5}, {"five", 5}, - {"six", 6}], - Result = find(fun({"one", 1}) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch({ok, {"one", 1}}, Result), - - Result2 = find(fun([fo, bar, baz]) -> - true; - ({"onehundred", 100}) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch(error, Result2). - - - -fetch1_test() -> - TestData = [1, 2, 3, 4, 5, 6], - Result = fetch(fun(5) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch(5, Result), - - ?assertThrow(not_found, - fetch(fun(37) -> - true; - (_) -> - false - end, - TestData)). - -fetch2_test() -> - TestData = ["one", "two", "three", "four", "five", "six"], - Result = fetch(fun("five") -> - true; - (_) -> - false - end, - TestData), - ?assertMatch("five", Result), - - ?assertThrow(not_found, - fetch(fun(super_duper) -> - true; - (_) -> - false - end, - TestData)). - -fetch3_test() -> - TestData = [{"one", 1}, {"two", 2}, {"three", 3}, {"four", 5}, {"five", 5}, - {"six", 6}], - Result = fetch(fun({"one", 1}) -> - true; - (_) -> - false - end, - TestData), - ?assertMatch({"one", 1}, Result), - - ?assertThrow(not_found, - fetch(fun([fo, bar, baz]) -> - true; - ({"onehundred", 100}) -> - true; - (_) -> - false - end, - TestData)). - -search1_test() -> - TestData = [1, 2, 3, 4, 5, 6], - Result = search(fun(5) -> - {ok, 5}; - (_) -> - not_found - end, - TestData), - ?assertMatch({ok, 5, 5}, Result), - - Result2 = search(fun(37) -> - {ok, 37}; - (_) -> - not_found - end, - TestData), - ?assertMatch(not_found, Result2). - -search2_test() -> - TestData = [1, 2, 3, 4, 5, 6], - Result = search(fun(1) -> - {ok, 10}; - (_) -> - not_found - end, - TestData), - ?assertMatch({ok, 10, 1}, Result), - - Result2 = search(fun(6) -> - {ok, 37}; - (_) -> - not_found - end, - TestData), - ?assertMatch({ok, 37, 6}, Result2). - -search3_test() -> - TestData = [1, 2, 3, 4, 5, 6], - Result = search(fun(10) -> - {ok, 10}; - (_) -> - not_found - end, - TestData), - ?assertMatch(not_found, Result), - - Result2 = search(fun(-1) -> - {ok, 37}; - (_) -> - not_found - end, - TestData), - ?assertMatch(not_found, Result2). - --endif. diff --git a/test/ec_lists_tests.erl b/test/ec_lists_tests.erl new file mode 100644 index 0000000..f6f4025 --- /dev/null +++ b/test/ec_lists_tests.erl @@ -0,0 +1,172 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_lists_tests). + +-include_lib("eunit/include/eunit.hrl"). + +find1_test() -> + TestData = [1, 2, 3, 4, 5, 6], + Result = ec_lists:find(fun(5) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch({ok, 5}, Result), + + Result2 = ec_lists:find(fun(37) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch(error, Result2). + +find2_test() -> + TestData = ["one", "two", "three", "four", "five", "six"], + Result = ec_lists:find(fun("five") -> + true; + (_) -> + false + end, + TestData), + ?assertMatch({ok, "five"}, Result), + + Result2 = ec_lists:find(fun(super_duper) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch(error, Result2). + +find3_test() -> + TestData = [{"one", 1}, {"two", 2}, {"three", 3}, {"four", 5}, {"five", 5}, + {"six", 6}], + Result = ec_lists:find(fun({"one", 1}) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch({ok, {"one", 1}}, Result), + + Result2 = ec_lists:find(fun([fo, bar, baz]) -> + true; + ({"onehundred", 100}) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch(error, Result2). + +fetch1_test() -> + TestData = [1, 2, 3, 4, 5, 6], + Result = ec_lists:fetch(fun(5) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch(5, Result), + + ?assertThrow(not_found, + ec_lists:fetch(fun(37) -> + true; + (_) -> + false + end, + TestData)). + +fetch2_test() -> + TestData = ["one", "two", "three", "four", "five", "six"], + Result = ec_lists:fetch(fun("five") -> + true; + (_) -> + false + end, + TestData), + ?assertMatch("five", Result), + + ?assertThrow(not_found, + ec_lists:fetch(fun(super_duper) -> + true; + (_) -> + false + end, + TestData)). + +fetch3_test() -> + TestData = [{"one", 1}, {"two", 2}, {"three", 3}, {"four", 5}, {"five", 5}, + {"six", 6}], + Result = ec_lists:fetch(fun({"one", 1}) -> + true; + (_) -> + false + end, + TestData), + ?assertMatch({"one", 1}, Result), + + ?assertThrow(not_found, + ec_lists:fetch(fun([fo, bar, baz]) -> + true; + ({"onehundred", 100}) -> + true; + (_) -> + false + end, + TestData)). + +search1_test() -> + TestData = [1, 2, 3, 4, 5, 6], + Result = ec_lists:search(fun(5) -> + {ok, 5}; + (_) -> + not_found + end, + TestData), + ?assertMatch({ok, 5, 5}, Result), + + Result2 = ec_lists:search(fun(37) -> + {ok, 37}; + (_) -> + not_found + end, + TestData), + ?assertMatch(not_found, Result2). + +search2_test() -> + TestData = [1, 2, 3, 4, 5, 6], + Result = ec_lists:search(fun(1) -> + {ok, 10}; + (_) -> + not_found + end, + TestData), + ?assertMatch({ok, 10, 1}, Result), + + Result2 = ec_lists:search(fun(6) -> + {ok, 37}; + (_) -> + not_found + end, + TestData), + ?assertMatch({ok, 37, 6}, Result2). + +search3_test() -> + TestData = [1, 2, 3, 4, 5, 6], + Result = ec_lists:search(fun(10) -> + {ok, 10}; + (_) -> + not_found + end, + TestData), + ?assertMatch(not_found, Result), + + Result2 = ec_lists:search(fun(-1) -> + {ok, 37}; + (_) -> + not_found + end, + TestData), + ?assertMatch(not_found, Result2). From 1a08e33b83c64a74553d06cab8aef1a1dfc56815 Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Mon, 18 Nov 2024 21:10:54 +0100 Subject: [PATCH 69/70] Dropped minor versions in GitHub actions Lastest minor in given version will be taken. Signed-off-by: Ariel Otilibili --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5e621b1..9611b10 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: - otp_version: ['27.0', '25.3', '25.2', '23.2'] + otp_version: ['27', '25', '23'] os: [ubuntu-latest] steps: From a5712997ef0950add2f0a5f961403a28fa27f83c Mon Sep 17 00:00:00 2001 From: Ariel Otilibili Date: Sat, 23 Nov 2024 13:03:14 +0100 Subject: [PATCH 70/70] Moved `ec_gb_trees` tests into separate file Part of #179. Signed-off-by: Ariel Otilibili --- src/ec_gb_trees.erl | 76 -------------------------------------- test/ec_gb_trees_tests.erl | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 76 deletions(-) create mode 100644 test/ec_gb_trees_tests.erl diff --git a/src/ec_gb_trees.erl b/src/ec_gb_trees.erl index 7985fab..cde3f1b 100644 --- a/src/ec_gb_trees.erl +++ b/src/ec_gb_trees.erl @@ -135,79 +135,3 @@ from_list(List) when is_list(List) -> -spec keys(gb_trees:tree(K,_V)) -> [ec_dictionary:key(K)]. keys(Data) -> gb_trees:keys(Data). - -%%%=================================================================== -%%% Tests -%%%=================================================================== - - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). - -%% For me unit testing initially is about covering the obvious case. A -%% check to make sure that what you expect the tested functionality to -%% do, it actually does. As time goes on and people detect bugs you -%% add tests for those specific problems to the unit test suit. -%% -%% However, when getting started you can only test your basic -%% expectations. So here are the expectations I have for the add -%% functionality. -%% -%% 1) I can put arbitrary terms into the dictionary as keys -%% 2) I can put arbitrary terms into the dictionary as values -%% 3) When I put a value in the dictionary by a key, I can retrieve -%% that same value -%% 4) When I put a different value in the dictionary by key it does -%% not change other key value pairs. -%% 5) When I update a value the new value in available by the new key -%% 6) When a value does not exist a not found exception is created - -add_test() -> - Dict0 = ec_dictionary:new(ec_gb_trees), - - Key1 = foo, - Key2 = [1, 3], - Key3 = {"super"}, - Key4 = <<"fabulous">>, - Key5 = {"Sona", 2, <<"Zuper">>}, - - Value1 = Key5, - Value2 = Key4, - Value3 = Key2, - Value4 = Key3, - Value5 = Key1, - - Dict01 = ec_dictionary:add(Key1, Value1, Dict0), - Dict02 = ec_dictionary:add(Key3, Value3, - ec_dictionary:add(Key2, Value2, - Dict01)), - Dict1 = - ec_dictionary:add(Key5, Value5, - ec_dictionary:add(Key4, Value4, - Dict02)), - - ?assertMatch(Value1, ec_dictionary:get(Key1, Dict1)), - ?assertMatch(Value2, ec_dictionary:get(Key2, Dict1)), - ?assertMatch(Value3, ec_dictionary:get(Key3, Dict1)), - ?assertMatch(Value4, ec_dictionary:get(Key4, Dict1)), - ?assertMatch(Value5, ec_dictionary:get(Key5, Dict1)), - - - Dict2 = ec_dictionary:add(Key3, Value5, - ec_dictionary:add(Key2, Value4, Dict1)), - - - ?assertMatch(Value1, ec_dictionary:get(Key1, Dict2)), - ?assertMatch(Value4, ec_dictionary:get(Key2, Dict2)), - ?assertMatch(Value5, ec_dictionary:get(Key3, Dict2)), - ?assertMatch(Value4, ec_dictionary:get(Key4, Dict2)), - ?assertMatch(Value5, ec_dictionary:get(Key5, Dict2)), - - - ?assertThrow(not_found, ec_dictionary:get(should_blow_up, Dict2)), - ?assertThrow(not_found, ec_dictionary:get("This should blow up too", - Dict2)). - - - --endif. diff --git a/test/ec_gb_trees_tests.erl b/test/ec_gb_trees_tests.erl new file mode 100644 index 0000000..2c0ee12 --- /dev/null +++ b/test/ec_gb_trees_tests.erl @@ -0,0 +1,67 @@ +%%% @copyright 2024 Erlware, LLC. +-module(ec_gb_trees_tests). +-include_lib("eunit/include/eunit.hrl"). + +%% For me unit testing initially is about covering the obvious case. A +%% check to make sure that what you expect the tested functionality to +%% do, it actually does. As time goes on and people detect bugs you +%% add tests for those specific problems to the unit test suit. +%% +%% However, when getting started you can only test your basic +%% expectations. So here are the expectations I have for the add +%% functionality. +%% +%% 1) I can put arbitrary terms into the dictionary as keys +%% 2) I can put arbitrary terms into the dictionary as values +%% 3) When I put a value in the dictionary by a key, I can retrieve +%% that same value +%% 4) When I put a different value in the dictionary by key it does +%% not change other key value pairs. +%% 5) When I update a value the new value in available by the new key +%% 6) When a value does not exist a not found exception is created + +add_test() -> + Dict0 = ec_dictionary:new(ec_gb_trees), + + Key1 = foo, + Key2 = [1, 3], + Key3 = {"super"}, + Key4 = <<"fabulous">>, + Key5 = {"Sona", 2, <<"Zuper">>}, + + Value1 = Key5, + Value2 = Key4, + Value3 = Key2, + Value4 = Key3, + Value5 = Key1, + + Dict01 = ec_dictionary:add(Key1, Value1, Dict0), + Dict02 = ec_dictionary:add(Key3, Value3, + ec_dictionary:add(Key2, Value2, + Dict01)), + Dict1 = + ec_dictionary:add(Key5, Value5, + ec_dictionary:add(Key4, Value4, + Dict02)), + + ?assertMatch(Value1, ec_dictionary:get(Key1, Dict1)), + ?assertMatch(Value2, ec_dictionary:get(Key2, Dict1)), + ?assertMatch(Value3, ec_dictionary:get(Key3, Dict1)), + ?assertMatch(Value4, ec_dictionary:get(Key4, Dict1)), + ?assertMatch(Value5, ec_dictionary:get(Key5, Dict1)), + + + Dict2 = ec_dictionary:add(Key3, Value5, + ec_dictionary:add(Key2, Value4, Dict1)), + + + ?assertMatch(Value1, ec_dictionary:get(Key1, Dict2)), + ?assertMatch(Value4, ec_dictionary:get(Key2, Dict2)), + ?assertMatch(Value5, ec_dictionary:get(Key3, Dict2)), + ?assertMatch(Value4, ec_dictionary:get(Key4, Dict2)), + ?assertMatch(Value5, ec_dictionary:get(Key5, Dict2)), + + + ?assertThrow(not_found, ec_dictionary:get(should_blow_up, Dict2)), + ?assertThrow(not_found, ec_dictionary:get("This should blow up too", + Dict2)).