diff --git a/Makefile b/Makefile index 3f9a316..70a920e 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,17 @@ PROJECT = katana -DEPS = inaka_aleppo xref_runner elvis_core +DEPS = xref_runner TEST_DEPS = mixer SHELL_DEPS = sync BUILD_DEPS = inaka_mk hexer_mk LOCAL_DEPS = xmerl tools compiler syntax_tools common_test inets ssl test_server hipe public_key dialyzer wx -# Prevents erlang.mk from downloading elvis_core's katana dependency -IGNORE_DEPS += katana - -dep_inaka_aleppo = hex 0.9.9 dep_xref_runner = hex 0.2.4 dep_mixer = git https://github.com/inaka/mixer.git 0.1.4 dep_elvis_core = git https://github.com/inaka/elvis_core.git efa6df5 dep_sync = git https://github.com/rustyio/sync.git 9c78e7b dep_inaka_mk = git https://github.com/inaka/inaka.mk.git 1.0.0 -dep_hexer_mk = git https://github.com/inaka/hexer.mk.git 1.0.1 +dep_hexer_mk = git https://github.com/inaka/hexer.mk.git 1.1.0 DEP_PLUGINS = inaka_mk hexer_mk diff --git a/README.md b/README.md index 5c87cc1..f94f3c3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ erlang katana ====== -![samuari](https://raw.githubusercontent.com/unbalancedparentheses/katana/master/images/samurai.jpg) +![samurai](https://raw.githubusercontent.com/unbalancedparentheses/katana/master/images/samurai.jpg) Even if you love Erlang as we do, from time to time you might ask yourself why some functions you normally find in other languages are not part of the erlang's @@ -17,7 +17,7 @@ To sum up this is a grab bag of useful functions (ideally). # Contact Us For **questions** or **general comments** regarding the use of this library, -please use our public [hipchat room](https://www.hipchat.com/gpBpW3SsT). +please use our public [hipchat room](https://inaka.net/hipchat). If you find any **bugs** or have a **problem** while using this library, please [open an issue](https://github.com/inaka/erlang-katana/issues/new) in this repo @@ -115,50 +115,6 @@ Possible error values for the body assert are: - {error, {nomatch, Pattern, Body}} if the body does not match the regex. - {nomatch, ResBody, Body}} if the body does not match the text. -### `ktn_meta_SUITE` - -#### Goals -The **meta** suite lets you check your code with `dialyzer`, `xref` and `elvis`. - -#### Usage -To include the suite in your project, you only need to invoke its functions from a common_test suite. If you use [mixer](https://github.com/inaka/mixer) you can just do… - -```erlang --module(your_meta_SUITE). - --include_lib("mixer/include/mixer.hrl"). --mixin([ktn_meta_SUITE]). - --export([init_per_suite/1]). - -init_per_suite(Config) -> [{application, your_app} | Config]. -``` - -Of course, you can choose what functions to include, for example if you want `dialyzer` but not `elvis` nor `xref` you can do… - -```erlang --mixin([{ ktn_meta_SUITE - , [ dialyzer/1 - ] - }]). - --export([all/0]). - -all() -> [dialyzer]. -``` - -#### Configuration -`ktn_meta_SUITE` uses the following configuration parameters that you can add to the common_test config for your test suite: - -| Parameter | Description | Default | -|-----------|-------------|---------| -| `base_dir` | The base_dir for your app | `code:lib_dir(App)` where `App` is what you define in `application` below | -| `application` | The name of your app | **no default** | -| `dialyzer_warnings` | The active warnings for _diaylzer_ | `[error_handling, race_conditions, unmatched_returns]` | -| `plts` | The list of plt files for _dialyzer_ | `filelib:wildcard("your_app/*.plt")` | -| `elvis_config` | Config file for _elvis_ | `"your_app/elvis.config"` | -| `xref_config` | Config options for _xref_ | `#{dirs => [filename:join(BaseDir, "ebin"), filename:join(BaseDir, "test")], xref_defaults => [{verbose, true}, {recurse, true}, {builtins, true}]}` | -| `xref_checks` | List of checks for _xref_ | `[ undefined_function_calls, locals_not_used, deprecated_function_calls]` | ### `ktn_recipe` diff --git a/src/ktn_code.erl b/src/ktn_code.erl deleted file mode 100644 index 8f504e3..0000000 --- a/src/ktn_code.erl +++ /dev/null @@ -1,753 +0,0 @@ -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% ktn_code: functions useful for dealing with erlang code -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --module(ktn_code). - --export([ - beam_to_string/1, - beam_to_erl/2, - parse_tree/1, - parse_tree/2, - eval/1, - consult/1 - ]). - -%% Getters --export([ - type/1, - attr/2, - node_attr/2, - content/1 - ]). - --type tree_node_type() :: - function | clause | match | tuple - | atom | integer | float | string | char - | binary | binary_element | var - | call | remote - | 'case' | case_expr | case_clauses - | 'fun' | named_fun - | 'query' - | 'try' | try_catch | try_case | try_after - | 'if' | 'catch' - | 'receive' | receive_after | receive_case - | nil | cons - | map | map_field_assoc | map_field_exact - | lc | lc_expr | generate - | bc | bc_expr | b_generate - | op - | record | record_field | record_index - | block - %% Attributes - | module - | type | callback - | export | export_type - | remote_type | type | ann_type | paren_type - | any. - --type tree_node() :: - #{type => tree_node_type(), - attrs => map(), - node_attrs => map(), - content => [tree_node()]}. - --exported_type(tree_node/1). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Exported API -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -%% @doc If the beam was not compiled with debug_info -%% the code generated by this function will look really ugly -%% @end --spec beam_to_string(beam_lib:beam()) -> - {ok, string()} | {error, beam_lib, term()}. -beam_to_string(BeamPath) -> - case beam_lib:chunks(BeamPath, [abstract_code]) of - {ok, {_, [{abstract_code, {raw_abstract_v1, Forms}}]}} -> - Src = erl_prettypr:format(erl_syntax:form_list(tl(Forms))), - {ok, Src}; - Error -> - Error - end. - -%% @doc If the beam was not compiled with debug_info -%% the code generated by this function will look really ugly -%% @end --spec beam_to_erl(beam_lib:beam(), string()) -> ok. -beam_to_erl(BeamPath, ErlPath) -> - case beam_to_string(BeamPath) of - {ok, Src} -> - {ok, Fd} = file:open(ErlPath, [write]), - io:fwrite(Fd, "~s~n", [Src]), - file:close(Fd); - Error -> - Error - end. - -%% @equiv parse_tree([], Source). --spec parse_tree(string()) -> tree_node(). -parse_tree(Source) -> - parse_tree([], Source). - -%% @doc Parses code in a string or binary format and returns the parse tree. --spec parse_tree([string()], string()) -> tree_node(). -parse_tree(IncludeDirs, Source) -> - SourceStr = to_str(Source), - ScanOpts = [text, return_comments], - {ok, Tokens, _} = erl_scan:string(SourceStr, {1, 1}, ScanOpts), - Options = [{include, IncludeDirs}], - {ok, NewTokens} = aleppo:process_tokens(Tokens, Options), - - IsComment = fun - ({comment, _, _}) -> true; - (_) -> false - end, - - {Comments, CodeTokens} = lists:partition(IsComment, NewTokens), - Forms = ktn_lists:split_when(fun is_dot/1, CodeTokens), - ParsedForms = lists:map(fun erl_parse:parse_form/1, Forms), - Children = [to_map(Parsed) || {ok, Parsed} <- ParsedForms], - - #{type => root, - attrs => #{tokens => lists:map(fun token_to_map/1, Tokens)}, - content => to_map(Comments) ++ Children}. - -token_to_map({Type, Attrs}) -> - #{type => Type, - attrs => #{text => get_text(Attrs), - location => get_location(Attrs)}}; -token_to_map({Type, Attrs, Value}) -> - Map = token_to_map({Type, Attrs}), - Map#{value => Value}. - -%% @doc Evaluates the erlang expression in the string provided. --spec eval(string() | binary()) -> term(). -eval(Source) -> - eval(Source, []). - --spec eval(string() | binary(), orddict:orddict()) -> term(). -eval(Source, Bindings) -> - SourceStr = to_str(Source), - {ok, Tokens, _} = erl_scan:string(SourceStr), - {ok, Parsed} = erl_parse:parse_exprs(Tokens), - {value, Result, _} = erl_eval:exprs(Parsed, Bindings), - Result. - -%% @doc Like file:consult/1 but for strings and binaries. --spec consult(string() | binary()) -> [term()]. -consult(Source) -> - SourceStr = to_str(Source), - {ok, Tokens, _} = erl_scan:string(SourceStr), - Forms = ktn_lists:split_when(fun is_dot/1, Tokens), - ParseFun = fun (Form) -> - {ok, Expr} = erl_parse:parse_exprs(Form), - Expr - end, - Parsed = lists:map(ParseFun, Forms), - ExprsFun = fun(P) -> - {value, Value, _} = erl_eval:exprs(P, []), - Value - end, - lists:map(ExprsFun, Parsed). - -%% Getters - --spec type(tree_node()) -> atom(). -type(#{type := Type}) -> - Type; -type(undefined) -> - undefined. - --spec attr(term(), tree_node()) -> term() | undefined. -attr(Key, #{attrs := Attrs}) -> - case maps:is_key(Key, Attrs) of - true -> maps:get(Key, Attrs); - false -> undefined - end; -attr(_Key, Node) when is_map(Node) -> - undefined; -attr(_Key, undefined) -> - undefined. - --spec node_attr(term(), tree_node()) -> term() | undefined. -node_attr(Key, #{node_attrs := Attrs}) -> - case maps:is_key(Key, Attrs) of - true -> maps:get(Key, Attrs); - false -> undefined - end; -node_attr(_Key, Node) when is_map(Node) -> - undefined; -node_attr(_Key, undefined) -> - undefined. - --spec content(tree_node()) -> [tree_node()]. -content(#{content := Content}) -> - Content; -content(_Node) -> - []. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% Internal -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - --spec to_str(binary() | list() | atom()) -> string(). -to_str(Arg) when is_binary(Arg) -> - unicode:characters_to_list(Arg); -to_str(Arg) when is_atom(Arg) -> - atom_to_list(Arg); -to_str(Arg) when is_integer(Arg) -> - integer_to_list(Arg); -to_str(Arg) when is_list(Arg) -> - Arg. - --spec is_dot(tuple()) -> boolean(). -is_dot({dot, _}) -> true; -is_dot(_) -> false. - -%% @private -get_location(Attrs) when is_integer(Attrs) -> - Line = Attrs, - {Line, 1}; -get_location(Attrs) when is_list(Attrs) -> - Line = proplists:get_value(line, Attrs), - Column = proplists:get_value(column, Attrs), - case {Line, Column} of - {undefined, undefined} -> - proplists:get_value(location, Attrs, {-1, -1}); - _ -> - {Line, Column} - end; -get_location(Location = {_Line, _Column}) -> - Location; -get_location(_Attrs) -> - {-1, -1}. - -%% @private -get_text(Attrs) when is_integer(Attrs) -> - undefined; -get_text(Attrs) when is_list(Attrs) -> - proplists:get_value(text, Attrs, ""); -get_text(_Attrs) -> - "". - -%% @doc Converts a parse tree form the abstract format to a map based repr. -%% TODO: Attributes are not being handled correctly. --spec to_map(term()) -> tree_node() | [tree_node()]. -to_map(ListParsed) when is_list(ListParsed) -> - lists:map(fun to_map/1, ListParsed); - -to_map({function, Attrs, Name, Arity, Clauses}) -> - #{type => function, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name, - arity => Arity}, - content => to_map(Clauses)}; -to_map({function, Name, Arity}) -> - #{type => function, - attrs => #{name => Name, - arity => Arity}}; -to_map({function, Module, Name, Arity}) -> - #{type => function, - attrs => #{module => Module, - name => Name, - arity => Arity}}; - -to_map({clause, Attrs, Patterns, Guards, Body}) -> - #{type => clause, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{pattern => to_map(Patterns), - guards => to_map(Guards)}, - content => to_map(Body)}; - -to_map({match, Attrs, Left, Right}) -> - #{type => match, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map([Left, Right])}; - -to_map({tuple, Attrs, Elements}) -> - #{type => tuple, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Elements)}; - -%% Literals - -to_map({Type, Attrs, Value}) when - Type == atom; - Type == integer; - Type == float; - Type == string; - Type == char -> - #{type => Type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - value => Value}}; - -to_map({bin, Attrs, Elements}) -> - #{type => binary, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Elements)}; - -to_map({bin_element, Attrs, Value, Size, TSL}) -> - #{type => binary_element, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - type_spec_list => TSL}, - node_attrs => #{value => to_map(Value), - size => case Size of - default -> #{type => default}; - _ -> to_map(Size) - end }}; - -%% Variables - -to_map({var, Attrs, Name}) -> - #{type => var, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}}; - -%% Function call - -to_map({call, Attrs, Function, Arguments}) -> - #{type => call, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{function => to_map(Function)}, - content => to_map(Arguments)}; - -to_map({remote, Attrs, Module, Function}) -> - #{type => remote, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{module => to_map(Module), - function => to_map(Function)}}; - -%% case - -to_map({'case', Attrs, Expr, Clauses}) -> - CaseExpr = to_map({case_expr, Attrs, Expr}), - CaseClauses = to_map({case_clauses, Attrs, Clauses}), - #{type => 'case', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{expression => to_map(Expr)}, - content => [CaseExpr, CaseClauses]}; -to_map({case_expr, Attrs, Expr}) -> - #{type => case_expr, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [to_map(Expr)]}; -to_map({case_clauses, Attrs, Clauses}) -> - #{type => case_clauses, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Clauses)}; - -%% fun - -to_map({'fun', Attrs, {function, Name, Arity}}) -> - #{type => 'fun', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name, - arity => Arity}}; - -to_map({'fun', Attrs, {function, Module, Name, Arity}}) -> - #{type => 'fun', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - module => Module, - name => Name, - arity => Arity}}; - -to_map({'fun', Attrs, {clauses, Clauses}}) -> - #{type => 'fun', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Clauses)}; - -to_map({named_fun, Attrs, Name, Clauses}) -> - #{type => named_fun, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - content => to_map(Clauses)}; - -%% query - deprecated, implemented for completion. - -to_map({'query', Attrs, ListCompr}) -> - #{type => 'query', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(ListCompr)}; - -%% try..catch..after - -to_map({'try', Attrs, Body, [], CatchClauses, AfterBody}) -> - TryBody = to_map(Body), - TryCatch = to_map({try_catch, Attrs, CatchClauses}), - TryAfter = to_map({try_after, Attrs, AfterBody}), - - #{type => 'try', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{catch_clauses => to_map(CatchClauses), - after_body => to_map(AfterBody)}, - content => TryBody ++ [TryCatch, TryAfter]}; - -%% try..of..catch..after - -to_map({'try', Attrs, Expr, CaseClauses, CatchClauses, AfterBody}) -> - TryCase = to_map({try_case, Attrs, Expr, CaseClauses}), - TryCatch = to_map({try_catch, Attrs, CatchClauses}), - TryAfter = to_map({try_after, Attrs, AfterBody}), - - #{type => 'try', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [TryCase, TryCatch, TryAfter]}; - -to_map({try_case, Attrs, Expr, Clauses}) -> - #{type => try_case, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{expression => to_map(Expr)}, - content => to_map(Clauses)}; - -to_map({try_catch, Attrs, Clauses}) -> - #{type => try_catch, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Clauses)}; - -to_map({try_after, Attrs, AfterBody}) -> - #{type => try_after, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(AfterBody)}; - -%% if - -to_map({'if', Attrs, IfClauses}) -> - #{type => 'if', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(IfClauses)}; - -%% catch - -to_map({'catch', Attrs, Expr}) -> - #{type => 'catch', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [to_map(Expr)]}; - -%% receive - -to_map({'receive', Attrs, Clauses}) -> - RecClauses = to_map({receive_case, Attrs, Clauses}), - #{type => 'receive', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [RecClauses]}; - -to_map({'receive', Attrs, Clauses, AfterExpr, AfterBody}) -> - RecClauses = to_map({receive_case, Attrs, Clauses}), - RecAfter = to_map({receive_after, Attrs, AfterExpr, AfterBody}), - #{type => 'receive', - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [RecClauses, RecAfter]}; - -to_map({receive_case, Attrs, Clauses}) -> - #{type => receive_case, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Clauses)}; - -to_map({receive_after, Attrs, Expr, Body}) -> - #{type => receive_after, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{expression => to_map(Expr)}, - content => to_map(Body)}; - -%% List - -to_map({nil, Attrs}) -> - #{type => nil, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}}; - -to_map({cons, Attrs, Head, Tail}) -> - #{type => cons, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [to_map(Head), to_map(Tail)]}; - -%% Map - -to_map({map, Attrs, Pairs}) -> - #{type => map, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Pairs)}; -to_map({map, Attrs, Var, Pairs}) -> - #{type => map, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{var => to_map(Var)}, - content => to_map(Pairs)}; - -to_map({Type, Attrs, Key, Value}) when - map_field_exact == Type; - map_field_assoc == Type -> - #{type => Type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{key => to_map(Key), - value => to_map(Value)}}; - -%% List Comprehension - -to_map({lc, Attrs, Expr, GeneratorsFilters}) -> - LcExpr = to_map({lc_expr, Attrs, Expr}), - LcGenerators = to_map(GeneratorsFilters), - #{type => lc, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [LcExpr | LcGenerators]}; - -to_map({generate, Attrs, Pattern, Expr}) -> - #{type => generate, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{pattern => to_map(Pattern), - expression => to_map(Expr)}}; -to_map({lc_expr, Attrs, Expr}) -> - #{type => lc_expr, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [to_map(Expr)]}; - -%% Binary Comprehension - -to_map({bc, Attrs, Expr, GeneratorsFilters}) -> - BcExpr = to_map({bc_expr, Attrs, Expr}), - BcGenerators = to_map(GeneratorsFilters), - #{type => bc, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [BcExpr | BcGenerators]}; -to_map({b_generate, Attrs, Pattern, Expr}) -> - #{type => b_generate, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{pattern => to_map(Pattern), - expression => to_map(Expr)}}; -to_map({bc_expr, Attrs, Expr}) -> - #{type => bc_expr, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => [to_map(Expr)]}; - -%% Operation - -to_map({op, Attrs, Operation, Left, Right}) -> - #{type => op, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - operation => Operation}, - content => to_map([Left, Right])}; - -to_map({op, Attrs, Operation, Single}) -> - #{type => op, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - operation => Operation}, - content => to_map([Single])}; - -%% Record - -to_map({record, Attrs, Name, Fields}) -> - #{type => record, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - content => to_map(Fields)}; -to_map({record, Attrs, Var, Name, Fields}) -> - #{type => record, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - node_attrs => #{variable => to_map(Var)}, - content => to_map(Fields)}; - -to_map({record_index, Attrs, Name, Field}) -> - #{type => record_index, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - content => [to_map(Field)]}; - -to_map({record_field, Attrs, Name}) -> - #{type => record_field, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{name => to_map(Name)}}; -to_map({record_field, Attrs, Name, Default}) -> - #{type => record_field, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{default => to_map(Default), - name => to_map(Name)}}; -to_map({record_field, Attrs, Var, Name, Field}) -> - #{type => record_field, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - node_attrs => #{variable => to_map(Var)}, - content => [to_map(Field)]}; - -%% Block - -to_map({block, Attrs, Body}) -> - #{type => block, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - content => to_map(Body)}; - -%% Record Attribute - -to_map({attribute, Attrs, record, {Name, Fields}}) -> - #{type => record_attr, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - content => to_map(Fields)}; -to_map({typed_record_field, Field, Type}) -> - FieldMap = to_map(Field), - #{type => typed_record_field, - attrs => #{location => attr(location, FieldMap), - text => attr(text, FieldMap), - field => FieldMap}, - node_attrs => #{type => to_map(Type)}}; - -%% Type - -to_map({type, Attrs, 'fun', Types}) -> - #{type => type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => 'fun'}, - content => to_map(Types)}; -to_map({type, Attrs, constraint, [Sub, SubType]}) -> - #{type => type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => constraint, - subtype => Sub}, - content => to_map(SubType)}; -to_map({type, Attrs, bounded_fun, [FunType, Defs]}) -> - #{type => type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => bounded_fun}, - node_attrs => #{'fun' => to_map(FunType)}, - content => to_map(Defs)}; -to_map({type, Attrs, Name, any}) -> - to_map({type, Attrs, Name, [any]}); -to_map({type, Attrs, any}) -> - #{type => type, - attrs => #{location => get_location(Attrs), - text => "...", - name => '...'}}; -to_map({type, Attrs, Name, Types}) -> - #{type => type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - content => to_map(Types)}; -to_map({user_type, Attrs, Name, Types}) -> %% any() - #{type => user_type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - content => to_map(Types)}; - -to_map({type, Attrs, map_field_assoc, Name, Type}) -> - {Location, Text} = - case Attrs of - Line when is_integer(Attrs) -> - {{Line, Line}, undefined}; - Attrs -> - {get_location(Attrs), - get_text(Attrs)} - end, - #{type => type_map_field, - attrs => #{location => Location, - text => Text}, - node_attrs => #{key => to_map(Name), - type => to_map(Type)}}; -to_map({remote_type, Attrs, [Module, Function, Args]}) -> - #{type => remote_type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{module => to_map(Module), - function => to_map(Function), - args => to_map(Args)}}; -to_map({ann_type, Attrs, [Var, Type]}) -> - #{type => record_field, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{var => to_map(Var), - type => to_map(Type)}}; -to_map({paren_type, Attrs, [Type]}) -> - #{type => record_field, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}, - node_attrs => #{type => to_map(Type)}}; -to_map(any) -> %% any() - #{type => any}; - -%% Other Attributes - -to_map({attribute, Attrs, type, {Name, Type, Args}}) -> - #{type => type_attr, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name}, - node_attrs => #{args => to_map(Args), - type => to_map(Type)}}; -to_map({attribute, Attrs, spec, {{Name, Arity}, Types}}) -> - #{type => spec, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - name => Name, - arity => Arity}, - node_attrs => #{types => to_map(Types)}}; -to_map({attribute, Attrs, Type, Value}) -> - #{type => Type, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs), - value => Value}}; - -%% Comments - -to_map({comment, Attrs, _Text}) -> - #{type => comment, - attrs => #{location => get_location(Attrs), - text => get_text(Attrs)}}; - -%% Unhandled forms - -to_map(Parsed) when is_tuple(Parsed) -> - throw({unhandled_abstract_form, Parsed}); -to_map(Parsed) -> - throw({unexpected_abstract_form, Parsed}). diff --git a/src/ktn_meta_SUITE.erl b/src/ktn_meta_SUITE.erl deleted file mode 100644 index 9cbaf2a..0000000 --- a/src/ktn_meta_SUITE.erl +++ /dev/null @@ -1,127 +0,0 @@ -%%% @doc Meta Testing SUITE -%%% Use with mixer or by yourself. Just include a call to each of its functions -%%% in your common test suites. -%%% Make sure to add an application property to your common test configuration. --module(ktn_meta_SUITE). --author('elbrujohalcon@inaka.net'). - --export([all/0]). --export([xref/1, dialyzer/1, elvis/1]). - --type config() :: [{atom(), term()}]. - --spec all() -> [dialyzer | elvis | xref]. -all() -> [dialyzer, elvis, xref]. - -%% @doc xref's your code using xref_runner. -%% Available Options: -%% - xref_config: Configuration for xref_runner -%% - xref_checks: List of xref checks to perform --spec xref(config()) -> {comment, []}. -xref(Config) -> - BaseDir = base_dir(Config), - XrefConfig = - case test_server:lookup_config(xref_config, Config) of - undefined -> - #{ dirs => [ filename:join(BaseDir, "ebin") - , filename:join(BaseDir, "test") - ] - , xref_defaults => [ {verbose, true} - , {recurse, true} - , {builtins, true} - ] - }; - XC -> XC - end, - Checks = - case test_server:lookup_config(xref_checks, Config) of - undefined -> - [ undefined_function_calls - , locals_not_used - , deprecated_function_calls - ]; - Cs -> Cs - end, - - ct:comment("There are no Warnings"), - [] = - [ Warning - || Check <- Checks, Warning <- xref_runner:check(Check, XrefConfig)], - - {comment, ""}. - -%% @doc dialyzes your code. -%% By default it uses all the plts in the project root folder. -%% You can change that by providing a 'plts' parameter in Config. -%% You can also change the warnings using the 'dialyzer_warnings' parameter --spec dialyzer(config()) -> {comment, []}. -dialyzer(Config) -> - BaseDir = base_dir(Config), - Plts = plts(Config), - Dirs = [ filename:join(BaseDir, "ebin") - , filename:join(BaseDir, "test") - ], - Warnings = - case test_server:lookup_config(dialyzer_warnings, Config) of - undefined -> [error_handling, race_conditions, unmatched_returns]; - Ws -> Ws - end, - - ct:comment("Dialyzer must emit no warnings"), - Opts = - [ {analysis_type, succ_typings} - , {plts, Plts} - , {files_rec, Dirs} - , {check_plt, true} - , {warnings, Warnings} - , {get_warnings, true} - ], - [] = [dialyzer:format_warning(W, basename) || W <- dialyzer:run(Opts)], - {comment, ""}. - -%% @doc Checks your code with elvis -%% Available Options: -%% - elvis_config: Location of elvis.config --spec elvis(config()) -> {comment, []}. -elvis(Config) -> - ElvisConfig = - case test_server:lookup_config(elvis_config, Config) of - undefined -> - ConfigFile = filename:join(base_dir(Config), "elvis.config"), - [ fix_dirs(Group, Config) - || Group <- elvis_config:load_file(ConfigFile)]; - ConfigFile -> elvis_config:load_file(ConfigFile) - end, - - ct:comment("Elvis rocks!"), - ok = elvis_core:rock(ElvisConfig), - - {comment, ""}. - -base_dir(Config) -> - case test_server:lookup_config(base_dir, Config) of - undefined -> - case test_server:lookup_config(application, Config) of - undefined -> - ct:fail("Missing base_dir and application in Config: ~p", [Config]); - App -> code:lib_dir(App) - end; - BaseDir -> BaseDir - end. - -plts(Config) -> - case test_server:lookup_config(plts, Config) of - undefined -> - Wildcard = filename:join(base_dir(Config), "*.plt"), - case filelib:wildcard(Wildcard) of - [] -> - ct:fail("No plts at ~s - you need to at least have one", [Wildcard]); - Plts -> Plts - end; - Plts -> Plts - end. - -fix_dirs(#{dirs := Dirs} = Group, Config) -> - NewDirs = - [filename:join(base_dir(Config), Dir) || Dir <- Dirs], - Group#{dirs := NewDirs}. diff --git a/test/cover.spec b/test/cover.spec index 8354f09..767df06 100644 --- a/test/cover.spec +++ b/test/cover.spec @@ -2,14 +2,12 @@ { incl_mods, [ktn_binary, - ktn_code, ktn_date, ktn_debug, ktn_fsm, ktn_json, ktn_lists, ktn_maps, - ktn_meta_SUITE, ktn_numbers, ktn_os, ktn_random, diff --git a/test/ktn_code_SUITE.erl b/test/ktn_code_SUITE.erl deleted file mode 100644 index ff959ad..0000000 --- a/test/ktn_code_SUITE.erl +++ /dev/null @@ -1,75 +0,0 @@ --module(ktn_code_SUITE). - --export([ - all/0, - init_per_suite/1, - end_per_suite/1 - ]). - --export([ - consult/1, - beam_to_string/1, - parse_tree/1 - ]). - --define(EXCLUDED_FUNS, - [ - module_info, - all, - test, - init_per_suite, - end_per_suite - ]). - --type config() :: [{atom(), term()}]. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Common test -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - --spec all() -> [atom()]. -all() -> - Exports = ?MODULE:module_info(exports), - [F || {F, _} <- Exports, not lists:member(F, ?EXCLUDED_FUNS)]. - --spec init_per_suite(config()) -> config(). -init_per_suite(Config) -> - Config. - --spec end_per_suite(config()) -> config(). -end_per_suite(Config) -> - Config. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Test Cases -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - --spec consult(config()) -> ok. -consult(_Config) -> - [{a}, {b}] = ktn_code:consult("{a}. {b}."), - [] = ktn_code:consult(""), - [{a}, {b}, {c, d, e}] = ktn_code:consult("{a}. {b}. {c, d, e}."), - [{a}, {b}, {c, d, e}] = ktn_code:consult("{a}.\r\n{b}.\r\n{c, d, e}."), - - [{'.'}] = ktn_code:consult("{'.'}.\n"), - [{<<"ble.bla">>}, {"github.com"}] = - ktn_code:consult("{<<\"ble.bla\">>}.\n{\"github.com\"}.\r\n"), - ok. - --spec beam_to_string(config()) -> ok. -beam_to_string(_Config) -> - {error, beam_lib, _} = ktn_code:beam_to_string(bla), - {ok, _} = ktn_code:beam_to_string("../../ebin/ktn_code.beam"), - ok. - -parse_tree(_Config) -> - ModuleNode = #{type => module, - attrs => #{location => {1, 2}, - text => "module", - value => x}}, - - #{type := root, - content := _} = ktn_code:parse_tree("-module(x)."), - - #{type := root, - content := [ModuleNode]} = ktn_code:parse_tree("-module(x)."). diff --git a/test/ktn_meta_suite_SUITE.erl b/test/ktn_meta_suite_SUITE.erl deleted file mode 100644 index 8bdfeca..0000000 --- a/test/ktn_meta_suite_SUITE.erl +++ /dev/null @@ -1,22 +0,0 @@ --module(ktn_meta_suite_SUITE). - --include_lib("mixer/include/mixer.hrl"). --mixin([{ ktn_meta_SUITE - , [ all/0 - , xref/1 - , dialyzer/1 - , elvis/1 - ] - }]). - --export([init_per_suite/1]). - --type config() :: [{atom(), term()}]. - --spec init_per_suite(config()) -> config(). -init_per_suite(Config) -> - [ {base_dir, "../.."} - , {elvis_config, "../../test/elvis.config"} - , {plts, ["../../.katana.plt"]} - | Config - ]. diff --git a/test/ktn_xref_SUITE.erl b/test/ktn_xref_SUITE.erl deleted file mode 100644 index 4351aa1..0000000 --- a/test/ktn_xref_SUITE.erl +++ /dev/null @@ -1,21 +0,0 @@ --module(ktn_xref_SUITE). --author('elbrujohalcon@inaka.net'). - --ignore_xref([all/0]). --ignore_xref([xref/1]). - --export([all/0]). --export([xref/1]). - --spec all() -> [xref]. -all() -> [xref]. - --spec xref(any()) -> {comment, []}. -xref(_Config) -> - Dirs = [filename:absname("../../ebin")], %%, filename:absname("../../test")], - [] = xref_runner:check(undefined_function_calls, #{dirs => Dirs}), - [] = xref_runner:check(undefined_functions, #{dirs => Dirs}), - [] = xref_runner:check(locals_not_used, #{dirs => Dirs}), - [] = xref_runner:check(deprecated_function_calls, #{dirs => Dirs}), - [] = xref_runner:check(deprecated_functions, #{dirs => Dirs}), - {comment, ""}.