Skip to content

Commit

Permalink
Fix tests on Elixir 1.15 (#335)
Browse files Browse the repository at this point in the history
Starts running tests on Elixir 1.15 with OTP 26

The map ordering change in OTP 26 caused many tests to break because they were
implicitly relying on a not guaranteed order.

Adds machete to add a sorted_list test helper (instead of changing many
assertions to be split across two statements)
  • Loading branch information
axelson authored Sep 25, 2023
1 parent e707dbf commit 910d97e
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 62 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:
include:
- elixir: '1.11.4'
otp: '24.2'
- elixir: '1.15.5'
otp: '26.0'

steps:
- uses: actions/checkout@v3
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ defmodule Scenic.Mixfile do
{:credo, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:excoveralls, ">= 0.0.0", only: :test, runtime: false},
{:inch_ex, "~> 2.0", only: [:dev, :docs], runtime: false},
{:dialyxir, "~> 1.1", only: :dev, runtime: false}
{:dialyxir, "~> 1.1", only: :dev, runtime: false},
{:machete, "~> 0.2.8", only: :test}
]
end

Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"inch_ex": {:hex, :inch_ex, "2.0.0", "24268a9284a1751f2ceda569cd978e1fa394c977c45c331bb52a405de544f4de", [:mix], [{:bunt, "~> 0.2", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "96d0ec5ecac8cf63142d02f16b7ab7152cf0f0f1a185a80161b758383c9399a8"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"machete": {:hex, :machete, "0.2.8", "ca7be4d2d65f05f4ab5eac0f5fd339cea08d4d75f775ac6c22991df2baed5d80", [:mix], [], "hexpm", "09bf5b306385d01c877453ead94914a595beecba3f2525c7364ee3ba0630a224"},
"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.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [: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", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
Expand Down
9 changes: 6 additions & 3 deletions test/scenic/component/input/text_field_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#

defmodule Scenic.Component.Input.TextFieldTest do
use ExUnit.Case, async: false
use Scenic.Test.DataCase, async: false
use Machete
import Scenic.Test.SortedListMatcher
doctest Scenic.Component.Input.TextField

alias Scenic.Graph
Expand Down Expand Up @@ -96,7 +98,7 @@ defmodule Scenic.Component.Input.TextFieldTest do
assert Input.fetch_captures!(vp) == {:ok, []}
Input.send(vp, @press_in)
force_sync(vp.pid, pid)
assert Input.fetch_captures!(vp) == {:ok, [:codepoint, :cursor_button, :key]}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_button, :key])}

Input.send(vp, @cp_k)
assert_receive({:fwd_event, {:value_changed, :text_field, "kInitial value"}}, 200)
Expand All @@ -105,7 +107,8 @@ defmodule Scenic.Component.Input.TextFieldTest do
test "press_out releases and ends editing", %{vp: vp, pid: pid} do
Input.send(vp, @press_in)
force_sync(vp.pid, pid)
assert Input.fetch_captures!(vp) == {:ok, [:codepoint, :cursor_button, :key]}

assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_button, :key])}

Input.send(vp, @press_out)
force_sync(vp.pid, pid)
Expand Down
16 changes: 7 additions & 9 deletions test/scenic/scene_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#

defmodule Scenic.SceneTest do
use ExUnit.Case, async: false
use Scenic.Test.DataCase, async: false
doctest Scenic.Scene

alias Scenic.ViewPort
Expand Down Expand Up @@ -382,34 +382,32 @@ defmodule Scenic.SceneTest do
test "fetch_requests works", %{scene: scene} do
Scenic.Scene.request_input(scene, :cursor_button)

assert Scene.fetch_requests(scene) == {:ok, [:cursor_button, :codepoint]}
assert Scene.fetch_requests(scene) ~> {:ok, sorted_list([:cursor_button, :codepoint])}

# request input from outside the scene
:ok = ViewPort.Input.request(scene.viewport, :cursor_pos)

# should only show input form the scene even though this is the caller
assert Scene.fetch_requests(scene) == {:ok, [:cursor_button, :codepoint]}
assert Scene.fetch_requests(scene) ~> {:ok, sorted_list([:cursor_button, :codepoint])}
end

test "request_input works", %{scene: scene} do
Scenic.Scene.request_input(scene, :cursor_button)

assert Scene.fetch_requests(scene) ==
{:ok, [:cursor_button, :codepoint]}
assert Scene.fetch_requests(scene) ~> {:ok, sorted_list([:cursor_button, :codepoint])}

:ok = Scene.request_input(scene, :cursor_pos)

assert Scene.fetch_requests(scene) ==
{:ok, [:cursor_pos, :cursor_button, :codepoint]}
assert Scene.fetch_requests(scene) ~> {:ok, sorted_list([:cursor_pos, :cursor_button, :codepoint])}
end

test "unrequest_input works", %{scene: scene} do
Scenic.Scene.request_input(scene, :cursor_button)

