-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Test framework for IEx #1137
Test framework for IEx #1137
Conversation
@alco this looks great, a couple comments:
|
@mururu Is it feasible to allow capture_io to return output in bursts? For instance, if I want to test a bunch of expressions in one IEx session and one of them is an exception, I need to construct a big regex to check the output. See this test. I'd like to be able to rewrite it like this: itest "h helper",
expected: [%r/{:module,M,<<(.+?)>>,{:hello_world,1}}\n/,
"# M\n\nModule M doc:ok\n",
"""* def hello_world()
Hello world
* def hello_world(arg)
Hello world 2
:ok
"""],
input: ["""
defmodule M do
@moduledoc "Module M doc"
@doc "Hello world"
def hello_world
@doc "Hello world 2"
def hello_world(arg)
end
""", "h(M)", "h(M.hello_world)"] So for each item in the input list, we'll get one item in the output. This doesn't necessarily need to be handled by capture_io itself, it could use message passing, for example. Then I would be able to put separate inputs/outputs into lists in IEx.TestFramework. |
itest "h helper", | ||
expected: %r/{:module,M,<<(.+?)>>,{:hello_world,1}}\n# M\n\nModule M doc:ok\n\* def hello_world\(\)\n\nHello world\n\* def hello_world\(arg\)\n\nHello world 2\n:ok/, | ||
input: """ | ||
defmodule M do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should cleanup a module after we dynamically define it in a test. :) Or for this case, couldn't we just call h
in an existing module? Maybe defined those in IEx.Test.Interactive
itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using docs on the test module is a great idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does ExDoc skip modules in test
directories?
@josevalim This is intentional. You can also use functions if you like. For example, those tests are equivalent: itest "color",
opts: [colors: [eval_result: "red"]],
input: "1 + 2",
expected: "\e[31m3\e[0m"
test "color redux" do
assert (capture_iex("1 + 2", [colors: [eval_result: "red"]])) == "\e[31m3\e[0m"
end I find the first one more preferable because it gives you a framework to fill the values in. If you need to test something tricky, you can use |
use IEx.TestFramework | ||
|
||
# All tests that `use IEx.TestFramework` should go into this module to avoid | ||
# race conditions with :iex application. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not going to have race conditions if all the modules are sync.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I create another module and use IEx.TestFramework
there, I get the {:error,{:already_started,:iex}}
error when running tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will need to take a better look then... but the test cases were meant to run on isolation. Unless :application.stop
is async (i.e. when it returns, the application may not have stopped completely yet, which I don't think is the case. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, I've written def teardown_all do
instead of teardown_all do
.
@alco I am still not sold why we need macros here. Compare: itest "color",
opts: [colors: [eval_result: "red"]],
input: "1 + 2",
expected: "\e[31m3\e[0m" To: test "color" do
opts = [colors: [eval_result: "red"]]
assert capture_iex("1 + 2", opts) == "\e[31m3\e[0m"
end There isn't really a benefit and everyone knows exactly how the second one works. There is no gotchas about when to use regular expressions, which expressions are supported by the macro, etc... |
@josevalim Here are the same tests rewritten using Does it look good? |
@alco It is possible to allow capture_io to return output each time it receives an output request( |
@mururu Thanks for the feedback. |
I've moved things around and renamed the modules. If it's OK, I will only need to add docs and tests for the remaining helpers. |
@alco this is perfect! Before you start working on the rest, do you mind squashing those commits together so we have the whole foundation set up in one single commit? Easier to visualize latter. |
@josevalim Please take another look. Tests for |
@@ -0,0 +1,28 @@ | |||
Code.require_file "../test_helper.exs", __DIR__ | |||
|
|||
defmodule IEx.Options.Test do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IEx.OptionsTest
:)
@josevalim I've rewritten the tests using Please take a look at |
There appears to be some kind race condition between the tests. Contrary to what Travis shows, this is what I get: |
We also need to test this one... we should just not forget... 6397ced |
Basically, to reproduce it, we just need to type something like "%B(%{bar})" in the shell and it should not fail. |
I've added tests for color, but the aren't working. Colors are working correctly in IEx, but after capture_io some escapes are lost. |
Dough, I see what's wrong: |
This is almost ready. Once the helpers issues are resolved, I'd like to squash some commits before merging. |
File.rm! "Elixir.Helpers_test_module.beam" | ||
|
||
# FIXME: This errors out with "Module 'Elixir.Helpers_test_module' must be purged before loading" | ||
#true = :code.delete Helpers_test_module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is in this commented line. When you call the l
helper, it purges the old version and then loads a new version. By loading a new version, the previous version is now marked as old. So when you get to :code.delete
to make the current new version as old, you can't actually do it, because the old version slot is still taken, so you do need to explicitly purge once again before. By fixing this, everything else seems to work fine (except the r
test that still fails).
@alco a couple quick notes: the test fixtures uses non conventional names like |
Thanks, I'll fix those. What about this line? That System.cmd call would better go away. |
Use the parallel compiler or c helper and then purge and delete the module? José Valim |
I renamed the module to Sample. What name should I use for the file? |
I'd like to not interfere with the modules loaded in runtime while testing module loading. Is it technically not possible to compile a module from Erlang without loading it? |
Give the file the name of the module, like sample.ex. About the r helper, José Valim |
@josevalim Please take another look. |
@@ -257,6 +264,7 @@ defmodule IEx.Helpers do | |||
""" | |||
def l(module) do | |||
:code.purge(module) | |||
# FIXME: shouldn't we use Code.load_file here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. They do different things. We want to continue using :code.load_file here.
@alco it looks good! we should just get rid of the #FIXME that have been answered (we should use :code.load_file and there isn't a way of compiling a module without loading it) and then pls go ahead and merge it. |
This is WIP. Curious to know if I'm moving in the right direction.
TODO
IEx.Case