Hi all! Curious if someone could share a pattern for conditional tests where the language parser changed. If the Elixir version is >= 1.15.0, I want to test this file, otherwise I don’t want it read it at all because parsing will fail.
One option might be using a suffix other than _test.exs
for this particular test (like _test_1_15.exs
), and then manually importing the file in my test helper if the Elixir version supports it. I’m wondering if there is another convention used elsewhere, though.
I assume you are aware of Version.match?/2
so you must be looking for something beyond that?
I am not aware of a pattern (though I am not good at those) so I’d probably just do:
if Version.match?(System.version(), "< 1.15.0") do
# do tests that would blow up if using 1.15.0 or above.
end
in the test files themselves, while keeping their _test.exs
format.
That technique works great when you’re dealing with a new API – a function or arity or something that was added in a later version. But with multi-letter sigils, it’s the parsing step that fails.
$ echo 'if Version.match?(System.version(), ">= 1.15.0"), do: ~SIGIL""' > scratch.exs
$ asdf shell elixir 1.15.0-otp-25
$ elixir scratch.exs
error: undefined function sigil_SIGIL/2 (there is no such import)
scratch.exs:1
** (CompileError) scratch.exs: cannot compile file (errors have been logged)
(stdlib 4.0.1) lists.erl:1462: :lists.mapfoldl_1/3
(stdlib 4.0.1) lists.erl:1463: :lists.mapfoldl_1/3
(stdlib 4.0.1) lists.erl:1462: :lists.mapfoldl_1/3
(elixir 1.15.0) expanding macro: Kernel.if/2
$ asdf shell elixir 1.14.5-otp-25
$ elixir scratch.exs
** (SyntaxError) scratch.exs:1:55: invalid sigil delimiter: "I" (column 57, code point U+0049). The available delimiters are: //, ||, "", '', (), [], {}, <>
|
1 | if Version.match?(System.version(), ">= 1.15.0"), do: ~SIGIL""
| ^
(elixir 1.14.5) lib/code.ex:1260: Code.require_file/2
So the test file can’t even load in earlier versions, which is the issue for me here.
Ah, I see. Then perhaps instead of directly using the sigil thingy you can do Code.require_file
in the do
clause of the if
?
This works:
# scratch.exs
if Version.match?(System.version(), ">= 1.15.0") do
Code.require_file("e_1_15.exs")
else
IO.puts("Skipping test because they can't run below Elixir 1.15")
end
# e_1_15.exs
~SIGIL""
asdf local elixir 1.15.0-otp-25
elixir scratch.exs
error: undefined function sigil_SIGIL/2 (there is no such import)
e_1_15.exs:1
** (CompileError) e_1_15.exs: cannot compile file (errors have been logged)
(elixir 1.15.0) lib/code.ex:1432: Code.require_file/2
Which is normal, as we don’t have that sigil defined.
And then:
asdf local elixir 1.14.5-otp-25
elixir scratch.exs
Skipping test because they can't run below Elixir 1.15
2 Likes
I think that’s what I’ll go with! Thanks.
There is also a configuration for test paths in your mix.exs, so you could have a special directory for v1.15 tests, but I think Code.require_file is fine. Or alternatively use one of the Code.eval_* in your tests.
3 Likes
I thought about test paths, but I think I read that each test path required it’s own helper and I have some config I didn’t want to duplicate.
I’ll go with require_file for now, but I should have thought of just evaling a string. Thanks!