Skip to content

Commit

Permalink
Merge pull request #89 from inaka/elbrujohalcon.89.create_ktn_meta_suite
Browse files Browse the repository at this point in the history
Create ktn_meta_SUITE
  • Loading branch information
David Cao committed Nov 12, 2015
2 parents ebbf9b8 + 765173b commit 50b6923
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 20 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PROJECT = katana

DEPS = eper aleppo xref_runner
DEPS = eper aleppo xref_runner elvis
SHELL_DEPS := sync
TEST_DEPS = mixer
LOCAL_DEPS := xmerl tools compiler syntax_tools common_test inets ssl test_server hipe public_key dialyzer wx
Expand All @@ -10,6 +10,7 @@ dep_sync = git https://github.com/inaka/sync.git 0.1.3
dep_aleppo = git https://github.com/inaka/aleppo.git 0.9.2
dep_xref_runner = git https://github.com/inaka/xref_runner.git 0.2.2
dep_mixer = git https://github.com/inaka/mixer.git 0.1.4
dep_elvis = git https://github.com/inaka/elvis.git b69eea4

include erlang.mk

Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ And you can check all of our open-source projects at

* `ktn_date`: functions useful for handling dates and time values.
* `ktn_debug`: functions useful for debugging.
* `ktn_fsm`: a _nice_ wrapper on top of `gen_fsm`
* `ktn_json`: functions useful for processing & creating JSON.
* `ktn_maps`: functions useful for handling maps.
* `ktn_meta_SUITE`: a helper common_test suite to add meta-testing to your projects
* `ktn_numbers`: functions useful for processing numeric values.
* `ktn_recipe`: A tool to structure code that consists of sequential steps in which decisions are made.
* `ktn_rpc`: functions useful for RPC mechanisms.
Expand Down Expand Up @@ -113,6 +115,45 @@ 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, serpents} | 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
, [ all/0
, dialyzer/1
]
}]).
```

#### 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 |
|-----------|-------------|---------|
| `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"` |

### `ktn_recipe`

