Skip to content

Commit fb7ab76

Browse files
committed
Add FETestHelpers.HeartbeatTracker
This will make tracking heartbeat intervals much easier
1 parent a95fb86 commit fb7ab76

File tree

15 files changed

+339
-6
lines changed

15 files changed

+339
-6
lines changed

.gitlab-ci.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,24 @@ dialyzer:fe_reporting:
121121
- install_asdf_env_and_deps
122122
- build:dev
123123

124+
dialyzer:fe_test_helpers:
125+
stage: test
126+
variables:
127+
DIALYZER_FE_TEST_HELPERS_CACHE_VERSION: 1
128+
# --format short can't be used here due to: https://github.com/jeremyjh/dialyxir/issues/314
129+
# --list-unused-filters must be used as a command line option instead of in
130+
# mix.exs due to: https://github.com/jeremyjh/dialyxir/issues/313
131+
script: make dialyzer_fe_test_helpers
132+
cache:
133+
key: $CI_JOB_NAME-$DIALYZER_FE_TEST_HELPERS_CACHE_VERSION
134+
paths:
135+
- "*/_build/dev/*.plt"
136+
- "*/_build/dev/*.plt.hash"
137+
- $MIX_HOME/*.plt
138+
dependencies:
139+
- install_asdf_env_and_deps
140+
- build:dev
141+
124142
dialyzer:freezer_eye:
125143
stage: test
126144
variables:

Makefile

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@ endef
2828

2929

3030
$(eval $(call APP_TEMPLATE,adafruit_io_http_client))
31+
$(eval $(call APP_TEMPLATE,fe_test_helpers))
3132
$(eval $(call APP_TEMPLATE,fe_reporting))
3233
$(eval $(call APP_TEMPLATE,freezer_eye))
3334

34-
install_deps: install_deps_adafruit_io_http_client install_deps_freezer_eye install_deps_fe_reporting
35-
compile: compile_adafruit_io_http_client compile_freezer_eye compile_fe_reporting
36-
test: test_adafruit_io_http_client test_freezer_eye test_fe_reporting
37-
dialyzer: dialyzer_adafruit_io_http_client dialyzer_freezer_eye dialyzer_fe_reporting
38-
check_format: check_format_adafruit_io_http_client check_format_freezer_eye check_format_fe_reporting
39-
check_unused_deps: check_unused_deps_adafruit_io_http_client check_unused_deps_freezer_eye check_unused_deps_fe_reporting
35+
install_deps: install_deps_adafruit_io_http_client install_deps_freezer_eye install_deps_fe_reporting install_deps_fe_test_helpers
36+
compile: compile_adafruit_io_http_client compile_freezer_eye compile_fe_reporting compile_fe_test_helpers
37+
test: test_adafruit_io_http_client test_freezer_eye test_fe_reporting test_fe_test_helpers
38+
dialyzer: dialyzer_adafruit_io_http_client dialyzer_freezer_eye dialyzer_fe_reporting dialyzer_fe_test_helpers
39+
check_format: check_format_adafruit_io_http_client check_format_freezer_eye check_format_fe_reporting check_format_fe_test_helpers
40+
check_unused_deps: check_unused_deps_adafruit_io_http_client check_unused_deps_freezer_eye check_unused_deps_fe_reporting check_unused_deps_fe_test_helpers

fe_test_helpers/.dialyzer_ignore.exs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Used for ignoring dialyzer errors
2+
# See: https://github.com/jeremyjh/dialyxir/tree/fd13c1cef28cde1b563236405e1441ada867b314#elixir-term-format
3+
[]

fe_test_helpers/.formatter.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
4+
]

fe_test_helpers/.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# The directory Mix will write compiled artifacts to.
2+
/_build/
3+
4+
# If you run "mix test --cover", coverage assets end up here.
5+
/cover/
6+
7+
# The directory Mix downloads your dependencies sources to.
8+
/deps/
9+
10+
# Where third-party dependencies like ExDoc output generated docs.
11+
/doc/
12+
13+
# Ignore .fetch files in case you like to edit your project deps locally.
14+
/.fetch
15+
16+
# If the VM crashes, it generates a dump, let's ignore it too.
17+
erl_crash.dump
18+
19+
# Also ignore archive artifacts (built via "mix archive.build").
20+
*.ez
21+
22+
# Ignore package tarball (built via "mix hex.build").
23+
fe_test_helpers-*.tar
24+

fe_test_helpers/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# FETestHelpers
2+
3+
Test helpers for use with FreezerEye.

fe_test_helpers/config/config.exs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file is responsible for configuring your application
2+
# and its dependencies with the aid of the Mix.Config module.
3+
use Mix.Config
4+
5+
# This configuration is loaded before any dependency and is restricted
6+
# to this project. If another project depends on this project, this
7+
# file won't be loaded nor affect the parent project. For this reason,
8+
# if you want to provide default values for your application for
9+
# third-party users, it should be done in your "mix.exs" file.
10+
11+
# You can configure your application as:
12+
#
13+
# config :fe_test_helpers, key: :value
14+
#
15+
# and access this configuration in your application as:
16+
#
17+
# Application.get_env(:fe_test_helpers, :key)
18+
#
19+
# You can also configure a third-party app:
20+
#
21+
# config :logger, level: :info
22+
#
23+
24+
# It is also possible to import configuration files, relative to this
25+
# directory. For example, you can emulate configuration per environment
26+
# by uncommenting the line below and defining dev.exs, test.exs and such.
27+
# Configuration from the imported file will override the ones defined
28+
# here (which is why it is important to import them last).
29+
#
30+
# import_config "#{Mix.env()}.exs"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
defmodule FETestHelpers do
2+
@moduledoc """
3+
Test helpers for FreezerEye
4+
"""
5+
end
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
defmodule FETestHelpers.HeartbeatTracker do
2+
@moduledoc """
3+
Heartbeat tracking helper
4+
"""
5+
6+
alias __MODULE__.Impl
7+
8+
@type start_link_opt :: GenServer.option()
9+
@type tracker :: pid()
10+
11+
@spec start_link([start_link_opt]) :: GenServer.on_start()
12+
def start_link(_opts) do
13+
Agent.start_link(fn -> Impl.new() end)
14+
end
15+
16+
@spec record_heartbeat(tracker) :: :ok
17+
def record_heartbeat(pid) do
18+
Agent.update(pid, fn impl ->
19+
Impl.record_heartbeat(impl)
20+
end)
21+
end
22+
23+
@spec list_heartbeat_timestamps(tracker) :: [DateTime.t()]
24+
def list_heartbeat_timestamps(pid) do
25+
pid
26+
|> Agent.get(& &1)
27+
|> Impl.list_heartbeat_timestamps()
28+
end
29+
30+
@spec list_heartbeat_intervals(tracker, System.time_unit()) :: [integer]
31+
def list_heartbeat_intervals(pid, time_unit \\ :second) do
32+
pid
33+
|> Agent.get(& &1)
34+
|> Impl.list_heartbeat_intervals(time_unit)
35+
end
36+
end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule FETestHelpers.HeartbeatTracker.Impl do
2+
@moduledoc false
3+
4+
@opaque t :: [DateTime.t()]
5+
6+
defguardp is_impl(data) when is_list(data)
7+
8+
@spec new :: t
9+
def new, do: []
10+
11+
@spec record_heartbeat(t, DateTime.t()) :: t
12+
def record_heartbeat(reversed_heartbeats, timestamp = %DateTime{} \\ DateTime.utc_now())
13+
when is_impl(reversed_heartbeats) do
14+
[timestamp | reversed_heartbeats]
15+
end
16+
17+
@spec list_heartbeat_timestamps(t) :: [DateTime.t()]
18+
def list_heartbeat_timestamps(reversed_heartbeats) when is_impl(reversed_heartbeats) do
19+
Enum.reverse(reversed_heartbeats)
20+
end
21+
22+
@spec list_heartbeat_intervals(t, System.time_unit()) :: [integer]
23+
def list_heartbeat_intervals(reversed_heartbeats, time_unit \\ :second) do
24+
reversed_heartbeats
25+
|> Enum.reverse()
26+
|> do_list_heartbeat_intervals(time_unit)
27+
end
28+
29+
defp do_list_heartbeat_intervals(
30+
[%DateTime{} = current | [%DateTime{} = next | _] = rest],
31+
time_unit
32+
) do
33+
[DateTime.diff(next, current, time_unit) | do_list_heartbeat_intervals(rest, time_unit)]
34+
end
35+
36+
defp do_list_heartbeat_intervals([%DateTime{} | []], _), do: []
37+
defp do_list_heartbeat_intervals([], _), do: []
38+
end

0 commit comments

Comments
 (0)