Erlang ct:run_test dependencies - erlang

I try to run common test suites from erl shell with command:
> ct:run_test([{spec,"test/spec"}]).
Test suite fails with an error:
undefined function eredis:start_link/3
But if I type in the shell this:
> eredis:module_info().
I can see that dependent module eredis was loaded:
[{exports,[{start_link,0},
{start_link,2},
...
{start_link,6},
...
Why ct:run_test does not see dependent modules and how to run it correctly from erl shell?

I found that the problem is not in dependencies.
I wrote sample code that reproduces the problem.
One should always set absolute path to ebin directory (with -pa option) when running ct:run_test/1 from erlang shell.
Otherwise common tests started from erlang shell with the command:
$ ./rebar3.1 compile && erl -pa _build/default/lib/ct_fail/ebin/
> ct:run_test([{spec,"test/spec"}]).
will fail with the error:
=== ERROR! init_per_testcase crashed!
Location: [{ct_fail,start_link},
{fail_SUITE,init_per_testcase,16},
{test_server,do_init_per_testcase,1191},
{test_server,run_test_case_eval1,955},
{test_server,run_test_case_eval,926}]
Reason: {undef,[{ct_fail,start_link,[1,2],[]},
{fail_SUITE,init_per_testcase,2,
[{file,"fail_SUITE.erl"},{line,16}]},
{test_server,do_init_per_testcase,2,
[{file,"test_server.erl"},{line,1191}]},
{test_server,run_test_case_eval1,6,
[{file,"test_server.erl"},{line,955}]},
{test_server,run_test_case_eval,9,
[{file,"test_server.erl"},{line,926}]}]}
The most interesting part is that when module_info command is executed before test running:
$ ./rebar3.1 compile && erl -pa _build/default/lib/ct_fail/ebin/
> ct_fail:module_info().
ct_fail:module_info().
[{module,ct_fail},
{exports,[{start_link,0},
{start_link,1},
{start_link,2},
{start_link,3},
{start_link,4},
{init,1},
...
> ct:run_test([{spec,"test/spec"}]).
then tests will succeed!
By the way when test fails:
$ ./rebar3.1 compile && erl -pa _build/default/lib/ct_fail/ebin/
> ct:run_test([{spec,"test/spec"}]).
...
function call that caused test failure disappears from module exports list:
> ct_fail:module_info().
[{module,ct_fail},
{exports,[{start_link,0},
{start_link,1},
{start_link,3},
{start_link,4},
{init,1},
...
ct_fail:start_link/2 is not exported anymore!

Related

Debugging an Erlang code with eunit in IntelliJ

IDE: IntelliJ IDEA 2022.2.2
I set a breakpoint in my Erlang code which has eunit testcases.
In order to debug it, I clicked on "Run --> Debug". I got an error "Test suite was cancelled". What could be the reason?
"C:\Program Files\Erlang OTP\bin\erl.exe" -pa C:\Users\bhu\AppData\Local\Temp\intellij_erlang_debugger_7789 -pa C:/Users/bhu/AppData/Local/Temp/eunit_teamcity9 -pa F:/1TB/P/workspace-IntelliJ-Erlang1/out/test/workspace-IntelliJ-Erlang1 -pa F:/1TB/P/workspace-IntelliJ-Erlang1 -run debugnode main 50067 -noshell -s init stop
Testing started at 20:59 ...
** Invalid beam file or no abstract code: "F:\\1TB\\P\\workspace-IntelliJ-Erlang1\\src\\my_sort.erl"
Test suite was cancelled. Reason: {abort,{module_not_found,my_sort}}
The error means that the module my_sort was not loaded into the VM. You can find the function here. I think this is because the search path to your module my_sort is not added to the code path using the flag -pa /mypath/my_sort.erl.
Could you try to add to the code path using -pa ..?

Using Elixir module from Erlang fails

I am trying to use in Erlang module a beam-file compiled from Elixir source. It raises error when I run Erlang node but I can use the code directly from Elixir.
Elixir module:
defmodule Recursion do
def print_multiple_times(msg, n) when n <= 1 do
IO.puts msg
end
def print_multiple_times(msg, n) do
IO.puts msg
print_multiple_times(msg, n - 1)
end
end
Erlang module:
-module(use_recur).
-define(elixir__recursion, 'Elixir.Recursion').
-export([hey/0]).
hey() ->
?elixir__recursion:print_multiple_times("Hello!", 3).
Compile both:
$ rm -f *.beam
$ elixirc recursion.ex
$ erlc use_recur.erl
Run Erlang:
$ erl -run use_recur hey -run init stop -noshell {"init terminating in do_boot",{undef,[{'Elixir.IO',puts,["Hello!"],[]},{'Elixir.Recursion',print_multiple_times,2,[{file,"recursion.ex"},{line,7}]},{init,start_em,1,[]},{init,do_boot,3,[]}]}}
init terminating in do_boot ({undef,[{Elixir.IO,puts,Hello!,[]},{Elixir.Recursion,print_multiple_times,2,[{},{}]},{init,start_em,1,[]},{init,do_boot,3,[]}]})
Crash dump is being written to: erl_crash.dump...done
Elixir script:
Recursion.print_multiple_times "Hello!", 3
Runs successfully:
$ elixir elx_recur.exs
Hello!
Hello!
Hello!
Why does it happen? I'd say Erlang's output should be the same.
The error means that Erlang could not find a module named 'Elixir.IO'. This module is part of core Elixir. You'll need to add the ebin folder of your Elixir installation to Erlang's load path using -pa (or other similar flags like -pz) to make Erlang load Elixir's core libraries, as that folder contains the compiled .beam files of Elixir core, including Elixir.IO.beam.
erl -pa /path/to/elixir/ebin ...

How i compile and run erlang program?

i have
freebsd 10
folder /usr/home/ec2-user/ezmq with files from https://github.com/zeromq/ezmq
installed erlang
then i:
in command line run - erl
in erl
c("/usr/home/ec2-user/ezmq/examples/hwclient.erl").
c("/usr/home/ec2-user/ezmq/examples/hwserver.erl").
hwserver:main().
hwclient:main().
and have error
exception error: undefined function ezmq:start/1
in function hwserver:main/0 (/usr/home/ec2-user/ezmq/examples/hwserver.erl, line 14)
and have error
exception error: undefined function ezmq:start/1 in function hwclient:main/0 (/usr/home/ec2-user/ezmq/examples/hwclient.erl, line 14)
how i compile and run
this https://github.com/zeromq/ezmq/blob/master/examples/hwclient.erl + hwserver.erl
examples?
and i have error
** exception exit: {{undef,[{gen_listener_tcp,start_link,
[ezmq_tcp_socket,
[<0.49.0>,<<>>,5555,
[binary,inet,
{active,false},
{send_timeout,5000},
{backlog,10},
{nodelay,true},
{packet,raw},
{reuseaddr,true}]],
[]],
[]},
{ezmq,handle_call,3,[{file,"src/ezmq.erl"},{line,238}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]},
{gen_server,call,[<0.49.0>,{bind,tcp,5555,[]}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
in call from hwserver:main/0 (/usr/home/ec2-user/ezmq/examples/hwserver.erl, line 15)
This project uses rebar, so try this:
Download it from here: https://github.com/rebar/rebar/wiki/rebar
Make it executable
Compile: ./rebar compile
Open two terminals
In terminal 1, run: erl -pa ebin -pa /deps/gen_listener_tcp/ebin
The -pa option tells the vm where to load beam files from.
In the erlang shell, run:
c("examples/hwserver")
hwserver:main().
In terminal 2, run: erl -pa ebin -pa /deps/gen_listener_tcp/ebin
In the erlang shell, run:
c("examples/hwclient")
hwclient:main().
You should see a bunch of output.
The make file seems to require putting rebar in your path. Once you do that, you can try the make targets.

run eunit test from console using erl -noshell

I wanted to run the following eunit test command from console
eunit:test([test_module, [verbose]).
I tried this, but seems not working
erl -noshell -pa ./ebin -s eunit test test_module verbose -init stop
~/uid_server$erl -noshell -pa ./ebin -s eunit test test_module verbose -init stop
undefined
*** test module not found ***
::test_module
=======================================================
Failed: 0. Skipped: 0. Passed: 0.
One or more tests were cancelled.
Do you know how to pass not a simple arguments properly from console?
Your parameters look wrong. This should work:
erl -noshell -pa ebin -eval "eunit:test(test_module, [verbose])" -s init stop
-s can only run functions without arguments by specifying the module and function name (for example init and stop to execute init:stop()).
You can also pass one list to a function of arity 1 like this:
-s foo bar a b c
would call
foo:bar([a,b,c])
All the parameters are passed as a list of atoms only (even when you try to use some other characters, such as numbers, they are converted to atoms).
So since you want to pass two params and not only atoms if you want to run eunit:test/2 you'd have to use -eval which takes a string containing Erlang code as an argument. All -eval and -s functions are executed sequentially in the order they are defined.
Also, make sure you have your test code in ./ebin as well (otherwise write -pa ebin test_ebin where test_ebin is where your test code is).
You can also use rebar...
Get rebar by cd'ing to your project directory and typing the following:
curl http://cloud.github.com/downloads/basho/rebar/rebar -o rebar
chmod u+x rebar
Add the following to your module under test, right after last export:
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
Next, add your tests at the bottom of your module, wrapped in an ifdef like so:
-ifdef(TEST).
simple_test() ->
?assertNot(true).
-endif.
Lastly, run rebar from your shell like so:
./rebar compile eunit
you can try quote parameters instead of listing.
erl -noshell -pa ./ebin -s eunit test "test_module verbose" -init stop
More than eight years passed since the question, but there's still a nice solution not mentioned in the previous answers.
Once you are using EUnit, you can leverage from some of it's "automagic" features. One of them is an automatic export of the test/0 function, containing all the tests for the module.
So, if you are writing your tests alongside with the source code in the same module, all you have to do is:
$ erl -noshell -run your_module test -run init stop
If you are writing the tests in a separated, dependent module (as you should), you have to point to that module:
$ erl -noshell -run your_module_tests test -run init stop
All this will work fine, but the test won't be run in verbose mode as the OP required, but this is simple to solve with the EUNIT environment variable set to verbose.
Final version:
$ EUNIT=verbose erl -noshell -run your_module_tests test -run init stop
Have fun with Erlang and EUnit!
I use this script: https://github.com/lafka/dotconfig/blob/master/bin/eunit-module to run eunit on specific modules.
Example:
eunit-module <module> src ebin -I deps
That will do a couple of things:
Arg #2 is the directory where .erl resides
Arg #3 is the directory to output the compiled .beam
Arg #4++ is all the additional paths to add to your code path
Using -I specifies additional code paths AND where to look for files referenced with -include_lib

Forcing erl -make to recompile files when macros are changed

I tried to do something similar to How to make two different source directories in a Makefile output to one bin directory?, so I have these files (relative to my project root):
Emakefile:
% EMakefile
% -*- mode: erlang -*-
{["src/*", "src/*/*", "src/*/*/*"],
[{i, "include"}, {outdir, "ebin"}, debug_info]}.
test/Emakefile:
% EMakefile
% -*- mode: erlang -*-
{["../src/*", "../src/*/*", "../src/*/*/*"],
[{i, "../include"}, {outdir, "../ebin"}, debug_info, {d, 'TEST'}]}.
Makefile:
EPATH=-pa ebin
all: before_compile
erl -make
all_test: before_compile
cd test
erl -make
cd ..
before_compile: mk_ebin copy_sqlite create_db copy_config copy_dot_app
test: all_test
erl -noshell $(EPATH) \
-s tests run \
-s init stop
rm -f ct.db
clean:
rm -fv ebin/*
... dependencies of before_compile
The problem is that running make test doesn't recompile any modules which are already compiled with make. It seems erl -make doesn't care that they were compiled without TEST defined, it just checks that the modules themselves are older than beam-files. How do I force it to recompile (and avoid recompilation when it isn't needed)?
UPDATE: Strangely, when running make all_test immediately after make clean, it appears that ./Emakefile is used instead of test/Emakefile: I am getting
Recompile: src/tests
Recompile: src/server_protocol_client
etc. and no tests instead of
Recompile: ../src/tests
Recompile: ../src/server_protocol_client
which I get by doing cd test; erl -make manually. Any idea why? Anyway, I've fixed this problem by removing test/Emakefile and replacing all_test in Makefile:
all_test: before_compile
erl -noshell -eval "make:all([{d, 'TEST'}])." -s init stop
all_test: before_compile
cd test
erl -make
cd ..
This is incorrect. Each line produces its own process. Do such:
all_test: before_compile
cd test; \
erl -make

Resources