%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%% 
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%% 
%% %CopyrightEnd%
%%

-module(ddll_SUITE).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Checks if the dynamic driver and linker loader works.
%%%
%%% These tests can only be run installed (outside clearcase).
%%%
%%% XXX In this suite is missing test cases for reference counts
%%% and that drivers are unloaded when their processes die.
%%% (For me to add :-)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
	 init_per_group/2,end_per_group/2, ddll_test/1, errors/1,
	 reference_count/1,
	 kill_port/1, dont_kill_port/1]).
-export([unload_on_process_exit/1, delayed_unload_with_ports/1, 
	 unload_due_to_process_exit/1,
	 no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1,
	 unload_reload_thingie/1, unload_reload_thingie_2/1,
	 unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1,
	 load_fail_init/1,
	 reload_pending_fail_init/1,
	 more_error_codes/1, forced_port_killing/1, 
	 no_trap_exit_and_kill_ports/1,
	 monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1, 
	 lock_driver/1]).

% Private exports
-export([echo_loader/2, nice_echo_loader/2 ,properties/1, load_and_unload/1]).

-import(ordsets, [subtract/2]).

-include_lib("test_server/include/test_server.hrl").

suite() -> [{ct_hooks,[ts_install_cth]}].

all() -> 
    [ddll_test, errors, reference_count, kill_port,
     dont_kill_port, properties, load_and_unload,
     unload_on_process_exit, delayed_unload_with_ports,
     unload_due_to_process_exit,
     no_unload_due_to_process_exit,
     no_unload_due_to_process_exit_2, unload_reload_thingie,
     unload_reload_thingie_2, unload_reload_thingie_3,
     reload_pending, load_fail_init,
     reload_pending_fail_init, reload_pending_kill,
     more_error_codes, forced_port_killing,
     no_trap_exit_and_kill_ports, monitor_demonitor,
     monitor_demonitor_load, new_interface, lock_driver].

groups() -> 
    [].

init_per_suite(Config) ->
    Config.

end_per_suite(_Config) ->
    ok.

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


unload_on_process_exit(suite) ->
    [];
unload_on_process_exit(doc) ->
    ["Check that the driver is unloaded on process exit"];
unload_on_process_exit(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
    Parent = self(),
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      erl_ddll:try_load(Path, echo_drv, []),
			      Parent ! gone,
			      receive go -> ok end,
			      erl_ddll:loaded_drivers(),
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
    Pid ! go,
    ?line receive
	gone -> ok
    end,
    ?line true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
    Pid ! go,
    ?line receive
	{'DOWN', Ref, process, Pid, banan} ->
	    ok
    end,
    receive after 500 -> ok end,
    ?line false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())),
    ?line test_server:timetrap_cancel(Dog),
    ok.

delayed_unload_with_ports(suite) ->
    [];
delayed_unload_with_ports(doc) ->
    ["Check that the driver is unloaded when the last port is closed"];
