Debugging an Erlang code with eunit in IntelliJ - erlang

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 ..?

Related

Erlang ct:run_test dependencies

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!

What's the difference between code:add_path and using -pa on the command line?

This is my .erlang file:
code:add_path("/home/root/projects/myapp/ebin").
code:add_path("/home/root/projects/esmtp/ebin").
application:load(esmtp),
application:set_env(esmtp, smarthost, {"localhost",25}),
application:set_env(esmtp, default_from, "<stuff>"),
application:start(esmtp).
When I run:
erl -boot myapp
it fails:
{"init terminating in do_boot",{'cannot get bootfile','myapp.boot'}}
Crash dump was written to: erl_crash.dump
init terminating in do_boot ()
But if I run:
erl -boot myapp -pa /home/root/projects/myapp/ebin
It works fine. That leads me to believe that -pa on the command line is treated differently than code:add_path() in the .erlang file. I know the .erlang file is in effect, because esmtp loads and runs correctly. What's the difference between -pa and code:add_path()?
The .erlang file is read and executed after the boot file is loaded, so the path has not been added when erlang is looking for the boot file.
The -pa on the other hand is added before the vm starts to look for the boot file so then it is found.

How to run erlang (rebar build) application

I am new to Erlang world and currently can't figure out how to start my dummy erlang application. Probably, I am just missing something... So, I created an application with rebar (rebar create-app appid=dummys).
Currently I have
rebar.config
src/dummys.app.src
src/dummys_app.erl
src/dummys_sup.erl
I have found that in order to run an application during a development it is better to create an additional start method which should call application:start(module).
I added some basic logging to my start methods..
start() ->
error_logger:info_msg("Starting app(dev)..~n"),
application:start(dummys_app).
start(_StartType, _StartArgs) ->
error_logger:info_msg("Starting app..~n"),
dummys_sup:start_link().
If I try
erl -noshell -pa ebin -s application start dummys
erl -noshell -pa ebin -s application start dummys_app
there are no output..
If I try
erl -noshell -pa ebin -s dummys start
erl crashes with an error..
If I try
erl -noshell -pa ebin -s dummys_app start
it outputs just "Starting app(dev).." and that's all. But I also expect to see "Starting app.."
What I am missing or doing wrong??
=============
And another question: How to add a new module to my dummy application correctly? For example I have an additional module called "*dummys_cool*" which has a "start" method. How to tell my application to run that "dummys_cool#start" method?
Thank you!
For quick development, if you just want to ensure your appliction can start, start a shell, then start the application:
erl -pa ebin
1> dummys_app:start().
That will give you a clean indication of what is wrong and right without the shell bombing out after.
Since you're making an application to run, rather than just a library to share, you'll want to make a release. Rebar can get you most of the way there:
mkdir rel
cd rel
rebar create-node nodeid=dummysnode
After you've compiled your application, you can create a release:
rebar generate
This will build a portable release which includes all the required libraries and even the erlang runtime system. This is put by default in the rel/ directory; in your case rel/dummys.
Within that directory there will be a control script that you can use to start, stop, and attach to the application:
rel/dummys/bin/dummys start
rel/dummys/bin/dummys stop
rel/dummys/bin/dummys start
rel/dummys/bin/dummys attach
Have a look at your dummys.app.src file. The meaning of all the directives is explained in the 'app' manpage, but the one I suspect is missing here is mod, which indicates the name of your application callback module. So make sure that this line is present:
{mod, {dummys_app, []}}
The empty list in there will be passed as the StartArgs argument to dummys_app:start/2.
To make a new module start along with your application, add it to the supervision tree in dummys_sup:init. This function should look something like:
init(_) ->
{ok, {{one_for_one, 10, 10},
[{dummys_cool, {dummys_cool, start_link, []},
permanent, brutal_kill, worker, [dummys_cool]}]}.
This is described in the 'supervisor' manpage, but basically this means that on startup, this supervisor will start one child process. dummys_cool:start_link() will be called, and that function is expected to spawn a new process, link to it, and return its process id. If you need more processes, just add more child specifications to the list.
erl -noshell -pa ebin -s application start dummys
The code above will not work because application:start([dummys]) will be called.
You can take a reference of the Erlang documentation for details.
For your case,
erl -noshell -pa ebin -s dummys
I ran into this problem, and this was the first answer on Google.
If you are using rebar3, the standard configuration will have a shell command that compiles your project and opens a shell:
$ rebar3 shell
===> Analyzing applications...
===> Compiling myapp
Erlang/OTP 21 [erts-10.2.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1]
Eshell V10.2.4 (abort with ^G)
1> ===> Booted myapp

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

Erlang emakefile explain

I have an Emakefile that looks like:
%% --
%%
%% --
{'/Users/user/projects/custom_test/trunk/*',
[debug_info,
{outdir, "/Users/user/projects/custom_test/trunk/ebin"},
{i, "/Users/user/projects/custom_test/trunk/include/."}
]
}.
What is an explanation in layman's terms for what each item does in the list?
How do I run the emakefile so that I am able to compile it?
After compilation, how do I run that generated BEAM file?
1/ {"source files globbed", Options}
Here the options are :
debug_info add debug info for the debugger
{outdir, "/Users/user/projects/custom_test/trunk/ebin"} where should the output be written (the .beam files)
{i, "/Users/user/projects/custom_test/trunk/include/."} where to find the .hrl header files.
2/ erl -make
3/ erl -pa /Users/user/projects/custom_test/trunk/ebin starts a shell.
Find the module serving as an entry point in your application and call the functions :
module:start().
You can also run the code non interactively :
erl -noinput -noshell -pa /Users/user/projects/custom_test/trunk/ebin -s module start
For the Emakefile synax visit the man page
In the directory where the Emakefile is run erl -make to compile using the Emakefile
Simplest way to run would be to simply start an erlang shell in the same directory as the beam files with the command erl. Then run the code with module_name:function_name(). (including the dot).

Resources