From 461efe3072d512a4ebde99a8c533f9fb11e5aa5a Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Date: Thu, 19 Jun 2025 19:56:01 -0300 Subject: [PATCH 1/4] Remove excoveralls and use built-in coverage --- .github/workflows/elixir.yml | 2 +- README.md | 2 +- mix.exs | 9 +-------- mix.lock | 10 ---------- 4 files changed, 3 insertions(+), 20 deletions(-) diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index a5488d9..b4a26eb 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -20,4 +20,4 @@ jobs: - name: Install Dependencies run: mix deps.get - name: Run Tests - run: mix test + run: mix test --cover diff --git a/README.md b/README.md index b5924a0..d87f9c3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ExFIX [![Build Status](https://github.com/santif/ex_fix/actions/workflows/elixir.yml/badge.svg?branch=master)](https://github.com/santif/ex_fix/actions/workflows/elixir.yml) -[![Coverage Status](https://coveralls.io/repos/github/santif/ex_fix/badge.svg?branch=master)](https://coveralls.io/github/santif/ex_fix?branch=master) +[![Coverage Status](https://github.com/santif/ex_fix/actions/workflows/elixir.yml/badge.svg?branch=master)](https://github.com/santif/ex_fix/actions/workflows/elixir.yml) [![Tokei](https://tokei.rs/b1/github/santif/ex_fix?category=code)](https://tokei.rs/b1/github/santif/ex_fix?category=code) [![Hex.pm Version](http://img.shields.io/hexpm/v/ex_fix.svg?style=flat)](https://hex.pm/packages/ex_fix) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) diff --git a/mix.exs b/mix.exs index e071ba8..0231e12 100644 --- a/mix.exs +++ b/mix.exs @@ -10,13 +10,7 @@ defmodule ExFix.Mixfile do start_permanent: Mix.env() == :prod, description: description(), package: package(), - test_coverage: [tool: ExCoveralls], - preferred_cli_env: [ - coveralls: :test, - "coveralls.detail": :test, - "coveralls.post": :test, - "coveralls.html": :test - ], + test_coverage: [summary: [threshold: 0]], deps: deps() ] end @@ -44,7 +38,6 @@ defmodule ExFix.Mixfile do [ {:benchfella, "~> 0.3.5", only: :dev}, {:ex_doc, "~> 0.30", only: :dev, runtime: false}, - {:excoveralls, "~> 0.18", only: :test}, {:dialyxir, "~> 1.3", only: [:dev], runtime: false}, {:credo, "~> 1.7", only: [:dev, :test], runtime: false} ] diff --git a/mix.lock b/mix.lock index 89dfc64..a379873 100644 --- a/mix.lock +++ b/mix.lock @@ -1,25 +1,15 @@ %{ "benchfella": {:hex, :benchfella, "0.3.5", "b2122c234117b3f91ed7b43b6e915e19e1ab216971154acd0a80ce0e9b8c05f5", [:mix], [], "hexpm", "23f27cbc482cbac03fc8926441eb60a5e111759c17642bac005c3225f5eb809d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"}, "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [: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", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "dialyxir": {:hex, :dialyxir, "1.4.5", "ca1571ac18e0f88d4ab245f0b60fa31ff1b12cbae2b11bd25d207f865e8ae78a", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b0fb08bb8107c750db5c0b324fa2df5ceaa0f9307690ee3c1f6ba5b9eb5d35c3"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"}, "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, - "excoveralls": {:hex, :excoveralls, "0.18.5", "e229d0a65982613332ec30f07940038fe451a2e5b29bce2a5022165f0c9b157e", [:mix], [{:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "523fe8a15603f86d64852aab2abe8ddbd78e68579c8525ae765facc5eae01562"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "hackney": {:hex, :hackney, "1.24.1", "f5205a125bba6ed4587f9db3cc7c729d11316fa8f215d3e57ed1c067a9703fa9", [:rebar3], [{:certifi, "~> 2.15.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.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.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", "f4a7392a0b53d8bbc3eb855bdcc919cd677358e65b2afd3840b5b3690c4c8a39"}, - "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.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [: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", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, - "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, - "tzdata": {:hex, :tzdata, "1.1.3", "b1cef7bb6de1de90d4ddc25d33892b32830f907e7fc2fccd1e7e22778ab7dfbc", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d4ca85575a064d29d4e94253ee95912edfb165938743dbf002acdf0dcecb0c28"}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"}, } From a242e015e619a7ddffdde9f43de511c642eee022 Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Date: Thu, 19 Jun 2025 21:13:03 -0300 Subject: [PATCH 2/4] Add additional tests (#13) * Add additional API and registry tests * Improve test coverage --- test/default_session_registry_test.exs | 42 ++++++++++++++++++ test/ex_fix_api_test.exs | 57 ++++++++++++++++++++++++ test/in_message_test.exs | 7 ++- test/parser_test.exs | 17 +++++++ test/session_sup_test.exs | 14 ++++++ test/session_worker_failure_test.exs | 36 +++++++++++++++ test/session_worker_test.exs | 61 ++++++++++++++++++++++++++ 7 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 test/default_session_registry_test.exs create mode 100644 test/ex_fix_api_test.exs create mode 100644 test/session_sup_test.exs create mode 100644 test/session_worker_failure_test.exs diff --git a/test/default_session_registry_test.exs b/test/default_session_registry_test.exs new file mode 100644 index 0000000..ead516c --- /dev/null +++ b/test/default_session_registry_test.exs @@ -0,0 +1,42 @@ +defmodule ExFix.DefaultSessionRegistryTest do + use ExUnit.Case + + alias ExFix.DefaultSessionRegistry + + test "session_on_init replies according to status" do + table = :ex_fix_registry + :ets.delete_all_objects(table) + + assert {:error, :notfound} = DefaultSessionRegistry.session_on_init("s1") + + DefaultSessionRegistry.session_update_status("s2", :connecting) + assert :ok = DefaultSessionRegistry.session_on_init("s2") + + DefaultSessionRegistry.session_update_status("s3", :connected) + assert :wait_to_reconnect = DefaultSessionRegistry.session_on_init("s3") + + DefaultSessionRegistry.session_update_status("s4", :disconnecting) + assert {:error, :disconnected} = DefaultSessionRegistry.session_on_init("s4") + end + + test "handles process DOWN messages" do + table = :ex_fix_registry + :ets.delete_all_objects(table) + + DefaultSessionRegistry.session_update_status("dn1", :connecting) + :ok = DefaultSessionRegistry.session_on_init("dn1") + state = :sys.get_state(DefaultSessionRegistry) + ref1 = Enum.find_value(state.monitor_map, fn {ref, name} -> if name == "dn1", do: ref end) + send(DefaultSessionRegistry, {:DOWN, ref1, :process, self(), :normal}) + Process.sleep(10) + assert DefaultSessionRegistry.get_session_status("dn1") == :disconnected + + DefaultSessionRegistry.session_update_status("dn2", :connecting) + :ok = DefaultSessionRegistry.session_on_init("dn2") + state = :sys.get_state(DefaultSessionRegistry) + ref2 = Enum.find_value(state.monitor_map, fn {ref, name} -> if name == "dn2", do: ref end) + send(DefaultSessionRegistry, {:DOWN, ref2, :process, self(), :shutdown}) + Process.sleep(10) + assert DefaultSessionRegistry.get_session_status("dn2") == :reconnecting + end +end diff --git a/test/ex_fix_api_test.exs b/test/ex_fix_api_test.exs new file mode 100644 index 0000000..4e44d70 --- /dev/null +++ b/test/ex_fix_api_test.exs @@ -0,0 +1,57 @@ +defmodule ExFix.ApiTest do + use ExUnit.Case + + alias ExFix.SessionConfig + alias ExFix.TestHelper.FixEmptySessionHandler + + defmodule CaptureRegistry do + @behaviour ExFix.SessionRegistry + + def get_session_status(_), do: :disconnected + + def start_session(name, %SessionConfig{} = config) do + send(self(), {:start_session, name, config}) + :ok + end + + def stop_session(name) do + send(self(), {:stop_session, name}) + :ok + end + + def session_on_init(_), do: :ok + def session_update_status(_, _), do: :ok + end + + test "start_session_initiator builds config and delegates" do + ExFix.start_session_initiator( + "test_sess", + "S", + "T", + FixEmptySessionHandler, + session_registry: CaptureRegistry, + hostname: "myhost", + port: 12_345, + log_incoming_msg: false + ) + + assert_receive {:start_session, "test_sess", config} + assert %SessionConfig{name: "test_sess", sender_comp_id: "S", target_comp_id: "T"} = config + assert config.hostname == "myhost" + assert config.port == 12_345 + assert config.log_incoming_msg == false + + ExFix.stop_session("test_sess", CaptureRegistry) + assert_receive {:stop_session, "test_sess"} + end + + test "start_session_initiator uses defaults" do + ExFix.start_session_initiator("defaults", "S", "T", FixEmptySessionHandler, session_registry: CaptureRegistry) + + assert_receive {:start_session, "defaults", config} + assert config.hostname == "localhost" + assert config.port == 9876 + assert config.log_incoming_msg == true + assert config.transport_mod == :gen_tcp + end +end diff --git a/test/in_message_test.exs b/test/in_message_test.exs index 8dc1713..e0c8d0c 100644 --- a/test/in_message_test.exs +++ b/test/in_message_test.exs @@ -18,11 +18,14 @@ defmodule ExFix.InMessageTest do %{bin_msg: bin_msg} end - test "InMessage tests", %{bin_msg: bin_msg} do + test "get_field returns value", %{bin_msg: bin_msg} do fix_msg = Parser.parse1(bin_msg, Dictionary, 12_345) assert InMessage.get_field(fix_msg, "49") == "MARKET" assert InMessage.get_field(fix_msg, "52") == "20161007-16:28:50.802" + end - # assert InMessage.get_field(fix_msg, "SendingTime", TestDict) == Calendar.DateTime.Parse.rfc3339_utc + test "get_field returns nil for missing field", %{bin_msg: bin_msg} do + fix_msg = Parser.parse1(bin_msg, Dictionary, 12_345) + assert InMessage.get_field(fix_msg, "9999") == nil end end diff --git a/test/parser_test.exs b/test/parser_test.exs index 10052ad..2e41c00 100644 --- a/test/parser_test.exs +++ b/test/parser_test.exs @@ -124,6 +124,23 @@ defmodule ExFix.ParserTest do assert fix_msg.original_fix_msg == data end + test "Parse message with invalid begin string" do + data = msg("8=FIX4.2|9=12|10=000|") + fix_msg = Parser.parse1(data, Dictionary, 1) + assert fix_msg.valid == false + assert fix_msg.error_reason == :begin_string_error + assert fix_msg.original_fix_msg == data + end + + test "Parse message with unexpected seqnum" do + now = DateTime.from_naive!(~N[2017-07-17 17:50:56], "Etc/UTC") + data = build_message("0", 10, "SENDER", "TARGET", now) + fix_msg = Parser.parse1(data, Dictionary, 5) + assert fix_msg.valid == false + assert fix_msg.error_reason == :unexpected_seqnum + assert fix_msg.seqnum == 10 + end + test "Parse message - stage 1 - subject with 2 fields" do data = msg( diff --git a/test/session_sup_test.exs b/test/session_sup_test.exs new file mode 100644 index 0000000..55cf373 --- /dev/null +++ b/test/session_sup_test.exs @@ -0,0 +1,14 @@ +defmodule ExFix.SessionSupTest do + use ExUnit.Case + + test "start_link when already started" do + assert {:error, {:already_started, _}} = ExFix.SessionSup.start_link([]) + end + + test "dynamic supervisor can start child" do + {:ok, pid} = DynamicSupervisor.start_child(ExFix.SessionSup, {Task, fn -> :timer.sleep(10) end}) + assert is_pid(pid) + ref = Process.monitor(pid) + assert_receive {:DOWN, ^ref, :process, ^pid, :normal}, 50 + end +end diff --git a/test/session_worker_failure_test.exs b/test/session_worker_failure_test.exs new file mode 100644 index 0000000..60c3bce --- /dev/null +++ b/test/session_worker_failure_test.exs @@ -0,0 +1,36 @@ +defmodule ExFix.SessionWorkerFailureTest do + use ExUnit.Case + + alias ExFix.SessionConfig + alias ExFix.TestHelper.FixEmptySessionHandler + + defmodule FailTransport do + def connect(_host, _port, _opts), do: {:error, :econnrefused} + def send(_conn, _data), do: :ok + def close(_conn), do: :ok + end + + alias ExFix.TestHelper.TestSessionRegistry + + test "worker reports reconnecting on connection failure" do + Process.flag(:trap_exit, true) + {:ok, _} = TestSessionRegistry.start_link() + config = %SessionConfig{ + name: "fail1", + mode: :initiator, + sender_comp_id: "S", + target_comp_id: "T", + session_handler: FixEmptySessionHandler, + transport_mod: FailTransport, + transport_options: [], + log_incoming_msg: false, + log_outgoing_msg: false, + reconnect_interval: 0 + } + + {:ok, pid} = ExFix.SessionWorker.start_link(config, TestSessionRegistry) + ref = Process.monitor(pid) + assert_receive {:DOWN, ^ref, :process, ^pid, :econnrefused} + assert TestSessionRegistry.get_session_status("fail1") == :reconnecting + end +end diff --git a/test/session_worker_test.exs b/test/session_worker_test.exs index 0f463b3..d15ccd2 100644 --- a/test/session_worker_test.exs +++ b/test/session_worker_test.exs @@ -145,4 +145,65 @@ defmodule ExFix.SessionWorkerTest do SessionWorker.stop("sessiontest1") assert TestSessionRegistry.get_session_status("sessiontest1") == :disconnected end + + test "higher seqnum triggers resend request" do + + cfg = %SessionConfig{ + name: "resend_sess", + mode: :initiator, + sender_comp_id: "SENDER", + target_comp_id: "TARGET", + session_handler: FixEmptySessionHandler, + dictionary: DefaultDictionary, + transport_mod: TestTransport, + transport_options: [test_pid: self()] + } + + {:ok, _pid} = SessionWorker.start_link(cfg, TestSessionRegistry) + assert_receive {:data, _logon_msg} + + now = DateTime.utc_now() + logon = %MessageToSend{seqnum: 1, sender: "TARGET", orig_sending_time: now, target: "SENDER", msg_type: "A", body: []} + TestTransport.receive_data("resend_sess", Serializer.serialize(logon, now)) + Process.sleep(20) + + msg = %MessageToSend{seqnum: 12, sender: "TARGET", orig_sending_time: now, target: "SENDER", msg_type: "8", body: []} + TestTransport.receive_data("resend_sess", Serializer.serialize(msg, now)) + + assert_receive {:data, resend} + parsed = Parser.parse(resend, DefaultDictionary, 1) + assert parsed.msg_type == "2" + SessionWorker.stop("resend_sess") + end + + test "invalid SenderCompID triggers logout" do + cfg = %SessionConfig{ + name: "badcomp", + mode: :initiator, + sender_comp_id: "SENDER", + target_comp_id: "TARGET", + session_handler: FixEmptySessionHandler, + dictionary: DefaultDictionary, + transport_mod: TestTransport, + transport_options: [test_pid: self()] + } + + {:ok, pid} = SessionWorker.start_link(cfg, TestSessionRegistry) + ref = Process.monitor(pid) + assert_receive {:data, _} + + now = DateTime.utc_now() + logon = %MessageToSend{seqnum: 1, sender: "TARGET", orig_sending_time: now, target: "SENDER", msg_type: "A", body: []} + TestTransport.receive_data("badcomp", Serializer.serialize(logon, now)) + Process.sleep(20) + + msg = %MessageToSend{seqnum: 2, sender: "OTHER", orig_sending_time: now, target: "SENDER", msg_type: "8", body: []} + TestTransport.receive_data("badcomp", Serializer.serialize(msg, now)) + + assert_receive {:data, reject} + assert_receive {:data, logout} + assert Parser.parse(reject, DefaultDictionary, 1).msg_type == "3" + assert Parser.parse(logout, DefaultDictionary, 1).msg_type == "5" + assert_receive {:DOWN, ^ref, :process, ^pid, :normal}, 1000 + end end From 29598b1d1659cfbce3a7aaf7668ea31b39eade94 Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Date: Thu, 19 Jun 2025 21:13:19 -0300 Subject: [PATCH 3/4] Show coverage percentage badge (#14) * Add coverage badge generation * Ignore generated coverage badge --- .github/workflows/elixir.yml | 10 ++++++++-- .gitignore | 1 + README.md | 2 +- scripts/generate_coverage_badge.sh | 12 ++++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100755 scripts/generate_coverage_badge.sh diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index b4a26eb..bc1fe68 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -17,7 +17,13 @@ jobs: otp-version: '25' install-hex: true install-rebar: true + - name: Install anybadge + run: pip install anybadge - name: Install Dependencies run: mix deps.get - - name: Run Tests - run: mix test --cover + - name: Run Tests and generate coverage badge + run: ./scripts/generate_coverage_badge.sh + - uses: actions/upload-artifact@v4 + with: + name: coverage + path: coverage.svg diff --git a/.gitignore b/.gitignore index e1b4852..b7625e5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ erl_crash.dump /bench/snapshots/ /bench/graphs/ +/coverage.svg diff --git a/README.md b/README.md index d87f9c3..8248027 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ExFIX [![Build Status](https://github.com/santif/ex_fix/actions/workflows/elixir.yml/badge.svg?branch=master)](https://github.com/santif/ex_fix/actions/workflows/elixir.yml) -[![Coverage Status](https://github.com/santif/ex_fix/actions/workflows/elixir.yml/badge.svg?branch=master)](https://github.com/santif/ex_fix/actions/workflows/elixir.yml) +[![Coverage](./coverage.svg)](https://github.com/santif/ex_fix/actions/workflows/elixir.yml) [![Tokei](https://tokei.rs/b1/github/santif/ex_fix?category=code)](https://tokei.rs/b1/github/santif/ex_fix?category=code) [![Hex.pm Version](http://img.shields.io/hexpm/v/ex_fix.svg?style=flat)](https://hex.pm/packages/ex_fix) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) diff --git a/scripts/generate_coverage_badge.sh b/scripts/generate_coverage_badge.sh new file mode 100755 index 0000000..364146b --- /dev/null +++ b/scripts/generate_coverage_badge.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +mix test --cover | tee coverage_output.log +coverage=$(grep -E "[0-9]+\.[0-9]+% \| Total" coverage_output.log | awk '{print $1}' | tr -d '%') +coverage=${coverage:-0} + +anybadge --value="${coverage}" --label=coverage \ + --file=coverage.svg \ + 0=red 80=yellow 90=green >/dev/null + +echo "Generated coverage.svg with ${coverage}% coverage" From 8d0a67d91f2d567bb4df53073fcc778b723cc64f Mon Sep 17 00:00:00 2001 From: Santiago Fernandez Date: Thu, 19 Jun 2025 21:15:50 -0300 Subject: [PATCH 4/4] Bump version to 0.2.9 --- README.md | 2 +- mix.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8248027..57e485f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Add `ex_fix` to your list of dependencies in `mix.exs`: ```elixir def deps do - [{:ex_fix, "~> 0.2.8"}] + [{:ex_fix, "~> 0.2.9"}] end ``` diff --git a/mix.exs b/mix.exs index 0231e12..e25603d 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule ExFix.Mixfile do def project do [ app: :ex_fix, - version: "0.2.8", + version: "0.2.9", elixir: "~> 1.18", build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod,