-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 9ed100d
Showing
17 changed files
with
532 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Used by "mix format" | ||
[ | ||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
name: build | ||
|
||
on: | ||
push: | ||
branches: | ||
- "**" | ||
|
||
pull_request: | ||
branches: | ||
- master | ||
types: [opened, synchronize, closed] | ||
|
||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
tests: | ||
runs-on: ubuntu-20.04 | ||
name: Tests on ${{matrix.environment.elixir-version}} | ||
strategy: | ||
matrix: | ||
environment: | ||
- elixir-version: 1.15.7 | ||
otp-version: 26.1.2 | ||
- elixir-version: 1.14 | ||
otp-version: 24.3.4 | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Set up Elixir | ||
uses: erlef/setup-beam@988e02bfe678367a02564f65ca2e37726dc0268f | ||
with: ${{matrix.environment}} | ||
- name: Restore dependencies cache | ||
uses: actions/cache@v3 | ||
with: | ||
path: deps/ | ||
key: deps-${{ runner.os }}-${{ matrix.environment.otp-version }}-${{ matrix.environment.elixir-version }}-${{ hashFiles('**/mix.lock') }} | ||
- name: Restore build cache | ||
uses: actions/cache@v3 | ||
with: | ||
path: _build/test/ | ||
key: build-${{ runner.os }}-${{ matrix.environment.otp-version }}-${{ matrix.environment.elixir-version }}-${{ hashFiles('**/mix.lock') }} | ||
- name: Install dependencies | ||
run: | | ||
mix local.rebar --force | ||
mix local.hex --force | ||
mix deps.get | ||
mix compile | ||
mix format --check-formatted | ||
- name: Run tests | ||
run: mix test | ||
env: | ||
MIX_ENV: test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# The directory Mix will write compiled artifacts to. | ||
/_build/ | ||
|
||
# If you run "mix test --cover", coverage assets end up here. | ||
/cover/ | ||
|
||
# The directory Mix downloads your dependencies sources to. | ||
/deps/ | ||
|
||
# Where third-party dependencies like ExDoc output generated docs. | ||
/doc/ | ||
|
||
# Ignore .fetch files in case you like to edit your project deps locally. | ||
/.fetch | ||
|
||
# If the VM crashes, it generates a dump, let's ignore it too. | ||
erl_crash.dump | ||
|
||
# Also ignore archive artifacts (built via "mix archive.build"). | ||
*.ez | ||
|
||
# Ignore package tarball (built via "mix hex.build"). | ||
ex_unit_summary-*.tar | ||
|
||
# Temporary files, for example, from tests. | ||
/tmp/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
[ | ||
version: "0.6.4", | ||
# Can also be set/reset with `--autocorrect`/`--no-autocorrect`. | ||
autocorrect: true, | ||
# With "--dry" no changes will be written to the files. | ||
# Can also be set/reset with `--dry`/`--no-dry`. | ||
# If dry is true then verbose is also active. | ||
dry: false, | ||
# Can also be set/reset with `--verbose`/`--no-verbose`. | ||
verbose: false, | ||
# Can be overwritten by calling `mix recode "lib/**/*.ex"`. | ||
inputs: ["{mix,.formatter}.exs", "{apps,config,lib,test}/**/*.{ex,exs}"], | ||
formatter: {Recode.Formatter, []}, | ||
tasks: [ | ||
# Tasks could be added by a tuple of the tasks module name and an options | ||
# keyword list. A task can be deactivated by `active: false`. The execution of | ||
# a deactivated task can be forced by calling `mix recode --task ModuleName`. | ||
{Recode.Task.AliasExpansion, []}, | ||
{Recode.Task.AliasOrder, []}, | ||
{Recode.Task.Dbg, [autocorrect: false]}, | ||
{Recode.Task.EnforceLineLength, [active: false]}, | ||
{Recode.Task.FilterCount, []}, | ||
{Recode.Task.IOInspect, [autocorrect: false]}, | ||
{Recode.Task.Nesting, []}, | ||
{Recode.Task.PipeFunOne, []}, | ||
{Recode.Task.SinglePipe, []}, | ||
{Recode.Task.Specs, [exclude: "test/**/*.{ex,exs}", config: [only: :visible]]}, | ||
{Recode.Task.TagFIXME, [exit_code: 2]}, | ||
{Recode.Task.TagTODO, [exit_code: 4]}, | ||
{Recode.Task.TestFileExt, []}, | ||
{Recode.Task.UnusedVariable, [active: false]} | ||
] | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# ExUnitSummary | ||
|
||
The library was created as an extension for EчUnit and adds test case output, just like Rspec does. | ||
|
||
It's a very convenient life cycle for fixing and adding tests is obtained. | ||
|
||
The developer just needs to select any entry from the results, copy it and paste it again into the console | ||
|
||
See the example below | ||
|
||
<img src="media/usage_sample.gif" alt="drawing" width="960"/> | ||
|
||
|
||
## How to add a library to your project | ||
|
||
```elixir | ||
def deps do | ||
[ | ||
{:ex_unit_summary, "~> 0.1.0", only: [:dev, :test]}} | ||
] | ||
end | ||
``` | ||
|
||
## How to setup | ||
|
||
```elixir | ||
# test_helper.exs | ||
|
||
# Start ExUnitSummary application, with recommended config | ||
ExUnitSummary.start(:normal, %ExUnitSummary.Config{filter_results: :failed, print_delay: 100}) | ||
|
||
# Add ExUnitSummary.Formatter to list of ExUnit's formatters. | ||
ExUnit.configure(formatters: [ExUnit.CLIFormatter, ExUnitSummary.Formatter]) | ||
``` | ||
|
||
# Contribution | ||
|
||
Feel free to make a pull request. All contributions are appreciated! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
defmodule ExUnitSummary do | ||
@moduledoc false | ||
use Application | ||
alias ExUnitSummary.Config | ||
|
||
def start(:normal, %Config{} = config) do | ||
children = [ | ||
{ExUnitSummary.ConfigStorage, config}, | ||
{ExUnitSummary.Formatter, config}, | ||
{ExUnitSummary.Recorder, []} | ||
] | ||
|
||
opts = [strategy: :one_for_one, name: ExUnitSummary.Supervisor] | ||
Supervisor.start_link(children, opts) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
defmodule ExUnitSummary.CaseResult do | ||
@moduledoc """ | ||
Wrapper around ExUnit case result | ||
""" | ||
defstruct [ | ||
:case_type, | ||
:describe_block_name, | ||
:case_name, | ||
:file, | ||
:line, | ||
:module, | ||
:case_result, | ||
:case_time, | ||
:error_info | ||
] | ||
|
||
@type t() :: %{ | ||
case_result: :failed | :success, | ||
file: String.t(), | ||
module: String.t(), | ||
line: non_neg_integer(), | ||
case_type: atom(), | ||
describe_block_name: nil | String.t(), | ||
case_time: non_neg_integer(), | ||
error_info: any() | ||
} | ||
|
||
@spec from_exunit_event({any(), any()}) :: nil | ExUnitSummary.CaseResult.t() | ||
def from_exunit_event({:test_finished, %ExUnit.Test{state: nil} = event}) do | ||
from_exunit_finish_event(event) | ||
end | ||
|
||
def from_exunit_event({:test_finished, %ExUnit.Test{state: {:failed, _info}} = event}) do | ||
from_exunit_finish_event(event) | ||
end | ||
|
||
def from_exunit_event({_another_event, _event}) do | ||
nil | ||
end | ||
|
||
defp from_exunit_finish_event(%ExUnit.Test{} = event) do | ||
%__MODULE__{ | ||
case_type: event.tags.test_type, | ||
case_name: String.replace_leading(to_string(event.name), "test ", ""), | ||
module: event.module, | ||
case_result: case_result(event.state), | ||
error_info: error_info(event.state), | ||
case_time: time_to_ms(event.time), | ||
describe_block_name: event.tags.describe, | ||
line: event.tags.line, | ||
file: event.tags.file | ||
} | ||
end | ||
|
||
defp time_to_ms(time), do: trunc(time / 1000) | ||
defp case_result(nil), do: :success | ||
defp case_result({:failed, _reason}), do: :failed | ||
defp error_info(nil), do: [] | ||
defp error_info({:failed, reason}), do: reason | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
defmodule ExUnitSummary.Config do | ||
@moduledoc """ | ||
Library Configurations | ||
* print_delay is used to delay output, becouse there is race condition with default ExUnit Formatter | ||
* filter_results is used to filter, which results should be printed in console | ||
""" | ||
defstruct print_delay: nil, | ||
filter_results: nil | ||
|
||
@type t() :: %{ | ||
print_delay: nil | non_neg_integer(), | ||
filter_results: :failed | :success | :all | nil | ||
} | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
defmodule ExUnitSummary.ConfigStorage do | ||
@moduledoc """ | ||
Storage for ExUnitSummary Config | ||
(Global State) | ||
""" | ||
use GenServer | ||
|
||
alias ExUnitSummary.Config | ||
|
||
def start_link(%Config{} = config) do | ||
GenServer.start_link(__MODULE__, config, name: __MODULE__) | ||
end | ||
|
||
@impl GenServer | ||
|
||
def init(%Config{} = config) do | ||
{:ok, config} | ||
end | ||
|
||
@spec get_config() :: any() | ||
def get_config() do | ||
GenServer.call(__MODULE__, :get_config) | ||
end | ||
|
||
@impl GenServer | ||
def handle_call(:get_config, _from, %Config{} = config) do | ||
{:reply, config, config} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
defmodule ExUnitSummary.Formatter do | ||
@moduledoc false | ||
use GenServer | ||
|
||
alias ExUnitSummary.CaseResult | ||
alias ExUnitSummary.Printer | ||
alias ExUnitSummary.Recorder | ||
|
||
@spec start_link(any()) :: :ignore | {:error, any()} | {:ok, pid()} | ||
def start_link(_any) do | ||
GenServer.start_link(__MODULE__, nil, name: __MODULE__) | ||
end | ||
|
||
def init(ex_unit_config) do | ||
{:ok, ex_unit_config} | ||
end | ||
|
||
def handle_cast({:suite_finished, _data}, ex_unit_config) do | ||
results = get_list_of_case_results() | ||
write_output(results) | ||
{:noreply, ex_unit_config} | ||
end | ||
|
||
def handle_cast({_event_type, _event_data} = event, ex_unit_config) do | ||
case_result = CaseResult.from_exunit_event(event) | ||
Recorder.write(case_result) | ||
|
||
{:noreply, ex_unit_config} | ||
end | ||
|
||
defp get_list_of_case_results() do | ||
Recorder.get_results() | ||
end | ||
|
||
defp write_output(results) do | ||
Printer.call(results) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
defmodule ExUnitSummary.Printer do | ||
@moduledoc """ | ||
This module prints results to console | ||
""" | ||
alias ExUnitSummary.CaseResult | ||
alias ExUnitSummary.Config | ||
alias ExUnitSummary.ConfigStorage | ||
|
||
@spec call(maybe_improper_list()) :: :ok | ||
def call(results) when is_list(results) do | ||
%Config{print_delay: delay} = config = ConfigStorage.get_config() | ||
|
||
# Delay is used for ensure, that ExUnitSummary print own report after ExUnit.CLIFormatter | ||
if delay, do: :timer.sleep(delay) | ||
|
||
print(config, results) | ||
end | ||
|
||
def print(%Config{filter_results: filter_results} = _config, results) do | ||
failed_list = | ||
results | ||
|> Enum.filter(&filter_function(&1, filter_results)) | ||
|> Enum.sort_by(&{&1.file, &1.line, &1.case_result}) | ||
|> Enum.map(&build_row/1) | ||
|
||
if length(failed_list) > 0 do | ||
result_string = | ||
"ExUnitSummary (#{filter_results || :all}): \n\n" <> Enum.join(failed_list, "\n") | ||
|
||
IO.puts(result_string) | ||
end | ||
|
||
:ok | ||
end | ||
|
||
defp filter_function(%CaseResult{} = _result, filter) when filter in [nil, :all] do | ||
true | ||
end | ||
|
||
defp filter_function(%CaseResult{case_result: result} = _result, filter) | ||
when filter in [:failed, :success] do | ||
result == filter | ||
end | ||
|
||
defp build_row(%CaseResult{case_result: result} = case_result) do | ||
Enum.join([ | ||
if(result == :success, do: IO.ANSI.green(), else: IO.ANSI.red()), | ||
"mix test ", | ||
"#{case_result.file}:#{case_result.line}", | ||
IO.ANSI.blue(), | ||
" # ", | ||
"#{case_result.case_name}", | ||
IO.ANSI.reset() | ||
]) | ||
end | ||
end |
Oops, something went wrong.