assert Scene.fetch_requests(scene) == {:ok, [:cursor_button, :codepoint]}
assert Scene.fetch_requests(scene) ~> {:ok, sorted_list([:cursor_button, :codepoint])}

:ok = Scene.unrequest_input(scene, :codepoint)
assert Scene.fetch_requests(scene) == {:ok, [:cursor_button]}
assert Scene.fetch_requests(scene) ~> {:ok, sorted_list([:cursor_button])}
end

test "fetch_captures works", %{scene: scene} do
Expand Down
98 changes: 49 additions & 49 deletions test/scenic/view_port/input_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#

defmodule Scenic.ViewPort.InputTest do
use ExUnit.Case, async: false
use Scenic.Test.DataCase, async: false
doctest Scenic.ViewPort.Input

alias Scenic.ViewPort
Expand Down Expand Up @@ -46,86 +46,86 @@ defmodule Scenic.ViewPort.InputTest do
end

test "Test that capture/release/list_captures work", %{vp: vp} do
assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}

:ok = Input.capture(vp, :cursor_pos)
assert Input.fetch_captures(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([:cursor_pos])}

:ok = Input.capture(vp, [:key, :codepoint])
assert Input.fetch_captures(vp) == {:ok, [:key, :cursor_pos, :codepoint]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([:key, :cursor_pos, :codepoint])}

:ok = Input.release(vp, :key)
assert Input.fetch_captures(vp) == {:ok, [:cursor_pos, :codepoint]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([:cursor_pos, :codepoint])}

:ok = Input.release(vp, :all)
assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}
end

test "list_captures and list_captures! work", %{vp: vp} do
assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_captures!(vp) == {:ok, []}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([])}

Agent.start(fn ->
:ok = Input.capture(vp, [:codepoint])
end)

assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_captures!(vp) == {:ok, [:codepoint]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint])}

:ok = Input.capture(vp, :cursor_pos)
assert Input.fetch_captures(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_captures!(vp) == {:ok, [:codepoint, :cursor_pos]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([:cursor_pos])}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_pos])}
end

test "captures are cleaned up when the owning process stops", %{vp: vp} do
# set up a capture
:ok = Input.capture(vp, [:codepoint])
assert Input.fetch_captures!(vp) == {:ok, [:codepoint]}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([:codepoint])}

# fake indicate this process went down
Process.send(vp.pid, {:DOWN, make_ref(), :process, self(), :test}, [])

assert Input.fetch_captures!(vp) == {:ok, []}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([])}
end

test "Test that request/unrequest/list_requests work", %{vp: vp} do
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}

:ok = Input.request(vp, :cursor_pos)
assert Input.fetch_requests(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([:cursor_pos])}

:ok = Input.request(vp, [:key, :codepoint])
assert Input.fetch_requests(vp) == {:ok, [:key, :cursor_pos, :codepoint]}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([:key, :cursor_pos, :codepoint])}

:ok = Input.unrequest(vp, :key)
assert Input.fetch_requests(vp) == {:ok, [:cursor_pos, :codepoint]}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([:cursor_pos, :codepoint])}

:ok = Input.unrequest(vp, :all)
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}
end

test "fetch_requests and fetch_requests! work", %{vp: vp} do
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_requests!(vp) == {:ok, []}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([])}

Agent.start(fn ->
:ok = Input.request(vp, [:codepoint])
end)

assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_requests!(vp) == {:ok, [:codepoint]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:codepoint])}

:ok = Input.request(vp, :cursor_pos)
assert Input.fetch_requests(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_requests!(vp) == {:ok, [:codepoint, :cursor_pos]}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([:cursor_pos])}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_pos])}
end

test "requests are cleaned up with the owning process stops", %{vp: vp, scene: scene} do
:ok = Input.request(vp, :cursor_pos)
Scenic.Scene.request_input(scene, :codepoint)
assert Input.fetch_requests!(vp) == {:ok, [:codepoint, :cursor_pos]}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:codepoint, :cursor_pos])}
Scenic.Scene.stop(scene)
assert Input.fetch_requests!(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:cursor_pos])}
end

# ----------------
Expand All @@ -135,7 +135,7 @@ defmodule Scenic.ViewPort.InputTest do
%{vp: vp, scene: scene} do
# make like a driver
GenServer.cast(vp.pid, {:register_driver, self()})
assert Input.fetch_requests!(vp) == {:ok, []}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([])}

graph =
Scenic.Graph.build()
Expand All @@ -162,7 +162,7 @@ defmodule Scenic.ViewPort.InputTest do
} do
# make like a driver
GenServer.cast(vp.pid, {:register_driver, self()})
assert Input.fetch_requests!(vp) == {:ok, []}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([])}

graph =
Scenic.Graph.build()
Expand All @@ -179,7 +179,7 @@ defmodule Scenic.ViewPort.InputTest do
} do
# make like a driver
GenServer.cast(vp.pid, {:register_driver, self()})
assert Input.fetch_requests!(vp) == {:ok, []}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([])}