delayed_unload_with_ports(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line erl_ddll:try_load(Path, echo_drv, []),
    ?line erl_ddll:try_load(Path, echo_drv, []),
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    ?line 1 = erl_ddll:info(echo_drv, port_count),
    ?line Port2 = open_port({spawn, echo_drv}, [eof]),
    ?line 2 = erl_ddll:info(echo_drv, port_count),
    ?line {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
    ?line {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]),
    ?line ok = receive _ -> false after 0 -> ok end,
    ?line Port ! {self(), close},
    ?line ok = receive {Port,closed} -> ok after 1000 -> false end,
    ?line 1 = erl_ddll:info(echo_drv, port_count),
    ?line Port2 ! {self(), close},
    ?line ok = receive {Port2,closed} -> ok after 1000 -> false end,
    ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

unload_due_to_process_exit(suite) ->
    [];
unload_due_to_process_exit(doc) ->
    ["Check that the driver with ports is unloaded on process exit"];
unload_due_to_process_exit(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
			      spawn(F3),
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      _Port2 = open_port({spawn, echo_drv}, [eof]),
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

no_unload_due_to_process_exit(suite) ->
    [];
no_unload_due_to_process_exit(doc) ->
    ["Check that a driver with driver loaded in another process is not unloaded on process exit"];
no_unload_due_to_process_exit(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
			      spawn(F3),
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      _Port2 = open_port({spawn, echo_drv}, [eof]),
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line ok = unload_expect_fast(echo_drv,[]),
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

no_unload_due_to_process_exit_2(suite) ->
    [];
no_unload_due_to_process_exit_2(doc) ->
    ["Check that a driver with open ports in another process is not unloaded on process exit"];
no_unload_due_to_process_exit_2(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
			      spawn(F3),
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      _Port2 = open_port({spawn, echo_drv}, [eof]),
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line erlang:port_close(Port),
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

unload_reload_thingie(suite) ->
    [];
unload_reload_thingie(doc) ->
    ["Check delayed unload and reload"];
unload_reload_thingie(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      spawn(F3),
			      receive go -> ok end,
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
    ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}),
    ?line ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end,
    ?line {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end,
    ?line Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line [{Parent,1}] = erl_ddll:info(echo_drv, processes),
    ?line 0 = erl_ddll:info(echo_drv, port_count),
    ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
    ?line ok = receive 
		   {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 
	       after 300 -> error 
	       end,
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

unload_reload_thingie_2(suite) ->
    [];
unload_reload_thingie_2(doc) ->
    ["Check delayed unload and reload"];
unload_reload_thingie_2(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      spawn(F3),
			      receive go -> ok end,
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok,pending_driver,Ref3} = erl_ddll:try_load(Path,echo_drv,[{monitor,pending_driver},{reload,pending_driver}]),
    ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
    ?line Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end,
    ?line ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end,
    ?line [{Parent,1}] = erl_ddll:info(echo_drv, processes),
    ?line 0 = erl_ddll:info(echo_drv, port_count),
    ?line ok = receive 
		   {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 
	       after 300 -> error 
	       end,
    ?line ok = unload_expect_fast(echo_drv,[{monitor,pending}]),
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

unload_reload_thingie_3(suite) ->
    [];
unload_reload_thingie_3(doc) ->
    ["Check delayed unload and reload failure"];
unload_reload_thingie_3(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      spawn(F3),
			      receive go -> ok end,
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]),echo_drv,[{monitor,pending_driver},{reload,pending_driver}]),
    ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
    ?line Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive 
		   {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 
	       after 300 -> error 
	       end,
    ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end,
    ?line ok = receive 
		   {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok 
	       after 1000 -> false 
	       end,
    ?line {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)),
    ?line {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]),
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

reload_pending(suite) -> [];
reload_pending(doc) -> ["Reload a driver that is pending on a user"];
reload_pending(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
			      spawn(F3),
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      _Port2 = open_port({spawn, echo_drv}, [eof]),
			      Parent ! opened,
			      receive go -> ok end,
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    Pid ! go,
    ?line receive opened -> ok end,
    ?line {error, pending_process} = 
	erl_ddll:try_load(Path, echo_drv,
			  [{reload,pending_driver},
			   {monitor,pending_driver}]),
    ?line {ok, pending_process, Ref3} = 
	erl_ddll:try_load(Path, echo_drv,
			  [{reload,pending},
			   {monitor,pending}]),
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive Y -> {error, Y} after 300 -> ok end,
    ?line erlang:port_close(Port),
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
    [{Parent,1}] = erl_ddll:info(echo_drv,processes),
    ?line ok = receive Z -> {error, Z} after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

load_fail_init(suite) -> [];
load_fail_init(doc) -> ["Tests failure in the init in driver struct."];
load_fail_init(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line PathFailing = ?config(priv_dir, Config),
    ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
    ?line lists:foreach(fun(Name) ->
				Src = filename:join([Path,Name]),
				Ext = filename:extension(Name),
				Dst =filename:join([PathFailing,"echo_drv"++Ext]),
				file:delete(Dst),
				{ok,_} = file:copy(Src,Dst)
			end,
			AllFailInits),
    ?line [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
    ?line {error, driver_init_failed} = erl_ddll:try_load(PathFailing, 
							  echo_drv,
							  [{monitor,pending}]),
    ?line ok = receive XX ->
		       {unexpected,XX}
	       after 300 ->
		       ok
	       end,
    ?line test_server:timetrap_cancel(Dog),
    ok.


reload_pending_fail_init(suite) -> [];
reload_pending_fail_init(doc) -> ["Reload a driver that is pending but init fails"];
reload_pending_fail_init(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line PathFailing = ?config(priv_dir, Config),
    ?line [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path),
    ?line lists:foreach(fun(Name) ->
				Src = filename:join([Path,Name]),
				Ext = filename:extension(Name),
				Dst =filename:join([PathFailing,"echo_drv"++Ext]),
				file:delete(Dst),
				{ok,_} = file:copy(Src,Dst)
			end,
			AllFailInits),
    ?line [_|_] = filelib:wildcard("echo_drv.*",PathFailing),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
			      spawn(F3),
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      _Port2 = open_port({spawn, echo_drv}, [eof]),
			      Parent ! opened,
			      receive go -> ok end,
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    Pid ! go,
    ?line receive opened -> ok end,
    ?line {ok, pending_process, Ref3} = 
	erl_ddll:try_load(PathFailing, echo_drv,
			  [{reload,pending},
			   {monitor,pending}]),
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive Y -> {error, Y} after 300 -> ok end,
    ?line erlang:port_close(Port),
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok  after 300 -> error end,
    ?line {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)),

    ?line ok = receive Z -> {error, Z} after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

reload_pending_kill(suite) -> [];
reload_pending_kill(doc) -> ["Reload a driver with kill_ports option "
			     "that is pending on a user"];
reload_pending_kill(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line OldFlag = process_flag(trap_exit,true),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      process_flag(trap_exit,true),
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
			      spawn(F3),
			      receive go -> ok end,
			      Port = open_port({spawn, echo_drv}, [eof]),
			      Port2 = open_port({spawn, echo_drv}, [eof]),
			      Parent ! opened,
			      receive go -> ok end,
			      receive 
				  {'EXIT', Port2, driver_unloaded} ->
				      Parent ! first_exit 
			      end,
			      receive 
				  {'EXIT', Port, driver_unloaded} ->
				      Parent ! second_exit 
			      end,
			      receive go -> ok end,
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]),
    ?line {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    Pid ! go,
    ?line receive opened -> ok end,
    ?line {error, pending_process} = 
	erl_ddll:try_load(Path, echo_drv,
			  [{driver_options,[kill_ports]},
			   {reload,pending_driver},
			   {monitor,pending_driver}]),
    ?line {ok, pending_process, Ref3} = 
	erl_ddll:try_load(Path, echo_drv,
			  [{driver_options,[kill_ports]},
			   {reload,pending},
			   {monitor,pending}]),
    ?line ok =  receive 
		    {'EXIT', Port, driver_unloaded} ->
			ok 
		after 300 -> error
		end,
    Pid ! go,
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end,
    ?line [_,_] = erl_ddll:info(echo_drv,processes),
    ?line ok = receive first_exit -> ok after 300 -> error end,
    ?line ok = receive second_exit -> ok after 300 -> error end,
    ?line 0 = erl_ddll:info(echo_drv,port_count), 
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive Y -> {error, Y} after 300 -> ok end,
    ?line Port2 = open_port({spawn, echo_drv}, [eof]),
    ?line true = is_port(Port2),
    [{Parent,1}] = erl_ddll:info(echo_drv,processes),
    ?line 1 = erl_ddll:info(echo_drv,port_count), 
    ?line erlang:port_close(Port2),
    ?line ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end,
    ?line 0 = erl_ddll:info(echo_drv,port_count), 
    ?line [{Parent,1}] = erl_ddll:info(echo_drv,processes),
    ?line Port3 = open_port({spawn, echo_drv}, [eof]),
    ?line {ok, pending_driver, Ref4} = 
	erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]),
    ?line ok =  receive 
		    {'EXIT', Port3, driver_unloaded} ->
			ok 
		after 300 -> error
		end,
    ?line ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end,
    io:format("Port = ~w, Port2 = ~w, Port3 = ~w~n",[Port,Port2,Port3]),
    ?line ok = receive Z -> {error, Z} after 300 -> ok end,
    ?line process_flag(trap_exit,OldFlag),
    ?line test_server:timetrap_cancel(Dog),
    ok.


more_error_codes(suite) ->
    [];
more_error_codes(doc) ->
    ["Some more error code checking"];
more_error_codes(Config) when is_list(Config) ->
    ?line {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]),
    ?line true = is_list(erl_ddll:format_error(Err)),
    ?line true = is_list(erl_ddll:format_error(not_loaded)),
    ok.

forced_port_killing(suite) ->
    [];
forced_port_killing(doc) ->
    ["Check kill_ports option to try_unload "];
forced_port_killing(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line OldFlag=process_flag(trap_exit,true),
    ?line Parent = self(),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line spawn(F3),
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    ?line Port2 = open_port({spawn, echo_drv}, [eof]),
    ?line {ok, pending_driver, Ref1} = 
	erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]),
    ?line ok = receive 
		   {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 
	       after 300 -> error 
	       end,
    ?line ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end,
    ?line ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end,
    ?line ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end,
    ?line process_flag(trap_exit,OldFlag),
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

no_trap_exit_and_kill_ports(suite) ->
    [];
no_trap_exit_and_kill_ports(doc) ->
    ["Check delayed unload and reload with no trap_exit"];
no_trap_exit_and_kill_ports(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line Parent = self(),
    ?line OldFlag=process_flag(trap_exit,true),
    ?line F3 = fun() -> 
		       Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 
		       receive X -> Parent ! {got,X} end 
	       end,
    ?line Pid = spawn(fun() ->
			      process_flag(trap_exit,false),
			      receive go -> ok end,
			      {ok, loaded} = erl_ddll:try_load(Path, echo_drv, 
							       [{driver_options,[kill_ports]}]),
			      spawn(F3),
			      receive go -> ok end,
			      _Port = open_port({spawn, echo_drv}, [eof]),
			      _Port2 = open_port({spawn, echo_drv}, [eof]),
			      exit(banan)
		      end),
    ?line Ref = erlang:monitor(process,Pid),
    Pid ! go,
    ?line {ok,Ref2} = receive 
			  R when is_reference(R) -> {ok,R}; 
			  Other -> {error, Other} 
		      after 500 -> {error, timeout} 
		      end,
    ?line {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []),
    ?line MyPort = open_port({spawn, echo_drv}, [eof]),
    Pid ! go,
    ?line ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end,
    ?line ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end,
    ?line ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end,
    ?line process_flag(trap_exit,OldFlag),
    ?line test_server:timetrap_cancel(Dog),
    ok.

monitor_demonitor(suite) ->
    [];
monitor_demonitor(doc) ->
    ["Check monitor and demonitor of drivers"];
monitor_demonitor(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line erl_ddll:try_load(Path, echo_drv, []),
    ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
    ?line Self = self(),
    ?line [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload),
    ?line true = erl_ddll:demonitor(Ref),
    ?line [] = erl_ddll:info(echo_drv,awaiting_unload),
    ?line erl_ddll:try_unload(echo_drv,[]),
    ?line ok = receive _ -> error after 300 -> ok end,
    ?line test_server:timetrap_cancel(Dog),
    ok.

monitor_demonitor_load(suite) ->
    [];
monitor_demonitor_load(doc) ->
    ["Check monitor/demonitor of driver loading"];
monitor_demonitor_load(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    ?line Ref = erl_ddll:monitor(driver,{echo_drv,loaded}),
    ?line ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end,
    ?line {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]),
    ?line Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}),
    ?line ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end,
    ?line {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []),
    ?line {ok, pending_driver} = 
	erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]),
    ?line Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}),
    ?line Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
    ?line ok = receive _ -> error after 300 -> ok end,
    ?line Self = self(),
    ?line [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load),
    ?line true = erl_ddll:demonitor(Ref3),
    ?line [] = erl_ddll:info(echo_drv,awaiting_load),
    ?line erlang:port_close(Port),
    ?line ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end,
    ?line ok = receive _ -> error after 300 -> ok end,
    ?line ok = unload_expect_fast(echo_drv,[]),
    ?line test_server:timetrap_cancel(Dog),
    ok.

