From f9559309930d90d8403c573ac2aab6216e1acf94 Mon Sep 17 00:00:00 2001 From: jaeyson Date: Fri, 12 Apr 2024 17:38:59 +0800 Subject: [PATCH] Move benchmark as a mix task --- .github/dependabot.yml | 14 +++ .github/workflows/ci.yml | 30 +++-- .github/workflows/hex.yml | 28 +++++ .gitignore | 3 + .iex.exs | 2 +- CHANGELOG.md | 8 +- README.md | 26 ++-- coveralls.json | 13 +- lib/freecodecamp_elixir/algo_projects.ex | 6 - lib/freecodecamp_elixir/basic_algo.ex | 4 +- .../benchmarks/basic_algo.ex | 95 ++++++++------ .../benchmarks/intermediate_algo.ex | 43 ++++--- lib/mix/tasks/benchmark.ex | 119 ++++++++++++++++++ lib/mix/tasks/precommit.ex | 14 ++- lib/mix/tasks/selective_test.ex | 18 ++- mix.exs | 8 +- mix.lock | 28 ++--- 17 files changed, 333 insertions(+), 126 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/hex.yml delete mode 100644 lib/freecodecamp_elixir/algo_projects.ex rename benchmarks/basic_algo.exs => lib/freecodecamp_elixir/benchmarks/basic_algo.ex (80%) rename benchmarks/intermediate_algo.exs => lib/freecodecamp_elixir/benchmarks/intermediate_algo.ex (73%) create mode 100644 lib/mix/tasks/benchmark.ex diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8125f4b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "mix" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + labels: + - "hex" + - "dependencies" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b89ffad..254de24 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,16 +18,20 @@ jobs: strategy: matrix: include: - # - elixir: 1.11.x - # otp: 22 - # - elixir: 1.11.x - # otp: 23 - # - elixir: 1.12.x - # otp: 23 - # - elixir: 1.13.x - # otp: 24 + - elixir: 1.11.x + otp: 22 + - elixir: 1.11.x + otp: 23 + - elixir: 1.12.x + otp: 23 + - elixir: 1.13.x + otp: 24 - elixir: 1.14.x otp: 25 + - elixir: 1.15.x + otp: 26 + - elixir: 1.16.x + otp: 26 warnings_as_errors: true static_analysis: true env: @@ -38,7 +42,7 @@ jobs: if: "!contains(github.event.head_commit.message, '[skip ci]')" steps: - name: Checkout repo - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup Erlang/OTP & Elixir uses: erlef/setup-beam@v1 @@ -47,7 +51,7 @@ jobs: elixir-version: ${{ matrix.elixir }} - name: Cache artifacts - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: | deps @@ -73,9 +77,3 @@ jobs: run: mix coveralls.github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Publish to Hex - if: "contains(github.event.head_commit.message, '[publish to hex]')" - run: | - mix hex.config api_key ${{ secrets.HEX_API_KEY }} - mix hex.publish --yes diff --git a/.github/workflows/hex.yml b/.github/workflows/hex.yml new file mode 100644 index 0000000..9049e3e --- /dev/null +++ b/.github/workflows/hex.yml @@ -0,0 +1,28 @@ +name: CI + +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + check_commit_message: + name: skips CI and prints cli message, without fail build badge + runs-on: ubuntu-latest + if: "contains(github.event.head_commit.message, '[skip ci]')" + steps: + - run: echo "no need to build, based from commit message" + + publish_to_hex: + name: "Publish to Hex" + runs-on: ubuntu-latest + + # doesn't contain "ci skip" in commit message + if: "!contains(github.event.head_commit.message, '[skip ci]')" + steps: + run: | + mix hex.config api_key ${{ secrets.HEX_API_KEY }} + mix hex.publish --yes diff --git a/.gitignore b/.gitignore index c570a3b..2b35ba7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ sample-*.tar # Temporary files, for example, from tests. /tmp/ +# Benchmarks from benchee +/benchmarks/ + .elixir_ls .DS_Store diff --git a/.iex.exs b/.iex.exs index abbc273..3bcce40 100644 --- a/.iex.exs +++ b/.iex.exs @@ -1,5 +1,5 @@ -alias FreecodecampElixir.AlgoProjects alias FreecodecampElixir.BasicAlgo alias FreecodecampElixir.IntermediateAlgo +alias FreecodecampElixir.Benchmarks require Logger diff --git a/CHANGELOG.md b/CHANGELOG.md index e52dc07..dd12fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.0 (2024.12.04) + +- Benchmarks as mix task +- Bump dependencies' versions +- Tidy docs + ## 0.1.0 (2022.09.04) -* Initial release +- Initial release diff --git a/README.md b/README.md index ec4f581..dbcdb8a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Solving exercises from Freecodecamp.org using Elixir programming language. Includes benchmarks and tests for every functions. [//]: # "Badges" + [![Last Commit][commit-badge]](https://github.com/jaeyson/freecodecamp_elixir/commit/main) [![Commit activity][pulse-badge]](https://github.com/jaeyson/freecodecamp_elixir/pulse) [![Dependabot][dependabot-badge]](https://github.com/jaeyson/freecodecamp_elixir/pulls/app%2Fdependabot) @@ -112,7 +113,6 @@ mix selective_test basic_algo intermediate_algo # basic_algo = Basic Algorithm Scripting # intermediate_algo = Intermediate Algorithm Scripting -# algo_projects = Algorithm Projects ``` ## Generate `HTML` Docs @@ -124,22 +124,22 @@ mix docs ## Benchmarks (using Benchee) -If you want to benchmark a specific function: - ```bash -# NOTE: file path is at -# freecodecamp_elixir/benchmarks/basic_algo.exs +# view benchmark commands +mix help benchmark +``` -# Example: change the function name from -# "mutation" to "repeat_string" -# BasicAlgo.run("mutation", HTML) -# or uncomment lines to use that instead -BasicAlgo.run("repeat_string", HTML) +```bash +# list available functions +mix benchmark --list +``` -# you can use the default formatter (console) -BasicAlgo.run("repeat_string", Console) +```bash +# specific function +mix benchmark mutation ``` ```bash -mix run benchmarks/basic_algo.exs +# benchmark results saved as html in "benchmarks/" directory +mix benchmark mutation --html ``` diff --git a/coveralls.json b/coveralls.json index f6d4e57..ae89bc1 100644 --- a/coveralls.json +++ b/coveralls.json @@ -1,9 +1,6 @@ { - "skip_files": [ - "benchmarks/", - "lib/mix/" - ], - "terminal_options": { - "file_column_width": 60 - } -} \ No newline at end of file + "skip_files": ["lib/freecodecamp_elixir/benchmarks/", "lib/mix/"], + "terminal_options": { + "file_column_width": 60 + } +} diff --git a/lib/freecodecamp_elixir/algo_projects.ex b/lib/freecodecamp_elixir/algo_projects.ex deleted file mode 100644 index 83beba0..0000000 --- a/lib/freecodecamp_elixir/algo_projects.ex +++ /dev/null @@ -1,6 +0,0 @@ -defmodule FreecodecampElixir.AlgoProjects do - @moduledoc """ - Documentation for Freecodecamp ([Algorithm Projects](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/#javascript-algorithms-and-data-structures-projects)). - """ - @moduledoc since: "0.1.0" -end diff --git a/lib/freecodecamp_elixir/basic_algo.ex b/lib/freecodecamp_elixir/basic_algo.ex index d897761..2cab885 100644 --- a/lib/freecodecamp_elixir/basic_algo.ex +++ b/lib/freecodecamp_elixir/basic_algo.ex @@ -284,8 +284,8 @@ defmodule FreecodecampElixir.BasicAlgo do end @spec do_get_index_to_ins(non_neg_integer | nil) :: non_neg_integer - def do_get_index_to_ins(nil), do: 0 - def do_get_index_to_ins(result), do: result + defp do_get_index_to_ins(nil), do: 0 + defp do_get_index_to_ins(result), do: result @doc """ Check if a string (first argument, `string`) ends with the diff --git a/benchmarks/basic_algo.exs b/lib/freecodecamp_elixir/benchmarks/basic_algo.ex similarity index 80% rename from benchmarks/basic_algo.exs rename to lib/freecodecamp_elixir/benchmarks/basic_algo.ex index 9c51a16..dbc15d9 100644 --- a/benchmarks/basic_algo.exs +++ b/lib/freecodecamp_elixir/benchmarks/basic_algo.ex @@ -1,11 +1,26 @@ -defmodule Benchmark.BasicAlgo do +defmodule FreecodecampElixir.Benchmarks.BasicAlgo do + alias Benchee.Formatters.Console + alias Benchee.Formatters.HTML alias FreecodecampElixir.BasicAlgo + @moduledoc false + + # NOTE: We can't use apply/3 here to run a number of + # arbitrary functions in order to minimize the use + # of atoms. So we listed all (and future) functions. + + @spec run(String.t(), String.t()) :: module() + def run(function_name, formatter \\ "--console") do + formatter = + case String.downcase(formatter) do + "--console" -> Console + "--html" -> HTML + end - @spec run(String.t(), module(), any()) :: module() - def run(function_name, formatter, args \\ nil), - do: __MODULE__ |> apply(String.to_atom(function_name), [formatter, args]) + do_run(function_name, formatter) + end # set config for benchmark here + @spec generic_benchee(map(), HTML | Console, (any() -> any())) :: Benchee.Suite.t() defp generic_benchee(map, formatter, before_each_function) do Benchee.run( map, @@ -17,8 +32,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec mutation(module(), any()) :: module() - def mutation(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("mutation", formatter) do generic_benchee( %{ "mutation: Enum.filter" => fn {string1, string2} -> @@ -38,8 +53,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec truncate_string(module(), any()) :: module() - def truncate_string(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("truncate_string", formatter) do generic_benchee( %{ "BasicAlgo.truncate_string" => fn {string, pos} -> @@ -62,8 +77,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec get_index_to_ins(module(), any()) :: module() - def get_index_to_ins(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("get_index_to_ins", formatter) do generic_benchee( %{ "get_index_to_ins: List comprehension" => fn {list_int, value} -> @@ -83,8 +98,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec factorialize(module(), any()) :: module() - def factorialize(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("factorialize", formatter) do generic_benchee( %{ "factorialize: Enum.filter and tco function" => &BasicAlgo.factorialize/1, @@ -95,8 +110,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec reverse_string(module(), any()) :: module() - def reverse_string(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("reverse_string", formatter) do generic_benchee( %{ "reverse_string: Pattern matching" => &BasicAlgo.reverse_string/1, @@ -107,8 +122,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec reverse_string(module(), any()) :: module() - def find_longest_word_length(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("find_longest_word_length", formatter) do generic_benchee( %{ "find_longest_word_length: Enum.max_by" => &BasicAlgo.find_longest_word_length/1, @@ -119,8 +134,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec largest_of_four(module(), any()) :: module() - def largest_of_four(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("largest_of_four", formatter) do list_int = -10_000..10_000 |> StreamData.integer() @@ -137,8 +152,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec confirm_ending(module(), any()) :: module() - def confirm_ending(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("confirm_ending", formatter) do generic_benchee( %{ "confirm_ending: String.ends_with?" => fn {str_one, str_two} -> @@ -158,8 +173,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec find_element(module(), any()) :: module() - def find_element(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("find_element", formatter) do generic_benchee( %{ "find_element: Enum.find" => fn list -> @@ -177,8 +192,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec title_case(module(), any()) :: module() - def title_case(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("title_case", formatter) do generic_benchee( %{ "title_case: BasicAlgo.title_case" => fn string -> @@ -193,8 +208,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec franken_splice(module(), any()) :: module() - def franken_splice(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("franken_splice", formatter) do generic_benchee( %{ "franken_splice: BasicAlgo.franken_splice" => fn {list_a, list_b, int} -> @@ -218,8 +233,8 @@ defmodule Benchmark.BasicAlgo do ) end - @spec chunk_array_in_groups(module(), any()) :: module() - def chunk_array_in_groups(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("chunk_array_in_groups", formatter) do generic_benchee( %{ "chunk_array_in_groups: BasicAlgo.chunk_array_in_groups" => fn {list, size} -> @@ -374,32 +389,32 @@ defmodule Benchmark.BasicAlgo do end @spec title_case_gen(String.t()) :: String.t() - def title_case_gen(string) do + defp title_case_gen(string) do ~w(#{string}) |> Enum.map_join(" ", &(String.downcase(&1) |> String.capitalize())) end @spec franken_splice_gen_v1(Enumerable.t(), Enumerable.t(), integer) :: Enumerable.t() - def franken_splice_gen_v1([], [], _el), do: [] + defp franken_splice_gen_v1([], [], _el), do: [] - def franken_splice_gen_v1(list_one, list_two, el) do + defp franken_splice_gen_v1(list_one, list_two, el) do List.insert_at(list_two, el, list_one) |> List.flatten() end @spec franken_splice_gen_v2(Enumerable.t(), Enumerable.t(), integer) :: Enumerable.t() - def franken_splice_gen_v2(list_one, list_two, -1) do + defp franken_splice_gen_v2(list_one, list_two, -1) do {head, tails} = Enum.split(list_two, length(list_two)) [head | [list_one | [tails]]] |> :lists.flatten() end - def franken_splice_gen_v2(list_one, list_two, el) when el < -1 do + defp franken_splice_gen_v2(list_one, list_two, el) when el < -1 do {head, tails} = Enum.split(list_two, el + 1) do_franken_splice(list_one, head, tails) |> :lists.flatten() end - def franken_splice_gen_v2(list_one, list_two, el) do + defp franken_splice_gen_v2(list_one, list_two, el) do {head, tails} = Enum.split(list_two, el) do_franken_splice(list_one, head, tails) @@ -411,19 +426,19 @@ defmodule Benchmark.BasicAlgo do end @spec chunk_array_in_groups_gen(Enumerable.t(), integer) :: list(Enumerable.t()) - def chunk_array_in_groups_gen([], _size), do: [] - def chunk_array_in_groups_gen(list, size) when size < 1, do: list + defp chunk_array_in_groups_gen([], _size), do: [] + defp chunk_array_in_groups_gen(list, size) when size < 1, do: list - def chunk_array_in_groups_gen(list, size) do + defp chunk_array_in_groups_gen(list, size) do Enum.chunk_every(list, size) end end -alias Benchmark.BasicAlgo -alias Benchee.Formatters.{HTML, Console} +# alias Benchmark.BasicAlgo +# alias Benchee.Formatters.{HTML, Console} # BasicAlgo.run("mutation", HTML) -BasicAlgo.run("chunk_array_in_groups", Console) +# BasicAlgo.run("chunk_array_in_groups", Console) # Available functions (uncomment above): # - mutation diff --git a/benchmarks/intermediate_algo.exs b/lib/freecodecamp_elixir/benchmarks/intermediate_algo.ex similarity index 73% rename from benchmarks/intermediate_algo.exs rename to lib/freecodecamp_elixir/benchmarks/intermediate_algo.ex index d4af275..07711e2 100644 --- a/benchmarks/intermediate_algo.exs +++ b/lib/freecodecamp_elixir/benchmarks/intermediate_algo.ex @@ -1,14 +1,27 @@ -defmodule Benchmark.IntermediateAlgo do +defmodule FreecodecampElixir.Benchmarks.IntermediateAlgo do + alias Benchee.Formatters.Console + alias Benchee.Formatters.HTML alias FreecodecampElixir.IntermediateAlgo + @moduledoc false - @spec run(String.t(), module(), any()) :: module() - def run(function_name, formatter, args \\ nil), - do: __MODULE__ |> apply(String.to_atom(function_name), [formatter, args]) + # NOTE: We can't use apply/3 here to run a number of + # arbitrary functions in order to minimize the use + # of atoms. So we listed all (and future) functions. + + @spec run(String.t(), String.t()) :: module() + def run(function_name, formatter \\ "--console") do + case formatter do + "--console" -> Console + "--html" -> HTML + end + + do_run(function_name, formatter) + end # set config for benchmark here - defp generic_benchee(jobs, formatter, before_each_function) do + defp generic_benchee(map, formatter, before_each_function) do Benchee.run( - jobs, + map, before_each: before_each_function, time: 5, memory_time: 2, @@ -17,8 +30,8 @@ defmodule Benchmark.IntermediateAlgo do ) end - @spec sum_all(module(), any()) :: module() - def sum_all(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("sum_all", formatter) do generic_benchee( %{ "IntermediateAlgo.sum_all" => fn {int1, int2} -> @@ -44,8 +57,8 @@ defmodule Benchmark.IntermediateAlgo do ) end - @spec diff_list(module(), any()) :: module() - def diff_list(formatter, _args) do + @spec do_run(String.t(), module()) :: module() + defp do_run("diff_list", formatter) do generic_benchee( %{ "IntermediateAlgo.diff_list" => fn {list1, list2} -> @@ -96,9 +109,9 @@ defmodule Benchmark.IntermediateAlgo do end @spec sum_all_gen_v3(list(integer)) :: integer - def sum_all_gen_v3([0, 0]), do: 0 + defp sum_all_gen_v3([0, 0]), do: 0 - def sum_all_gen_v3([num_one, num_two] = _list) do + defp sum_all_gen_v3([num_one, num_two] = _list) do for(x <- num_one..num_two, do: x) |> do_sum_all_v3() end @@ -119,11 +132,11 @@ defmodule Benchmark.IntermediateAlgo do end end -alias Benchmark.IntermediateAlgo -alias Benchee.Formatters.{HTML, Console} +# alias Benchmark.IntermediateAlgo +# alias Benchee.Formatters.{HTML, Console} # IntermediateAlgo.run("mutation", HTML) -IntermediateAlgo.run("diff_list", Console) +# IntermediateAlgo.run("diff_list", Console) # Available functions (uncomment above): # - diff_list diff --git a/lib/mix/tasks/benchmark.ex b/lib/mix/tasks/benchmark.ex new file mode 100644 index 0000000..c9a3fe4 --- /dev/null +++ b/lib/mix/tasks/benchmark.ex @@ -0,0 +1,119 @@ +defmodule Mix.Tasks.Benchmark do + @moduledoc """ + ## Usage + + `mix benchmark [module, ...] [options] ` + + Running e.g. `mix selective_test basic_algo` will + only include tests from `test/basic_algo_test.exs`. + Note: these command is not to be ran inside `iex`, + rather run this in your OS shell. + + To view this via terminal: + ```bash + mix help benchmark + ``` + + ## Options + + - `--list`: Lists available functions + - `--console`: Outputs benchmark result in terminal (Default). + - `--html`: Outputs benchmark as html doc + + ## Example + + ```bash + # list available functions + mix benchmark --list + ``` + + ```bash + # specific function + mix benchmark mutation + ``` + + ```bash + # benchmark specific function, export to html + mix benchmark mutation --html + ``` + + """ + @moduledoc since: "0.2.0" + @shortdoc "Benchmarks a specific function" + + use Mix.Task + + @formatter ~w(--html --console) + @benchmarks "Benchmarks" + + @impl Mix.Task + @spec run([String.t()]) :: IO.puts() + def run(["--list"]) do + functions = list_available_functions() |> Map.values() |> List.flatten() + available_functions = Enum.map_join(functions, "\n", &("- " <> to_string(&1))) + specific_function = Enum.random(functions) + + """ + Here's the list of available modules: + #{available_functions} + + To run specific module: + $ mix benchmark #{specific_function} + + To run and export result to html: + $ mix benchmark #{specific_function} --html + """ + |> IO.puts() + end + + @spec run(list(String.t())) :: IO.puts() | Benchee.Suite.t() + def run(args) do + all_fns = list_available_functions() |> Map.values() |> List.flatten() + + case Enum.all?(args, &(&1 in all_fns or &1 in @formatter)) do + true -> + formatter = Enum.find(args, &(&1 in @formatter)) || "console" + function = Enum.filter(args, &(&1 in all_fns)) |> hd() + mod = find_module(function) + + mod.run(function, formatter) + + false -> + IO.puts(~s(Error: invalid arguments. See "mix help benchmark" for instructions)) + end + end + + @spec find_module(String.t()) :: module() + defp find_module(function) do + {mod, _functions} = + list_available_functions() + |> Enum.find(fn {_mod, functions} -> function in functions end) + + mod + end + + @spec list_available_functions :: %{module() => list(String.t())} + defp list_available_functions do + {:ok, modules} = :application.get_key(:freecodecamp_elixir, :modules) + + modules + |> Enum.filter(fn module -> + case Module.split(module) do + [_app_name, _namespace] -> true + _ -> false + end + end) + |> Enum.group_by( + fn module -> + [app_name, base] = module |> Module.split() + app_name |> Module.concat(@benchmarks) |> Module.concat(base) + end, + fn module -> + module.__info__(:functions) |> Keyword.keys() |> Enum.map(&to_string/1) + end + ) + |> Enum.reduce(%{}, fn {module, functions}, acc -> + Map.put(acc, module, List.flatten(functions)) + end) + end +end diff --git a/lib/mix/tasks/precommit.ex b/lib/mix/tasks/precommit.ex index 96df0e0..0174c30 100644 --- a/lib/mix/tasks/precommit.ex +++ b/lib/mix/tasks/precommit.ex @@ -1,6 +1,8 @@ defmodule Mix.Tasks.Precommit do @moduledoc """ - Usage: `mix precommit [--force]` + ## Usage + + `mix precommit [--force]` Generates `pre-commit` git hook. Visit [this link](https://git-scm.com/book/en/Customizing-Git-Git-Hooks) to know more about git hooks. Add option `--force` to @@ -8,10 +10,18 @@ defmodule Mix.Tasks.Precommit do command is not to be ran inside `iex`, rather run this in your OS shell. - Example: + To view this via terminal: + ```bash + mix help precommit + ``` + + ## Example ```bash mix precommit + + # force + mix precommit --force ``` """ diff --git a/lib/mix/tasks/selective_test.ex b/lib/mix/tasks/selective_test.ex index b3e5c52..669804e 100644 --- a/lib/mix/tasks/selective_test.ex +++ b/lib/mix/tasks/selective_test.ex @@ -1,18 +1,28 @@ defmodule Mix.Tasks.SelectiveTest do @moduledoc """ - Usage: `mix selective_test [module, ...]` + ## Usage + + `mix selective_test [module, ...]` Running e.g. `mix selective_test basic_algo` will only include tests from `test/basic_algo_test.exs`. - Another way to call it: - `mix test --exclude intermediate_algo basic_algo ...`. Note: these command is not to be ran inside `iex`, rather run this in your OS shell. - Example: + To view this via terminal: + ```bash + mix help selective_test + ``` + ## Example + + Another way to call it: ```bash + # specific test mix selective_test basic_algo + + # two or more + mix test --exclude intermediate_algo basic_algo ``` """ diff --git a/mix.exs b/mix.exs index 53939dd..2ce810e 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule FreecodecampElixir.MixProject do use Mix.Project - @version "0.1.0" + @version "0.2.0" @elixir_version "~> 1.9" @source_url "https://github.com/jaeyson/freecodecamp_elixir" @coverage_url "https://coveralls.io/github/jaeyson/freecodecamp_elixir" @@ -42,11 +42,11 @@ defmodule FreecodecampElixir.MixProject do [ # {:dep_from_hexpm, "~> 0.3.0"}, # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} - {:stream_data, "~> 0.5.0", only: [:dev, :test]}, + {:stream_data, "~> 0.6", only: [:dev, :test]}, {:benchee, "~> 1.0", only: [:dev], runtime: false}, {:benchee_html, "~> 1.0", only: [:dev], runtime: false}, - {:ex_doc, "~> 0.28.2", only: [:dev, :test], runtime: false}, - {:credo, "~> 1.6.4", only: [:dev, :test], runtime: false}, + {:ex_doc, "~> 0.32", only: :dev, runtime: false}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:excoveralls, "~> 0.10", only: :test} ] end diff --git a/mix.lock b/mix.lock index fad4189..5c8bfda 100644 --- a/mix.lock +++ b/mix.lock @@ -1,27 +1,27 @@ %{ - "benchee": {:hex, :benchee, "1.1.0", "f3a43817209a92a1fade36ef36b86e1052627fd8934a8b937ac9ab3a76c43062", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}], "hexpm", "7da57d545003165a012b587077f6ba90b89210fd88074ce3c60ce239eb5e6d93"}, - "benchee_html": {:hex, :benchee_html, "1.0.0", "5b4d24effebd060f466fb460ec06576e7b34a00fc26b234fe4f12c4f05c95947", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:benchee_json, "~> 1.0", [hex: :benchee_json, repo: "hexpm", optional: false]}], "hexpm", "5280af9aac432ff5ca4216d03e8a93f32209510e925b60e7f27c33796f69e699"}, + "benchee": {:hex, :benchee, "1.3.0", "f64e3b64ad3563fa9838146ddefb2d2f94cf5b473bdfd63f5ca4d0657bf96694", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "34f4294068c11b2bd2ebf2c59aac9c7da26ffa0068afdf3419f1b176e16c5f81"}, + "benchee_html": {:hex, :benchee_html, "1.0.1", "1e247c0886c3fdb0d3f4b184b653a8d6fb96e4ad0d0389267fe4f36968772e24", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:benchee_json, "~> 1.0", [hex: :benchee_json, repo: "hexpm", optional: false]}], "hexpm", "b00a181af7152431901e08f3fc9f7197ed43ff50421a8347b0c80bf45d5b3fef"}, "benchee_json": {:hex, :benchee_json, "1.0.0", "cc661f4454d5995c08fe10dd1f2f72f229c8f0fb1c96f6b327a8c8fc96a91fe5", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "da05d813f9123505f870344d68fb7c86a4f0f9074df7d7b7e2bb011a63ec231c"}, - "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, - "credo": {:hex, :credo, "1.6.6", "f51f8d45db1af3b2e2f7bee3e6d3c871737bda4a91bff00c5eec276517d1a19c", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "625520ce0984ee0f9f1f198165cd46fa73c1e59a17ebc520038b8fce056a5bdc"}, + "credo": {:hex, :credo, "1.7.5", "643213503b1c766ec0496d828c90c424471ea54da77c8a168c725686377b9545", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f799e9b5cd1891577d8c773d245668aa74a2fcd15eb277f51a0131690ebfb3fd"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"}, - "ex_doc": {:hex, :ex_doc, "0.28.5", "3e52a6d2130ce74d096859e477b97080c156d0926701c13870a4e1f752363279", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d2c4b07133113e9aa3e9ba27efb9088ba900e9e51caa383919676afdf09ab181"}, - "excoveralls": {:hex, :excoveralls, "0.14.6", "610e921e25b180a8538229ef547957f7e04bd3d3e9a55c7c5b7d24354abbba70", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "0eceddaa9785cfcefbf3cd37812705f9d8ad34a758e513bb975b081dce4eb11e"}, - "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "ex_doc": {:hex, :ex_doc, "0.32.0", "896afb57b1e00030f6ec8b2e19d3ca99a197afb23858d49d94aea673dc222f12", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.1", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "ed2c3e42c558f49bda3ff37e05713432006e1719a6c4a3320c7e4735787374e7"}, + "excoveralls": {:hex, :excoveralls, "0.18.1", "a6f547570c6b24ec13f122a5634833a063aec49218f6fff27de9df693a15588c", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d65f79db146bb20399f23046015974de0079668b9abb2f5aac074d078da60b8d"}, + "file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, - "stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"}, + "stream_data": {:hex, :stream_data, "0.6.0", "e87a9a79d7ec23d10ff83eb025141ef4915eeb09d4491f79e52f2562b73e5f47", [:mix], [], "hexpm", "b92b5031b650ca480ced047578f1d57ea6dd563f5b57464ad274718c9c29501c"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, }