Why does EUnit compile .beam files into .eunit/ebin?

643 views Asked by At

Disclaimer: The author of this question has mostly theoretical knowledge of Erlang/OTP.

I have a small OTP application which calls some non-Erlang executable inside the myapp/ebin directory via open_port(). When I run the application itself everything goes well and the port to the executable is successfully opened.

But when I try to run unit tests for the application, the ones that depend on the open_port() fail, because when started with EUnit the application tries to find the executable under myapp/.eunit/ebin.

How can I change that behaviour without changing the code of the application itself? How can I run EUnit tests with the same current directory as when running the application itself? (I mean it would not be a good idea to change the code which provides the path to the executable just to be able to run EUnit).

Edit: I followed the advice in the Erlang mailing list, but code:priv_dir(myapp_name) returns {error, bad_name}.

Edit: I can see that .eunit/ contains modulename.beam files and ebin/ contains both modulename.beam files and modulename_tests.beam files. Now I am completely lost. When I run make test, rebar runs eunit command, which calls each modulename_tests.beam file in the ebin/ directory which calls a corresponding modulename.beam file in the .eunit/ directory (filename:absname("") clearly shows that modulename.beam files are executed from .eunit/ during test). Why is it so? Why do we need to run modulename.beam files from the .eunit/ directory instead of ebin/?

Why do we actually need to have the very same .beam files in myapp/ebin and myapp/.eunit/ebin?

P.S. I have read the official documentation and did not find the solution.

2

There are 2 answers

2
Chen Yu On

To use erlang start script ".erlang" , and it can solve your problem.

In the .erlang file, to use code:add_pathz/N to add your necessary path.

Before reading couchdb source code there is example of how to use priv directory. Maybe the solution is helpful to you. It wrap open_port with start_port function, and set the directory in start_port function.

In file couch_os_daemon.erl

start_port(Command) ->
    PrivDir = couch_util:priv_dir(),
    Spawnkiller = filename:join(PrivDir, "couchspawnkillable"),
    Port = open_port({spawn, Spawnkiller ++ " " ++ Command}, ?PORT_OPTIONS),
    {ok, Port}.


stop_port(#daemon{port=Port, kill=undefined}=D) ->
    ?LOG_ERROR("Stopping daemon without a kill command: ~p", [D#daemon.name]),
    catch port_close(Port);
stop_port(#daemon{port=Port}=D) ->
    ?LOG_DEBUG("Stopping daemon: ~p", [D#daemon.name]),
    os:cmd(D#daemon.kill),
    catch port_close(Port).

In file couch_util.erl

priv_dir() ->
    case code:priv_dir(couch) of
        {error, bad_name} ->
            % small hack, in dev mode "app" is couchdb. Fixing requires
            % renaming src/couch to src/couch. Not really worth the hassle.
            % -Damien
            code:priv_dir(couchdb);
        Dir -> Dir
    end.

start_driver(LibDir) ->
    case erl_ddll:load_driver(LibDir, "couch_icu_driver") of
    ok ->
        ok;
    {error, already_loaded} ->
        ok = erl_ddll:reload_driver(LibDir, "couch_icu_driver");
    {error, Error} ->
        exit(erl_ddll:format_error(Error))
    end.

You can grep priv, many example can be found.

2
RichardC On

EUnit does not do this by itself - the .eunit directory is a convention used by Rebar.