new_interface(suite) ->
    [];
new_interface(doc) ->
    ["Test the new load/unload/reload interface"];
new_interface(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    % Typical scenario
    ?line ok = erl_ddll:load(Path, echo_drv),
    ?line Port = open_port({spawn, echo_drv}, [eof]),
    ?line ok = erl_ddll:unload(echo_drv),
    ?line Port ! {self(), {command, "text"}},
    ?line ok = receive 
		   {Port, {data, "text"}} -> ok;
		   _ -> error
	       after 
		   1000 -> error
	       end,
    ?line Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}),
    ?line ok = receive X -> {error, X} after 300 -> ok end,
    ?line erlang:port_close(Port),
    ?line ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end,
    % More than one user
    ?line ok = erl_ddll:load(Path, echo_drv),
    ?line Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}),
    ?line ok = erl_ddll:load(Path, echo_drv),
    ?line ok = erl_ddll:load(Path, echo_drv),
    ?line Port2 = open_port({spawn, echo_drv}, [eof]),
    ?line ok = erl_ddll:unload(echo_drv),
    ?line Port2 ! {self(), {command, "text"}},
    ?line ok = receive 
		   {Port2, {data, "text"}} -> ok;
		   _ -> error
	       after 
		   1000 -> error
	       end,
    ?line ok = erl_ddll:unload(echo_drv),
    ?line Port2 ! {self(), {command, "text"}},
    ?line ok = receive 
		   {Port2, {data, "text"}} -> ok;
		   _ -> error
	       after 
		   1000 -> error
	       end,
    ?line ok = erl_ddll:unload(echo_drv),
    ?line Port2 ! {self(), {command, "text"}},
    ?line ok = receive 
		   {Port2, {data, "text"}} -> ok;
		   _ -> error
	       after 
		   1000 -> error
	       end,
    ?line ok = receive X2 -> {error, X2} after 300 -> ok end,
    ?line ok = erl_ddll:load(Path, echo_drv),
    ?line ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end,
    ?line Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}),
    ?line erlang:port_close(Port2),
    ?line ok = receive X3 -> {error, X3} after 300 -> ok end,
    ?line ok = erl_ddll:unload(echo_drv),
    ?line ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end,
    ?line test_server:timetrap_cancel(Dog),
    ok.
    
    


