Merge remote-tracking branch 'canonical/next'

* canonical/next:
  massively expand the documentation in the README
  bring ec_plists up to erlware standards
  replace ec_plists with Stephan's plists
  add Stephen Marsh's plists to the system
  reorder default tasks so dialyzer is run after compile
  add a clean and rebuild task to makefile
  add fullpath to the makefile
  enable the rebar semver plugin on erlware_commons
  cleanup the rebar config
  support non-numeric versions in major/minor/patch/minor-patch
  support reasonable versioning for erlware_commons
  add exists to ec_file
  fix bug in ec_file:copy/3 spec
  export mkdir_p (this should have been done already)
  support four primary version numbers of in parsing
  minor whitespace cleanup for ec_semver
  provide the ability to format a version into a string as well as parse a version
  make sure the docs get run as part of a bare make
  fixes for edoc compilation
  compilation utilities for the implementors
This commit is contained in:
Eric Merritt 2012-10-30 13:03:29 -05:00
commit 1a39438f3c
10 changed files with 1469 additions and 300 deletions

View file

@ -14,9 +14,10 @@ endif
ERLWARE_COMMONS_PLT=$(CURDIR)/.erlware_commons_plt ERLWARE_COMMONS_PLT=$(CURDIR)/.erlware_commons_plt
.PHONY: all compile doc clean test dialyzer typer shell distclean pdf get-deps escript .PHONY: all compile doc clean test dialyzer typer shell distclean pdf get-deps \
rebuild
all: compile test dialyzer all: compile dialyzer doc test
get-deps: get-deps:
$(REBAR) get-deps $(REBAR) get-deps
@ -34,11 +35,11 @@ test: compile
$(ERLWARE_COMMONS_PLT): $(ERLWARE_COMMONS_PLT):
@echo Building local plt at $(ERLWARE_COMMONS_PLT) @echo Building local plt at $(ERLWARE_COMMONS_PLT)
@echo @echo
- dialyzer --output_plt $(ERLWARE_COMMONS_PLT) --build_plt \ - dialyzer --fullpath --output_plt $(ERLWARE_COMMONS_PLT) --build_plt \
--apps erts kernel stdlib eunit -r deps --apps erts kernel stdlib eunit -r deps
dialyzer: $(ERLWARE_COMMONS_PLT) dialyzer: $(ERLWARE_COMMONS_PLT)
dialyzer --plt $(ERLWARE_COMMONS_PLT) -Wrace_conditions --src src dialyzer --fullpath --plt $(ERLWARE_COMMONS_PLT) -Wrace_conditions --src src
typer: typer:
typer --plt $(ERLWARE_COMMONS_PLT) -r ./src typer --plt $(ERLWARE_COMMONS_PLT) -r ./src
@ -62,3 +63,5 @@ clean:
distclean: clean distclean: clean
rm -rf $(ERLWARE_COMMONS_PLT) rm -rf $(ERLWARE_COMMONS_PLT)
rm -rvf $(CURDIR)/deps/* rm -rvf $(CURDIR)/deps/*
rebuild: distclean all

104
README.md
View file

@ -23,3 +23,107 @@ Goals for the project
* High Quality * High Quality
* Well Documented * Well Documented
* Well Tested * Well Tested
Currently Available Modules/Systems
------------------------------------
### [ec_date](https://github.com/erlware/erlware_commons/blob/master/src/ec_date.erl)
This module formats erlang dates in the form {{Year, Month, Day},
{Hour, Minute, Second}} to printable strings, using (almost)
equivalent formatting rules as http://uk.php.net/date, US vs European
dates are disambiguated in the same way as
http://uk.php.net/manual/en/function.strtotime.php That is, Dates in
the m/d/y or d-m-y formats are disambiguated by looking at the
separator between the various components: if the separator is a slash
(/), then the American m/d/y is assumed; whereas if the separator is a
dash (-) or a dot (.), then the European d-m-y format is assumed. To
avoid potential ambiguity, it's best to use ISO 8601 (YYYY-MM-DD)
dates.
erlang has no concept of timezone so the following formats are not
implemented: B e I O P T Z formats c and r will also differ slightly
### [ec_file](https://github.com/erlware/erlware_commons/blob/master/src/ec_file.erl)
A set of commonly defined helper functions for files that are not
included in stdlib.
### [ec_plists](https://github.com/erlware/erlware_commons/blob/master/src/ec_plists.erl)
plists is a drop-in replacement for module <a
href="http://www.erlang.org/doc/man/lists.html">lists</a>, 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
failures. It can be configured, tuned, and tweaked to get optimal
performance while minimizing overhead.
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.
fold is the one exception, parallel fold is different from linear
fold. This module also include a simple mapreduce implementation, and
the function runmany. All the other functions are implemented with
runmany, which is as a generalization of parallel list operations.
### [ec_semver](https://github.com/erlware/erlware_commons/blob/master/src/ec_semver.erl)
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)
A set of additional list manipulation functions designed to supliment
the `lists` module in stdlib.
### [ec_talk](https://github.com/erlware/erlware_commons/blob/master/src/ec_talk.erl)
A set of simple utility functions to falicitate command line
communication with a user.
Signatures
-----------
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
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).
### [ec_dictionary](https://github.com/erlware/erlware_commons/blob/master/src/ec_dictionary.erl)
A signature that supports association of keys to values. A map cannot
contain duplicate keys; each key can map to at most one value.
### [ec_dict](https://github.com/erlware/erlware_commons/blob/master/src/ec_dict.erl)
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)
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)
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)
This provides an implementation of the ec_dictionary signature using
Robert Virding's rbdict module as a base. The function documentation
for ec_dictionary applies here as well.

View file

@ -1,12 +1,25 @@
%% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*- %% -*- mode: Erlang; fill-column: 80; comment-column: 75; -*-
%% These are all only compile time dependencies %% Dependencies ================================================================
{deps, [{neotoma, "", {deps, [{neotoma, "",
{git, "https://github.com/seancribbs/neotoma.git", {tag, "1.5"}}}, {git, "https://github.com/seancribbs/neotoma.git", {branch, master}}},
{proper, "", {git, "https://github.com/manopapad/proper.git", {branch, master}}}]}. {proper, "", {git, "https://github.com/manopapad/proper.git", {branch, master}}},
{rebar_vsn_plugin, ".*", {git, "https://github.com/erlware/rebar_vsn_plugin.git",
{branch, "master"}}}]}.
{erl_first_files, ["ec_dictionary"]}. {erl_first_files, ["ec_dictionary"]}.
%% Compiler Options ============================================================
{erl_opts, {erl_opts,
[debug_info, [debug_info,
warnings_as_errors]}. warnings_as_errors]}.
%% EUnit =======================================================================
{eunit_opts, [verbose,
{report, {eunit_surefire, [{dir, "."}]}}]}.
{cover_enabled, true}.
{cover_print_enabled, true}.
%% Rebar Plugins ==============================================================
{plugins, [rebar_vsn_plugin]}.

107
src/ec_compile.erl Normal file
View file

@ -0,0 +1,107 @@
%%%-------------------------------------------------------------------
%%% @author Eric Merritt <>
%%% @copyright (C) 2011, Erlware, LLC.
%%% @doc
%%% These are various utility functions to help with compiling and
%%% decompiling erlang source. They are mostly useful to the
%%% language/parse transform implementor.
%%% @end
%%%-------------------------------------------------------------------
-module(ec_compile).
-export([beam_to_erl_source/2,
erl_source_to_core_ast/1,
erl_source_to_erl_ast/1,
erl_source_to_asm/1,
erl_string_to_core_ast/1,
erl_string_to_erl_ast/1,
erl_string_to_asm/1]).
%%%===================================================================
%%% API
%%%===================================================================
%% @doc decompile a beam file that has been compiled with +debug_info
%% into a erlang source file
%%
%% @param BeamFName the name of the beamfile
%% @param ErlFName the name of the erlang file where the generated
%% source file will be output. This should *not* be the same as the
%% source file that created the beamfile unless you want to overwrite
%% it.
-spec beam_to_erl_source(string(), string()) -> ok | term().
beam_to_erl_source(BeamFName, ErlFName) ->
case beam_lib:chunks(BeamFName, [abstract_code]) of
{ok, {_, [{abstract_code, {raw_abstract_v1,Forms}}]}} ->
Src =
erl_prettypr:format(erl_syntax:form_list(tl(Forms))),
{ok, Fd} = file:open(ErlFName, [write]),
io:fwrite(Fd, "~s~n", [Src]),
file:close(Fd);
Error ->
Error
end.
%% @doc compile an erlang source file into a Core Erlang AST
%%
%% @param Path - The path to the erlang source file
-spec erl_source_to_core_ast(file:filename()) -> CoreAst::term().
erl_source_to_core_ast(Path) ->
{ok, Contents} = file:read_file(Path),
erl_string_to_core_ast(binary_to_list(Contents)).
%% @doc compile an erlang source file into an Erlang AST
%%
%% @param Path - The path to the erlang source file
-spec erl_source_to_erl_ast(file:filename()) -> ErlangAst::term().
erl_source_to_erl_ast(Path) ->
{ok, Contents} = file:read_file(Path),
erl_string_to_erl_ast(binary_to_list(Contents)).
%% @doc compile an erlang source file into erlang terms that represent
%% the relevant ASM
%%
%% @param Path - The path to the erlang source file
-spec erl_source_to_asm(file:filename()) -> ErlangAsm::term().
erl_source_to_asm(Path) ->
{ok, Contents} = file:read_file(Path),
erl_string_to_asm(binary_to_list(Contents)).
%% @doc compile a string representing an erlang expression into an
%% Erlang AST
%%
%% @param StringExpr - The path to the erlang source file
-spec erl_string_to_erl_ast(string()) -> ErlangAst::term().
erl_string_to_erl_ast(StringExpr) ->
Forms0 =
lists:foldl(fun(<<>>, Acc) ->
Acc;
(<<"\n\n">>, Acc) ->
Acc;
(El, Acc) ->
{ok, Tokens, _} =
erl_scan:string(binary_to_list(El)
++ "."),
[Tokens | Acc]
end, [], re:split(StringExpr, "\\.\n")),
%% No need to reverse. This will rereverse for us
lists:foldl(fun(Form, Forms) ->
{ok, ErlAST} = erl_parse:parse_form(Form),
[ErlAST | Forms]
end, [], Forms0).
%% @doc compile a string representing an erlang expression into a
%% Core Erlang AST
%%
%% @param StringExpr - The path to the erlang source file
-spec erl_string_to_core_ast(string()) -> CoreAst::term().
erl_string_to_core_ast(StringExpr) ->
compile:forms(erl_string_to_erl_ast(StringExpr), [to_core]).
%% @doc compile a string representing an erlang expression into a term
%% that represents the ASM
%%
%% @param StringExpr - The path to the erlang source file
-spec erl_string_to_asm(string()) -> ErlangAsm::term().
erl_string_to_asm(StringExpr) ->
compile:forms(erl_string_to_erl_ast(StringExpr), ['S']).

View file

@ -7,10 +7,12 @@
-module(ec_file). -module(ec_file).
-export([ -export([
exists/1,
copy/2, copy/2,
copy/3, copy/3,
insecure_mkdtemp/0, insecure_mkdtemp/0,
mkdir_path/1, mkdir_path/1,
mkdir_p/1,
find/2, find/2,
is_symlink/1, is_symlink/1,
remove/1, remove/1,
@ -35,12 +37,21 @@
%% Types %% Types
%%============================================================================ %%============================================================================
-type option() :: recursive. -type option() :: recursive.
-type void() :: ok.
%%%=================================================================== %%%===================================================================
%%% API %%% API
%%%=================================================================== %%%===================================================================
-spec exists(file:filename()) -> boolean().
exists(Filename) ->
case file:read_file_info(Filename) of
{ok, _} ->
true;
{error, _Reason} ->
false
end.
%% @doc copy an entire directory to another location. %% @doc copy an entire directory to another location.
-spec copy(file:name(), file:name(), Options::[option()]) -> void(). -spec copy(file:name(), file:name(), Options::[option()]) -> ok | {error, Reason::term()}.
copy(From, To, []) -> copy(From, To, []) ->
copy(From, To); copy(From, To);
copy(From, To, [recursive] = Options) -> copy(From, To, [recursive] = Options) ->
@ -229,7 +240,7 @@ tmp() ->
end. end.
%% Copy the subfiles of the From directory to the to directory. %% Copy the subfiles of the From directory to the to directory.
-spec copy_subfiles(file:name(), file:name(), [option()]) -> void(). -spec copy_subfiles(file:name(), file:name(), [option()]) -> {error, Reason::term()} | ok.
copy_subfiles(From, To, Options) -> copy_subfiles(From, To, Options) ->
Fun = Fun =
fun(ChildFrom) -> fun(ChildFrom) ->
@ -313,6 +324,16 @@ setup_base_and_target() ->
ok = file:write_file(NoName, DummyContents), ok = file:write_file(NoName, DummyContents),
{BaseDir, SourceDir, {Name1, Name2, Name3, NoName}}. {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)).
find_test() -> find_test() ->
%% Create a directory in /tmp for the test. Clean everything afterwards %% Create a directory in /tmp for the test. Clean everything afterwards
{BaseDir, _SourceDir, {Name1, Name2, Name3, _NoName}} = setup_base_and_target(), {BaseDir, _SourceDir, {Name1, Name2, Name3, _NoName}} = setup_base_and_target(),

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,4 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2011, Erlware LLC %%% @copyright (C) 2011, Erlware LLC
%%% @doc %%% @doc
@ -8,6 +9,7 @@
-module(ec_semver). -module(ec_semver).
-export([parse/1, -export([parse/1,
format/1,
eql/2, eql/2,
gt/2, gt/2,
gte/2, gte/2,
@ -27,15 +29,20 @@
%%% Public Types %%% Public Types
%%%=================================================================== %%%===================================================================
-type major_minor_patch() :: -type version_element() :: non_neg_integer() | binary().
non_neg_integer()
| {non_neg_integer(), non_neg_integer()}
| {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
-type alpha_part() :: integer() | binary(). -type major_minor_patch_minpatch() ::
version_element()
| {version_element(), version_element()}
| {version_element(), version_element(), version_element()}
| {version_element(), version_element(),
version_element(), version_element()}.
-type semver() :: {major_minor_patch(), {PreReleaseVersion::[alpha_part()], -type alpha_part() :: integer() | binary() | string().
BuildVersion::[alpha_part()]}}. -type alpha_info() :: {PreRelease::[alpha_part()],
BuildVersion::[alpha_part()]}.
-type semver() :: {major_minor_patch_minpatch(), alpha_info()}.
-type version_string() :: string() | binary(). -type version_string() :: string() | binary().
@ -48,12 +55,58 @@
%% @doc parse a string or binary into a valid semver representation %% @doc parse a string or binary into a valid semver representation
-spec parse(any_version()) -> semver(). -spec parse(any_version()) -> semver().
parse(Version) when erlang:is_list(Version) -> parse(Version) when erlang:is_list(Version) ->
ec_semver_parser:parse(Version); case ec_semver_parser:parse(Version) of
{fail, _} ->
{erlang:iolist_to_binary(Version), {[],[]}};
Good ->
Good
end;
parse(Version) when erlang:is_binary(Version) -> parse(Version) when erlang:is_binary(Version) ->
ec_semver_parser:parse(Version); case ec_semver_parser:parse(Version) of
{fail, _} ->
{Version, {[],[]}};
Good ->
Good
end;
parse(Version) -> parse(Version) ->
Version. Version.
-spec format(semver()) -> iolist().
format({Maj, {AlphaPart, BuildPart}})
when erlang:is_integer(Maj);
erlang:is_binary(Maj) ->
[format_version_part(Maj),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)];
format({{Maj, Min}, {AlphaPart, BuildPart}}) ->
[format_version_part(Maj), ".",
format_version_part(Min),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)];
format({{Maj, Min, Patch}, {AlphaPart, BuildPart}}) ->
[format_version_part(Maj), ".",
format_version_part(Min), ".",
format_version_part(Patch),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)];
format({{Maj, Min, Patch, MinPatch}, {AlphaPart, BuildPart}}) ->
[format_version_part(Maj), ".",
format_version_part(Min), ".",
format_version_part(Patch), ".",
format_version_part(MinPatch),
format_vsn_rest(<<"-">>, AlphaPart),
format_vsn_rest(<<"+">>, BuildPart)].
-spec format_version_part(integer() | binary()) -> iolist().
format_version_part(Vsn)
when erlang:is_integer(Vsn) ->
erlang:integer_to_list(Vsn);
format_version_part(Vsn)
when erlang:is_binary(Vsn) ->
Vsn.
%% @doc test for quality between semver versions %% @doc test for quality between semver versions
-spec eql(any_version(), any_version()) -> boolean(). -spec eql(any_version(), any_version()) -> boolean().
eql(VsnA, VsnB) -> eql(VsnA, VsnB) ->
@ -141,8 +194,8 @@ between(Vsn1, Vsn2, VsnMatch) ->
%% revisions and "~> 2.6.5" is pessimistic about future minor %% revisions and "~> 2.6.5" is pessimistic about future minor
%% revisions. %% revisions.
%% %%
%% "~> 2.6" matches cookbooks >= 2.6.0 AND < 3.0.0 %% "~> 2.6" matches cookbooks >= 2.6.0 AND &lt; 3.0.0
%% "~> 2.6.5" matches cookbooks >= 2.6.5 AND < 2.7.0 %% "~> 2.6.5" matches cookbooks >= 2.6.5 AND &lt; 2.7.0
pes(VsnA, VsnB) -> pes(VsnA, VsnB) ->
internal_pes(parse(VsnA), parse(VsnB)). internal_pes(parse(VsnA), parse(VsnB)).
@ -152,17 +205,20 @@ pes(VsnA, VsnB) ->
%% @doc helper function for the peg grammer to parse the iolist into a semver %% @doc helper function for the peg grammer to parse the iolist into a semver
-spec internal_parse_version(iolist()) -> semver(). -spec internal_parse_version(iolist()) -> semver().
internal_parse_version([MMP, AlphaPart, BuildPart, _]) -> internal_parse_version([MMP, AlphaPart, BuildPart, _]) ->
{parse_major_minor_patch(MMP), {parse_alpha_part(AlphaPart), {parse_major_minor_patch_minpatch(MMP), {parse_alpha_part(AlphaPart),
parse_alpha_part(BuildPart)}}. 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 grammer to parse the iolist into a major_minor_patch
-spec parse_major_minor_patch(iolist()) -> major_minor_patch(). -spec parse_major_minor_patch_minpatch(iolist()) -> major_minor_patch_minpatch().
parse_major_minor_patch([MajVsn, [], []]) -> parse_major_minor_patch_minpatch([MajVsn, [], [], []]) ->
MajVsn; MajVsn;
parse_major_minor_patch([MajVsn, [<<".">>, MinVsn], []]) -> parse_major_minor_patch_minpatch([MajVsn, [<<".">>, MinVsn], [], []]) ->
{MajVsn, MinVsn}; {MajVsn, MinVsn};
parse_major_minor_patch([MajVsn, [<<".">>, MinVsn], [<<".">>, PatchVsn]]) -> parse_major_minor_patch_minpatch([MajVsn, [<<".">>, MinVsn], [<<".">>, PatchVsn], []]) ->
{MajVsn, MinVsn, PatchVsn}. {MajVsn, MinVsn, PatchVsn};
parse_major_minor_patch_minpatch([MajVsn, [<<".">>, MinVsn],
[<<".">>, PatchVsn], [<<".">>, MinPatch]]) ->
{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 grammer to parse the iolist into an alpha part
-spec parse_alpha_part(iolist()) -> [alpha_part()]. -spec parse_alpha_part(iolist()) -> [alpha_part()].
@ -189,32 +245,59 @@ format_alpha_part([<<".">>, AlphaPart]) ->
%%%=================================================================== %%%===================================================================
%%% Internal Functions %%% Internal Functions
%%%=================================================================== %%%===================================================================
-spec to_list(integer() | binary() | string()) -> string() | binary().
to_list(Detail) when erlang:is_integer(Detail) ->
erlang:integer_to_list(Detail);
to_list(Detail) when erlang:is_list(Detail); erlang:is_binary(Detail) ->
Detail.
-spec format_vsn_rest(binary() | string(), [integer() | binary()]) -> iolist().
format_vsn_rest(_TypeMark, []) ->
[];
format_vsn_rest(TypeMark, [Head | Rest]) ->
[TypeMark, Head |
[[".", to_list(Detail)] || Detail <- Rest]].
%% @doc normalize the semver so they can be compared %% @doc normalize the semver so they can be compared
-spec normalize(semver()) -> semver(). -spec normalize(semver()) -> semver().
normalize({Vsn, Rest}) normalize({Vsn, Rest})
when erlang:is_integer(Vsn) -> when erlang:is_binary(Vsn);
{{Vsn, 0, 0}, Rest}; erlang:is_integer(Vsn) ->
{{Vsn, 0, 0, 0}, Rest};
normalize({{Maj, Min}, Rest}) -> normalize({{Maj, Min}, Rest}) ->
{{Maj, Min, 0}, Rest}; {{Maj, Min, 0, 0}, Rest};
normalize(Other) -> normalize({{Maj, Min, Patch}, Rest}) ->
{{Maj, Min, Patch, 0}, Rest};
normalize(Other = {{_, _, _, _}, {_,_}}) ->
Other. Other.
%% @doc to do the pessimistic compare we need a parsed semver. This is %% @doc to do the pessimistic compare we need a parsed semver. This is
%% the internal implementation of the of the pessimistic run. The %% the internal implementation of the of the pessimistic run. The
%% external just ensures that versions are parsed. %% external just ensures that versions are parsed.
internal_pes(VsnA, {{LM, LMI}, _}) -> -spec internal_pes(semver(), semver()) -> boolean().
internal_pes(VsnA, {{LM, LMI}, _})
when erlang:is_integer(LM),
erlang:is_integer(LMI) ->
gte(VsnA, {{LM, LMI, 0}, {[], []}}) andalso gte(VsnA, {{LM, LMI, 0}, {[], []}}) andalso
lt(VsnA, {{LM + 1, 0, 0}, {[], []}}); lt(VsnA, {{LM + 1, 0, 0, 0}, {[], []}});
internal_pes(VsnA, {{LM, LMI, LP}, _}) -> internal_pes(VsnA, {{LM, LMI, LP}, _})
when erlang:is_integer(LM),
erlang:is_integer(LMI),
erlang:is_integer(LP) ->
gte(VsnA, {{LM, LMI, LP}, {[], []}}) gte(VsnA, {{LM, LMI, LP}, {[], []}})
andalso andalso
lt(VsnA, {{LM, LMI + 1, 0}, {[], []}}); lt(VsnA, {{LM, LMI + 1, 0, 0}, {[], []}});
internal_pes(VsnA, {{LM, LMI, LP, LMP}, _})
when erlang:is_integer(LM),
erlang:is_integer(LMI),
erlang:is_integer(LP),
erlang:is_integer(LMP) ->
gte(VsnA, {{LM, LMI, LP, LMP}, {[], []}})
andalso
lt(VsnA, {{LM, LMI, LP + 1, 0}, {[], []}});
internal_pes(Vsn, LVsn) -> internal_pes(Vsn, LVsn) ->
gte(Vsn, LVsn). gte(Vsn, LVsn).
%%%=================================================================== %%%===================================================================
%%% Test Functions %%% Test Functions
%%%=================================================================== %%%===================================================================
@ -231,25 +314,43 @@ eql_test() ->
"1.0.0")), "1.0.0")),
?assertMatch(true, eql("1.0.0", ?assertMatch(true, eql("1.0.0",
"1")), "1")),
?assertMatch(true, eql("1.0.0.0",
"1")),
?assertMatch(true, eql("1.0+alpha.1", ?assertMatch(true, eql("1.0+alpha.1",
"1.0.0+alpha.1")), "1.0.0+alpha.1")),
?assertMatch(true, eql("1.0-alpha.1+build.1", ?assertMatch(true, eql("1.0-alpha.1+build.1",
"1.0.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("aa", "aa")),
?assertMatch(true, eql("AA.BB", "AA.BB")),
?assertMatch(true, eql("BBB-super", "BBB-super")),
?assertMatch(true, not eql("1.0.0", ?assertMatch(true, not eql("1.0.0",
"1.0.1")), "1.0.1")),
?assertMatch(true, not eql("1.0.0-alpha", ?assertMatch(true, not eql("1.0.0-alpha",
"1.0.1+alpha")), "1.0.1+alpha")),
?assertMatch(true, not eql("1.0.0+build.1", ?assertMatch(true, not eql("1.0.0+build.1",
"1.0.1+build.2")). "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() -> gt_test() ->
?assertMatch(true, gt("1.0.0-alpha.1", ?assertMatch(true, gt("1.0.0-alpha.1",
"1.0.0-alpha")), "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", ?assertMatch(true, gt("1.0.0-beta.2",
"1.0.0-alpha.1")), "1.0.0-alpha.1")),
?assertMatch(true, gt("1.0.0-beta.11", ?assertMatch(true, gt("1.0.0-beta.11",
"1.0.0-beta.2")), "1.0.0-beta.2")),
?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", "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-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", "1.0.0-rc.1+build.1")),
@ -257,10 +358,16 @@ gt_test() ->
?assertMatch(true, gt("1.3.7+build", "1.0.0+0.3.7")), ?assertMatch(true, gt("1.3.7+build", "1.0.0+0.3.7")),
?assertMatch(true, gt("1.3.7+build.2.b8f12d7", ?assertMatch(true, gt("1.3.7+build.2.b8f12d7",
"1.3.7+build")), "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", ?assertMatch(true, gt("1.3.7+build.11.e0f985a",
"1.3.7+build.2.b8f12d7")), "1.3.7+build.2.b8f12d7")),
?assertMatch(true, gt("aa.cc",
"aa.bb")),
?assertMatch(true, not gt("1.0.0-alpha", ?assertMatch(true, not gt("1.0.0-alpha",
"1.0.0-alpha.1")), "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", ?assertMatch(true, not gt("1.0.0-alpha.1",
"1.0.0-beta.2")), "1.0.0-beta.2")),
?assertMatch(true, not gt("1.0.0-beta.2", ?assertMatch(true, not gt("1.0.0-beta.2",
@ -283,6 +390,10 @@ gt_test() ->
"1.0.0-alpha")), "1.0.0-alpha")),
?assertMatch(true, not gt("1", ?assertMatch(true, not gt("1",
"1.0.0")), "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", ?assertMatch(true, not gt("1.0",
"1.0.0")), "1.0.0")),
?assertMatch(true, not gt("1.0.0", ?assertMatch(true, not gt("1.0.0",
@ -295,12 +406,16 @@ gt_test() ->
lt_test() -> lt_test() ->
?assertMatch(true, lt("1.0.0-alpha", ?assertMatch(true, lt("1.0.0-alpha",
"1.0.0-alpha.1")), "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", ?assertMatch(true, lt("1.0.0-alpha.1",
"1.0.0-beta.2")), "1.0.0-beta.2")),
?assertMatch(true, lt("1.0.0-beta.2", ?assertMatch(true, lt("1.0.0-beta.2",
"1.0.0-beta.11")), "1.0.0-beta.11")),
?assertMatch(true, lt("1.0.0-beta.11", ?assertMatch(true, lt("1.0.0-beta.11",
"1.0.0-rc.1")), "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", ?assertMatch(true, lt("1.0.0-rc.1",
"1.0.0-rc.1+build.1")), "1.0.0-rc.1+build.1")),
?assertMatch(true, lt("1.0.0-rc.1+build.1", ?assertMatch(true, lt("1.0.0-rc.1+build.1",
@ -317,12 +432,17 @@ lt_test() ->
"1.0.0-alpha")), "1.0.0-alpha")),
?assertMatch(true, not lt("1", ?assertMatch(true, not lt("1",
"1.0.0")), "1.0.0")),
?assertMatch(true, lt("1",
"1.0.0.1")),
?assertMatch(true, lt("AA.DD",
"AA.EE")),
?assertMatch(true, not lt("1.0", ?assertMatch(true, not lt("1.0",
"1.0.0")), "1.0.0")),
?assertMatch(true, not lt("1.0.0", ?assertMatch(true, not lt("1.0.0.0",
"1")), "1")),
?assertMatch(true, not lt("1.0+alpha.1", ?assertMatch(true, not lt("1.0+alpha.1",
"1.0.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", ?assertMatch(true, not lt("1.0-alpha.1+build.1",
"1.0.0-alpha.1+build.1")), "1.0.0-alpha.1+build.1")),
?assertMatch(true, not lt("1.0.0-alpha.1", ?assertMatch(true, not lt("1.0.0-alpha.1",
@ -341,7 +461,6 @@ lt_test() ->
?assertMatch(true, not lt("1.3.7+build.11.e0f985a", ?assertMatch(true, not lt("1.3.7+build.11.e0f985a",
"1.3.7+build.2.b8f12d7")). "1.3.7+build.2.b8f12d7")).
gte_test() -> gte_test() ->
?assertMatch(true, gte("1.0.0-alpha", ?assertMatch(true, gte("1.0.0-alpha",
"1.0.0-alpha")), "1.0.0-alpha")),
@ -355,18 +474,25 @@ gte_test() ->
?assertMatch(true, gte("1.0.0", ?assertMatch(true, gte("1.0.0",
"1")), "1")),
?assertMatch(true, gte("1.0.0.0",
"1")),
?assertMatch(true, gte("1.0+alpha.1", ?assertMatch(true, gte("1.0+alpha.1",
"1.0.0+alpha.1")), "1.0.0+alpha.1")),
?assertMatch(true, gte("1.0-alpha.1+build.1", ?assertMatch(true, gte("1.0-alpha.1+build.1",
"1.0.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", ?assertMatch(true, gte("1.0.0-alpha.1",
"1.0.0-alpha")), "1.0.0-alpha")),
?assertMatch(true, gte("1.0.0-beta.2", ?assertMatch(true, gte("1.0.0-beta.2",
"1.0.0-alpha.1")), "1.0.0-alpha.1")),
?assertMatch(true, gte("1.0.0-beta.11", ?assertMatch(true, gte("1.0.0-beta.11",
"1.0.0-beta.2")), "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", "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-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", "1.0.0-rc.1+build.1")),
@ -378,6 +504,7 @@ gte_test() ->
"1.3.7+build.2.b8f12d7")), "1.3.7+build.2.b8f12d7")),
?assertMatch(true, not gte("1.0.0-alpha", ?assertMatch(true, not gte("1.0.0-alpha",
"1.0.0-alpha.1")), "1.0.0-alpha.1")),
?assertMatch(true, not gte("CC", "DD")),
?assertMatch(true, not gte("1.0.0-alpha.1", ?assertMatch(true, not gte("1.0.0-alpha.1",
"1.0.0-beta.2")), "1.0.0-beta.2")),
?assertMatch(true, not gte("1.0.0-beta.2", ?assertMatch(true, not gte("1.0.0-beta.2",
@ -429,25 +556,29 @@ lte_test() ->
"1")), "1")),
?assertMatch(true, lte("1.0+alpha.1", ?assertMatch(true, lte("1.0+alpha.1",
"1.0.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", ?assertMatch(true, lte("1.0-alpha.1+build.1",
"1.0.0-alpha.1+build.1")), "1.0.0-alpha.1+build.1")),
?assertMatch(true, not lt("1.0.0-alpha.1", ?assertMatch(true, lte("aa","cc")),
?assertMatch(true, lte("cc","cc")),
?assertMatch(true, not lte("1.0.0-alpha.1",
"1.0.0-alpha")), "1.0.0-alpha")),
?assertMatch(true, not lt("1.0.0-beta.2", ?assertMatch(true, not lte("cc", "aa")),
?assertMatch(true, not lte("1.0.0-beta.2",
"1.0.0-alpha.1")), "1.0.0-alpha.1")),
?assertMatch(true, not lt("1.0.0-beta.11", ?assertMatch(true, not lte("1.0.0-beta.11",
"1.0.0-beta.2")), "1.0.0-beta.2")),
?assertMatch(true, not lt("1.0.0-rc.1", "1.0.0-beta.11")), ?assertMatch(true, not lte("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 lte("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 lte("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 lte("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 lte("1.3.7+build", "1.0.0+0.3.7")),
?assertMatch(true, not lt("1.3.7+build.2.b8f12d7", ?assertMatch(true, not lte("1.3.7+build.2.b8f12d7",
"1.3.7+build")), "1.3.7+build")),
?assertMatch(true, not lt("1.3.7+build.11.e0f985a", ?assertMatch(true, not lte("1.3.7+build.11.e0f985a",
"1.3.7+build.2.b8f12d7")). "1.3.7+build.2.b8f12d7")).
between_test() -> between_test() ->
?assertMatch(true, between("1.0.0-alpha", ?assertMatch(true, between("1.0.0-alpha",
"1.0.0-alpha.3", "1.0.0-alpha.3",
@ -464,6 +595,10 @@ between_test() ->
?assertMatch(true, between("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.3",
"1.0.0-rc.1+build.1")), "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", ?assertMatch(true, between("1.0.0-rc.1+build.1",
"1.0.0", "1.0.0",
"1.0.0-rc.33")), "1.0.0-rc.33")),
@ -488,6 +623,10 @@ between_test() ->
?assertMatch(true, between("1.0", ?assertMatch(true, between("1.0",
"1.0.0", "1.0.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", ?assertMatch(true, between("1.0.0",
"1", "1",
"1")), "1")),
@ -497,6 +636,9 @@ between_test() ->
?assertMatch(true, between("1.0-alpha.1+build.1", ?assertMatch(true, between("1.0-alpha.1+build.1",
"1.0.0-alpha.1+build.1", "1.0.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", ?assertMatch(true, not between("1.0.0-alpha.1",
"1.0.0-alpha.22", "1.0.0-alpha.22",
"1.0.0")), "1.0.0")),
@ -506,13 +648,16 @@ between_test() ->
?assertMatch(true, not between("1.0.0-beta.1", ?assertMatch(true, not between("1.0.0-beta.1",
"1.0.0-beta.11", "1.0.0-beta.11",
"1.0.0-alpha")), "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("1.0.0-beta.11", "1.0.0-rc.1",
"1.0.0-rc.22")),
?assertMatch(true, not between("aaa", "ddd", "zzz")).
pes_test() -> pes_test() ->
?assertMatch(true, pes("2.6.0", "2.6")), ?assertMatch(true, pes("2.6.0", "2.6")),
?assertMatch(true, pes("2.7", "2.6")), ?assertMatch(true, pes("2.7", "2.6")),
?assertMatch(true, pes("2.8", "2.6")), ?assertMatch(true, pes("2.8", "2.6")),
?assertMatch(true, pes("2.9", "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("3.0.0", "2.6")),
?assertMatch(true, not pes("2.5", "2.6")), ?assertMatch(true, not pes("2.5", "2.6")),
?assertMatch(true, pes("2.6.5", "2.6.5")), ?assertMatch(true, pes("2.6.5", "2.6.5")),
@ -520,7 +665,29 @@ pes_test() ->
?assertMatch(true, pes("2.6.7", "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.8", "2.6.5")),
?assertMatch(true, pes("2.6.9", "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.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")). ?assertMatch(true, not pes("2.5", "2.6.5")).
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+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">>, erlang:iolist_to_binary(format({1, {[],[]}}))).
-endif. -endif.

View file

@ -1,11 +1,12 @@
semver <- major_minor_patch ("-" alpha_part ("." alpha_part)*)? ("+" alpha_part ("." alpha_part)*)? !. semver <- major_minor_patch_min_patch ("-" alpha_part ("." alpha_part)*)? ("+" alpha_part ("." alpha_part)*)? !.
` ec_semver:internal_parse_version(Node) ` ; ` ec_semver:internal_parse_version(Node) ` ;
major_minor_patch <- version_part ("." version_part)? ("." version_part)? ; major_minor_patch_min_patch <- version_part ("." version_part)? ("." version_part)? ("." version_part)? ;
version_part <- [0-9]+ `erlang:list_to_integer(erlang:binary_to_list(erlang:iolist_to_binary(Node)))` ; version_part <- numeric_part / alpha_part ;
alpha_part <- [A-Za-z0-9-]+ ; numeric_part <- [0-9]+ `erlang:list_to_integer(erlang:binary_to_list(erlang:iolist_to_binary(Node)))` ;
alpha_part <- [A-Za-z0-9]+ `erlang:iolist_to_binary(Node)` ;
%% This only exists to get around a bug in erlang where if %% This only exists to get around a bug in erlang where if
%% warnings_as_errors is specified `nowarn` directives are ignored %% warnings_as_errors is specified `nowarn` directives are ignored

View file

@ -1,7 +1,7 @@
%% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*- %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*-
{application, erlware_commons, {application, erlware_commons,
[{description, "Additional standard library for Erlang"}, [{description, "Additional standard library for Erlang"},
{vsn, "0.8.0"}, {vsn, "semver"},
{modules, []}, {modules, []},
{registered, []}, {registered, []},
{applications, [kernel, stdlib]}]}. {applications, [kernel, stdlib]}]}.

75
test/ec_plists_tests.erl Normal file
View file

@ -0,0 +1,75 @@
%%% @copyright Erlware, LLC.
-module(ec_plists_tests).
-include_lib("eunit/include/eunit.hrl").
%%%===================================================================
%%% Tests
%%%===================================================================
map_good_test() ->
Results = ec_plists:map(fun(_) ->
ok
end,
lists:seq(1, 5)),
?assertMatch([ok, ok, ok, ok, ok],
Results).
ftmap_good_test() ->
Results = ec_plists:ftmap(fun(_) ->
ok
end,
lists:seq(1, 3)),
?assertMatch([{value, ok}, {value, ok}, {value, ok}],
Results).
filter_good_test() ->
Results = ec_plists:filter(fun(X) ->
X == show
end,
[show, show, remove]),
?assertMatch([show, show],
Results).
map_timeout_test() ->
?assertExit(timeout,
ec_plists:map(fun(T) ->
timer:sleep(T),
T
end,
[1, 100], {timeout, 10})).
ftmap_timeout_test() ->
?assertExit(timeout,
ec_plists:ftmap(fun(X) ->
timer:sleep(X),
true
end,
[100, 1], {timeout, 10})).
filter_timeout_test() ->
?assertExit(timeout,
ec_plists:filter(fun(T) ->
timer:sleep(T),
T == 1
end,
[1, 100], {timeout, 10})).
map_bad_test() ->
?assertExit({{nocatch,test_exception}, _},
ec_plists:map(fun(_) ->
erlang:throw(test_exception)
end,
lists:seq(1, 5))).
ftmap_bad_test() ->
Results =
ec_plists:ftmap(fun(2) ->
erlang:throw(test_exception);
(N) ->
N
end,
lists:seq(1, 5)),
?assertMatch([{value, 1}, {error,{throw,test_exception}}, {value, 3},
{value, 4}, {value, 5}] , Results).