#### What is a recipe?
Expand Down
1 change: 1 addition & 0 deletions deps/katana
22 changes: 14 additions & 8 deletions elvis.config
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
elvis,
[
{config,
[#{dirs => ["src", "src/*"],
[#{dirs => ["src", "test"],
filter => "*.erl",
rules => [{elvis_style, line_length, #{limit => 80}},
rules => [{elvis_style, line_length, #{limit => 80,
skip_comments => false}},
{elvis_style, no_tabs},
{elvis_style, no_trailing_whitespace},
{elvis_style, macro_names},
{elvis_style, macro_module_names},
{elvis_style, operator_spaces, #{rules => [{right, ","},
Expand All @@ -24,17 +26,21 @@
#{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$",
ignore => []}
},
{
elvis_style,
function_naming_convention,
#{regex => "^([a-z$][a-z0-9]*_?)*$"}
},
{elvis_style, state_record_and_type},
{elvis_style, no_spec_with_records}
{elvis_style, no_spec_with_records},
{elvis_style, dont_repeat_yourself, #{min_complexity => 15}},
{elvis_style, no_debug_call, #{debug_functions => [{ct, pal}]}}
]
},
#{dirs => ["."],
filter => "Makefile",
rules => [{elvis_project, no_deps_master_erlang_mk, #{ignore => [eper, sync]}}]
},
#{dirs => ["."],
filter => "rebar.config",
rules => [{elvis_project, no_deps_master_rebar, #{ignore => []}}]
rules => [{elvis_project, no_deps_master_erlang_mk, #{ignore => []}},
{elvis_project, protocol_for_deps_erlang_mk, #{ignore => []}}]
},
#{dirs => ["."],
filter => "elvis.config",
Expand Down
2 changes: 2 additions & 0 deletions src/ktn_fsm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
%%% looping. This way you don't have to have catch-all clauses there.
-module(ktn_fsm).

-ignore_xref([start/3]).

-export(
[ state/1
, call/2
Expand Down
111 changes: 111 additions & 0 deletions src/ktn_meta_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
%%% @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
-spec xref(config()) -> {comment, []}.
xref(Config) ->
BaseDir = base_dir(Config),
Dirs = [ filename:join(BaseDir, "ebin")
, filename:join(BaseDir, "test")
],

ct:comment("Undefined Function Calls"),
UFCs = xref_runner:check(undefined_function_calls, #{dirs => Dirs}),

ct:comment("Undefined Functions"),
UFs = xref_runner:check(undefined_functions, #{dirs => Dirs}),

ct:comment("Locals not Used"),
LNUs = xref_runner:check(locals_not_used, #{dirs => Dirs}),

ct:comment("Deprecated Function Calls"),
DFCs = xref_runner:check(deprecated_function_calls, #{dirs => Dirs}),

ct:comment("Deprecated Functions"),
DFs = xref_runner:check(deprecated_functions, #{dirs => Dirs}),

ct:comment("There are no Warnings"),
[] = UFCs ++ UFs ++ LNUs ++ DFCs ++ DFs,

{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
-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:rock(ElvisConfig),

{comment, ""}.

base_dir(Config) ->
case test_server:lookup_config(application, Config) of
undefined -> ct:fail("Missing application in Config: ~p", [Config]);
App -> code:lib_dir(App)
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}.
19 changes: 8 additions & 11 deletions src/ktn_recipe_verify.erl
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,7 @@ verify_transitions(
(X, A) ->
[X | A]
end,
case verify_transitions(F, Transitions) of
ok -> {ok, State};
Error -> {error, State#{error => Error}}
end;
verify_transitions(F, Transitions, State);
verify_transitions(
State = #{recipe_type := explicit, transitions := Transitions}
) ->
Expand All @@ -121,15 +118,15 @@ verify_transitions(
(X, A) ->
[X | A]
end,
case verify_transitions(F, Transitions) of
ok -> {ok, State};
Error -> {error, State#{error => Error}}
end.
verify_transitions(F, Transitions, State).

verify_transitions(F, Transitions) ->
verify_transitions(F, Transitions, State) ->
case lists:foldl(F, [], Transitions) of
[] -> ok;
InvalidElements -> {invalid_transition_table_elements, InvalidElements}
[] -> {ok, State};
InvalidElements ->
{ error
, State#{error => {invalid_transition_table_elements, InvalidElements}}
}
end.

-spec verify_transition_exports(state()) -> {ok, state()} | {error, state()}.
Expand Down
53 changes: 53 additions & 0 deletions test/elvis.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[
{
elvis,
[
{config,
[#{dirs => ["../../src", "../../test"],
filter => "*.erl",
rules => [{elvis_style, line_length, #{limit => 80,
skip_comments => false}},
{elvis_style, no_tabs},
{elvis_style, no_trailing_whitespace},
{elvis_style, macro_names},
{elvis_style, macro_module_names},
{elvis_style, operator_spaces, #{rules => [{right, ","},
{right, "++"},
{left, "++"}]}},
{elvis_style, nesting_level, #{level => 3}},
{elvis_style, god_modules, #{limit => 25}},
{elvis_style, no_if_expression},
{elvis_style, invalid_dynamic_call, #{ignore => [ktn_recipe_verify]}},
{elvis_style, used_ignored_variable},
{elvis_style, no_behavior_info},
{
elvis_style,
module_naming_convention,
#{regex => "^([a-z][a-z0-9]*_?)*(_SUITE)?$",
ignore => []}
},
{
elvis_style,
function_naming_convention,
#{regex => "^([a-z$][a-z0-9]*_?)*$"}
},
{elvis_style, state_record_and_type},
{elvis_style, no_spec_with_records},
{elvis_style, dont_repeat_yourself, #{min_complexity => 15}},
{elvis_style, no_debug_call, #{debug_functions => [{ct, pal}]}}
]
},
#{dirs => ["../.."],
filter => "Makefile",
rules => [{elvis_project, no_deps_master_erlang_mk, #{ignore => []}},
{elvis_project, protocol_for_deps_erlang_mk, #{ignore => []}}]
},
#{dirs => ["../.."],
filter => "elvis.config",
rules => [{elvis_project, old_configuration_format}]
}
]
}
]
}
].
1 change: 1 addition & 0 deletions test/katana.coverspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ktn_json,
ktn_lists,
ktn_maps,
ktn_meta_SUITE,
ktn_numbers,
ktn_os,
ktn_random,
Expand Down
22 changes: 22 additions & 0 deletions test/ktn_meta_suite_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-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) ->
[ {application, katana}
, {elvis_config, "../../test/elvis.config"}
, {plts, ["../../.katana.plt"]}
| Config
].
2 changes: 2 additions & 0 deletions test/secure_vault.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

-behaviour(gen_fsm).

-ignore_xref([{ktn_fsm, start, 3}]).

-export([ start/2
, state/1
, contents/1
Expand Down

0 comments on commit 50b6923

Please sign in to comment.