ddll_test(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),

    %?line {error,{already_started,ErlDdllPid}} = erl_ddll:start(),
    %?line ErlDdllPid = whereis(ddll_server),

    %% Load the echo driver and verify that it was loaded.
    {ok,L1,L2}=load_echo_driver(Path),

    %% Verify that the driver works.

     ?line Port = open_port({spawn, echo_drv}, [eof]),
     ?line {hej, "hopp",4711,123445567436543653} = 
 	erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
     ?line {hej, "hopp",4711,123445567436543653} = 
 	erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}),
     ?line Port ! {self(), {command, "text"}},
     ?line 1 = receive 
 		  {Port, {data, "text"}} -> 1;
 		  _Other -> 2
 	      after 
 		  1000 -> 2
 	      end,
     ?line Port ! {self(), close},
     ?line receive {Port, closed} -> ok end,

%%     %% Unload the driver and verify that it was unloaded.
     ok = unload_echo_driver(L1,L2),

%%     %?line {error, {already_started, _}} = erl_ddll:start(),

    ?line test_server:timetrap_cancel(Dog),
    ok.

%% Tests errors having to do with bad drivers.

errors(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),

    ?line {ok, L1} = erl_ddll:loaded_drivers(),

    ?line {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name),
    ?line {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv),
    ?line {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv),

    %% We assume that there is a statically linked driver named "ddll":
    ?line {error, linked_in_driver} = erl_ddll:unload_driver(efile),
    ?line {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"),
	
    case os:type() of
	{unix, _} ->
	    ?line {error, no_driver_init} =
		erl_ddll:load_driver(Path, noinit_drv);
	_ ->
	    ok
    end,

    ?line {ok, L1} = erl_ddll:loaded_drivers(),

    ?line test_server:timetrap_cancel(Dog),
    ok.

reference_count(doc) ->
    ["Check that drivers are unloaded when their reference count ",
     "reaches zero, and that they cannot be unloaded while ",
     "they are still referenced."];
reference_count(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),

    %% Spawn a process that loads the driver (and holds a reference
    %% to it).
    Pid1=spawn_link(?MODULE, echo_loader, [Path, self()]),
    receive
	{Pid1, echo_loaded} -> ok
    after 2000 -> test_server:fail("echo_loader failed to start.")
    end,

    Pid1 ! {self(), die},
    ?line test_server:sleep(200),   % Give time to unload.
    % Verify that the driver was automaticly unloaded when the
    % process died.
    ?line {error, not_loaded}=erl_ddll:unload_driver(echo_drv),
    
    ?line test_server:timetrap_cancel(Dog),
    ok.

% Loads the echo driver, send msg to started, sits and waits to
% get a signal to die, then unloads the driver and terminates.
echo_loader(Path, Starter) ->
    ?line {ok, L1, L2}=load_echo_driver(Path),
    ?line Starter ! {self(), echo_loaded},
    receive
	{Starter, die} ->
	    ?line unload_echo_driver(L1,L2)
    end.

% Loads the echo driver, send msg to started, sits and waits to
% get a signal to die, then unloads the driver and terminates.
nice_echo_loader(Path, Starter) ->
    ?line {ok, L1, L2}=load_nice_echo_driver(Path),
    ?line Starter ! {self(), echo_loaded},
    receive
	{Starter, die} ->
	    ?line unload_echo_driver(L1,L2)
    end.


kill_port(doc) ->
    ["Test that a port that uses a driver is killed when the ",
     "process that loaded the driver dies."];
kill_port(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),

    %% Spawn a process that loads the driver (and holds a reference
    %% to it).
    ?line Pid1=spawn(?MODULE, echo_loader, [Path, self()]),
    ?line receive
	      {Pid1, echo_loaded} ->
		  ok
	  after 3000 ->
		  ?line exit(Pid1, kill),
		  ?line test_server:fail("echo_loader failed to start.")
	  end,

    % Spawn off a port that uses the driver.
    ?line Port = open_port({spawn, echo_drv}, [eof]),

    % Kill the process / unload the driver.
    ?line process_flag(trap_exit, true),
    ?line exit(Pid1, kill),
    ?line test_server:sleep(200),    % Give some time to unload.
    ?line {error, not_loaded} = erl_ddll:unload_driver(echo_drv),

    % See if the port is killed.
    receive
	{'EXIT', Port, Reason} ->
	    io:format("Port exited with reason ~w", [Reason])
    after 5000 ->
	    ?line test_server:fail("Echo port did not terminate.")
    end,

    %% Cleanup and exit.
    ?line test_server:timetrap_cancel(Dog),
    ok.

dont_kill_port(doc) ->
    ["Test that a port that uses a driver is not killed when the ",
     "process that loaded the driver dies and it's nicely opened."];
dont_kill_port(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),

    %% Spawn a process that loads the driver (and holds a reference
    %% to it).
    ?line Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]),
    ?line receive
	      {Pid1, echo_loaded} ->
		  ok
	  after 3000 ->
		  ?line exit(Pid1, kill),
		  ?line test_server:fail("echo_loader failed to start.")
	  end,

    % Spawn off a port that uses the driver.
    ?line Port = open_port({spawn, echo_drv}, [eof]),

    % Kill the process / unload the driver.
    ?line process_flag(trap_exit, true),
    ?line exit(Pid1, kill),
    ?line test_server:sleep(200),    % Give some time to unload.
    ?line {hej, "hopp",4711,123445567436543653} = 
	erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}),
    ?line [] = erl_ddll:info(echo_drv,processes),
    %% unload should work with no owner
    ?line ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it

    % See if the port is killed.
    receive
	{'EXIT', Port, Reason} ->
	    io:format("Port exited with reason ~w", [Reason])
    after 5000 ->
	    ?line test_server:fail("Echo port did not terminate.")
    end,

    %% Cleanup and exit.
    ?line test_server:timetrap_cancel(Dog),
    ok.