graph =
Scenic.Graph.build()
Expand All @@ -193,7 +193,7 @@ defmodule Scenic.ViewPort.InputTest do
test ":cursor_pos only the input listed in a input style is requested", %{vp: vp, scene: scene} do
# make like a driver
GenServer.cast(vp.pid, {:register_driver, self()})
assert Input.fetch_requests!(vp) == {:ok, []}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([])}

graph =
Scenic.Graph.build()
Expand Down Expand Up @@ -333,11 +333,11 @@ defmodule Scenic.ViewPort.InputTest do
# specific input types

test "cursor_scroll request works", %{vp: vp} do
assert Input.fetch_captures!(vp) == {:ok, []}
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}

:ok = Input.request(vp, :cursor_scroll)
assert Input.fetch_requests(vp) == {:ok, [:cursor_scroll]}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([:cursor_scroll])}

assert Input.send(vp, {:cursor_scroll, {{1, 2}, {3, 4}}}) == :ok

Expand All @@ -348,11 +348,11 @@ defmodule Scenic.ViewPort.InputTest do
end

test "cursor_scroll capture works", %{vp: vp} do
assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}

:ok = Input.capture(vp, :cursor_scroll)
assert Input.fetch_captures(vp) == {:ok, [:cursor_scroll]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([:cursor_scroll])}

assert Input.send(vp, {:cursor_scroll, {{1, 2}, {3, 4}}}) == :ok

Expand All @@ -363,11 +363,11 @@ defmodule Scenic.ViewPort.InputTest do
end

test "cursor_pos request works", %{vp: vp} do
assert Input.fetch_captures!(vp) == {:ok, []}
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}

:ok = Input.request(vp, :cursor_pos)
assert Input.fetch_requests(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([:cursor_pos])}

assert Input.send(vp, {:cursor_pos, {1, 2}}) == :ok

Expand All @@ -378,11 +378,11 @@ defmodule Scenic.ViewPort.InputTest do
end

test "cursor_pos capture works", %{vp: vp} do
assert Input.fetch_captures(vp) == {:ok, []}
assert Input.fetch_requests(vp) == {:ok, []}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests(vp) ~> {:ok, sorted_list([])}

:ok = Input.capture(vp, :cursor_pos)
assert Input.fetch_captures(vp) == {:ok, [:cursor_pos]}
assert Input.fetch_captures(vp) ~> {:ok, sorted_list([:cursor_pos])}

assert Input.send(vp, {:cursor_pos, {1, 2}}) == :ok

Expand All @@ -396,7 +396,7 @@ defmodule Scenic.ViewPort.InputTest do
# drivers are sent input updates

test "drivers are sent requested input updates", %{vp: vp} do
assert Input.fetch_requests!(vp) == {:ok, []}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([])}
:ok = Input.request(vp, :cursor_button)

GenServer.cast(vp.pid, {:register_driver, self()})
Expand All @@ -418,7 +418,7 @@ defmodule Scenic.ViewPort.InputTest do
test "drivers are sent requested input updates when a scene goes down", %{vp: vp, scene: scene} do
Scenic.Scene.request_input(scene, :cursor_button)

assert Input.fetch_requests!(vp) == {:ok, [:cursor_button]}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:cursor_button])}

GenServer.cast(vp.pid, {:register_driver, self()})
assert_receive({:_request_input_, [:cursor_button]}, 100)
Expand All @@ -432,8 +432,8 @@ defmodule Scenic.ViewPort.InputTest do
test "drivers are sent captured input updates", %{vp: vp, scene: scene} do
Scenic.Scene.request_input(scene, :cursor_button)

assert Input.fetch_captures!(vp) == {:ok, []}
assert Input.fetch_requests!(vp) == {:ok, [:cursor_button]}
assert Input.fetch_captures!(vp) ~> {:ok, sorted_list([])}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:cursor_button])}

GenServer.cast(vp.pid, {:register_driver, self()})
assert_receive({:_request_input_, [:cursor_button]}, 100)
Expand All @@ -454,7 +454,7 @@ defmodule Scenic.ViewPort.InputTest do
test "drivers are sent captured input updates when a scene goes down", %{vp: vp, scene: scene} do
Scenic.Scene.request_input(scene, :cursor_button)

assert Input.fetch_requests!(vp) == {:ok, [:cursor_button]}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:cursor_button])}
self = self()
# have to have an agent do the capture so that it comes from a different pid than this
# test, which is pretending to be a driver...
Expand All @@ -479,7 +479,7 @@ defmodule Scenic.ViewPort.InputTest do

# should get an update when the owning pid goes down
# calling fetch_requests! makes sure the vp has processed the agent DOWN message
assert Input.fetch_requests!(vp) == {:ok, [:cursor_button]}
assert Input.fetch_requests!(vp) ~> {:ok, sorted_list([:cursor_button])}
assert_receive({:_request_input_, [:cursor_button]}, 100)
end

Expand Down
Loading

0 comments on commit 910d97e

Please sign in to comment.