-module(er_xdg_pipe_menu_watcher_tests). -include_lib("eunit/include/eunit.hrl"). %% Exercises the real debounce-timer-reset-then-invalidate wiring by %% sending the watcher a synthetic message of the exact shape `fs' %% delivers, rather than waiting on a real inotify event -- `fs' %% itself is treated as out of scope for this test. debounced_event_invalidates_cache_test() -> Root = mk_tmp_dir(), AppDir = filename:join(Root, "applications"), ok = file:make_dir(AppDir), write_desktop(AppDir, "foo.desktop", "Foo", "foo"), PrevDebounce = application:get_env(er_xdg_pipe_menu, debounce_ms), application:set_env(er_xdg_pipe_menu, debounce_ms, 20), with_env( [{"XDG_DATA_HOME", Root}, {"XDG_DATA_DIRS", "/does-not-exist-eunit"}], fun() -> {ok, CachePid} = er_xdg_pipe_menu_cache:start_link(), {ok, WatcherPid} = er_xdg_pipe_menu_watcher:start_link(), write_desktop(AppDir, "bar.desktop", "Bar", "bar"), WatcherPid ! {self(), {fs, file_event}, {"dummy", [modified]}}, timer:sleep(100), Menu = er_xdg_pipe_menu_cache:get_menu(), stop(WatcherPid), stop(CachePid), ?assert(binary:match(Menu, <<"Bar">>) =/= nomatch) end ), ok = rm_rf(Root), restore_env(er_xdg_pipe_menu, debounce_ms, PrevDebounce). %% -- helpers ---------------------------------------------------------------- %% start_link/0 links the started process to us (the test process); %% unlink before killing it so the kill signal doesn't propagate back %% and take the test process down with it. stop(Pid) -> unlink(Pid), true = erlang:exit(Pid, kill). restore_env(App, Key, undefined) -> application:unset_env(App, Key); restore_env(App, Key, {ok, Value}) -> application:set_env(App, Key, Value). with_env(Vars, Fun) -> Saved = [{Name, os:getenv(Name)} || {Name, _} <- Vars], try lists:foreach(fun({Name, Value}) -> set_env(Name, Value) end, Vars), Fun() after lists:foreach(fun({Name, Value}) -> set_env(Name, Value) end, Saved) end. set_env(Name, false) -> os:unsetenv(Name); set_env(Name, Value) -> os:putenv(Name, Value). write_desktop(Dir, FileName, Name, Exec) -> ok = file:write_file( filename:join(Dir, FileName), iolist_to_binary(["[Desktop Entry]\nName=", Name, "\nExec=", Exec, "\n"]) ). mk_tmp_dir() -> Path = filename:join( test_tmp_dir(), "eunit-watcher-" ++ integer_to_list(erlang:unique_integer([positive])) ), ok = file:make_dir(Path), Path. test_tmp_dir() -> case os:getenv("TMPDIR") of false -> "/tmp"; Dir -> Dir end. rm_rf(Path) -> case filelib:is_dir(Path) of true -> {ok, Entries} = file:list_dir(Path), lists:foreach(fun(E) -> rm_rf(filename:join(Path, E)) end, Entries), file:del_dir(Path); false -> file:delete(Path) end.