properties(doc) -> ["Test that a process that loaded a driver ",
		    "is the only process that can unload it."];
properties(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),

    % Let another process load the echo driver.
    Pid=spawn_link(?MODULE, echo_loader, [Path, self()]),
    receive
	{Pid, echo_loaded} -> ok
    after 2000 -> test_server:fail("echo_loader failed to start.")
    end,

    % Try to unload the driver from this process (the wrong one).
    ?line {error, _} = erl_ddll:unload_driver(echo_drv),
    ?line {ok, Drivers} = erl_ddll:loaded_drivers(),
    ?line case lists:member("echo_drv", Drivers) of
	      true ->
		  ok;
	      false ->
		  test_server:fail("Unload from wrong process "
				   "succeeded.")
	  end,

    % Unload the driver and terminate dummy process.
    ?line Pid ! {self(), die},
    ?line test_server:sleep(200),   % Give time to unload.
    ?line test_server:timetrap_cancel(Dog),
    ok.

load_and_unload(doc) -> ["Load two drivers and unload them in load order."];
load_and_unload(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(60)),
    ?line Path = ?config(data_dir, Config),
    ?line {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(),
    ?line ok = erl_ddll:load_driver(Path, echo_drv),
    ?line ok = erl_ddll:load_driver(Path, dummy_drv),
    ?line ok = erl_ddll:unload_driver(echo_drv),
    ?line ok = erl_ddll:unload_driver(dummy_drv),
    ?line {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(),
    ?line Set1 = ordsets:from_list(Loaded_drivers1),
    ?line Set2 = ordsets:from_list(Loaded_drivers2),
    ?line io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]),
    ?line [] = ordsets:to_list(ordsets:subtract(Set2, Set1)),

    ?line test_server:timetrap_cancel(Dog),
    ok.    

lock_driver(suite) ->
    [];
lock_driver(doc) ->
    ["Check multiple calls to driver_lock_driver"];
lock_driver(Config) when is_list(Config) ->
    ?line Dog = test_server:timetrap(test_server:seconds(10)),
    ?line Path = ?config(data_dir, Config),
    ?line {ok, _} = erl_ddll:try_load(Path, lock_drv, []),
    ?line Port1 = open_port({spawn, lock_drv}, [eof]),
    ?line Port2 = open_port({spawn, lock_drv}, [eof]),
    ?line true = erl_ddll:info(lock_drv,permanent),
    ?line erlang:port_close(Port1),
    ?line erlang:port_close(Port2),
    ?line test_server:timetrap_cancel(Dog),
    ok.
    

% Load and unload the echo_drv driver.
% Make sure that the driver doesn't exist before we load it,
% and that it exists before we unload it.
load_echo_driver(Path) ->
    ?line {ok, L1} = erl_ddll:loaded_drivers(),
    ?line ok = erl_ddll:load_driver(Path, echo_drv),
    ?line {ok, L2} = erl_ddll:loaded_drivers(),
    ?line ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), 
						  ordsets:from_list(L1))),
    {ok,L1,L2}.

load_nice_echo_driver(Path) ->
    ?line {ok, L1} = erl_ddll:loaded_drivers(),
    ?line ok = erl_ddll:load(Path, echo_drv),
    ?line {ok, L2} = erl_ddll:loaded_drivers(),
    ?line ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), 
						  ordsets:from_list(L1))),
    {ok,L1,L2}.

unload_echo_driver(L1,L2) ->
    ?line {ok, L2} = erl_ddll:loaded_drivers(),
    ?line ok = erl_ddll:unload_driver(echo_drv),
    ?line {ok, L3} = erl_ddll:loaded_drivers(),
    ?line [] = ordsets:to_list(subtract(ordsets:from_list(L3),
					ordsets:from_list(L1))),
    ok.

unload_expect_fast(Driver,XFlags) ->
    {ok, pending_driver, Ref} = 
	erl_ddll:try_unload(Driver,
			    [{monitor,pending_driver}]++XFlags),
    receive
	{'DOWN', Ref, driver, Driver, unloaded} ->
	    case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of
		true ->
		    {error, {still_there, Driver}};
		false ->
		    ok
	    end
    after 1000 ->
	    {error,{unable_to_unload, Driver}}
    end.