From 13491ac0c68e24e6aeb5d47e8b89777979ad6320 Mon Sep 17 00:00:00 2001 From: "Christopher R. Nelson" Date: Mon, 20 Jul 2026 22:48:29 -0400 Subject: Add .desktop file parser Parses the [Desktop Entry] group of a .desktop file into a #desktop_entry{} record with name, exec, icon, and a derived hidden flag (set by NoDisplay, Hidden, or a non-Application Type). Exec field codes (%f, %u, etc.) are stripped since a pipemenu has no file/URI context to expand them with. Co-Authored-By: Claude Sonnet 5 --- test/er_xdg_pipe_menu_desktop_entry_tests.erl | 158 ++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 test/er_xdg_pipe_menu_desktop_entry_tests.erl (limited to 'test') diff --git a/test/er_xdg_pipe_menu_desktop_entry_tests.erl b/test/er_xdg_pipe_menu_desktop_entry_tests.erl new file mode 100644 index 0000000..399e3a5 --- /dev/null +++ b/test/er_xdg_pipe_menu_desktop_entry_tests.erl @@ -0,0 +1,158 @@ +-module(er_xdg_pipe_menu_desktop_entry_tests). + +-include_lib("eunit/include/eunit.hrl"). +-include("er_xdg_pipe_menu.hrl"). + +basic_entry_test() -> + Bin = << + "[Desktop Entry]\n" + "Type=Application\n" + "Name=Firefox\n" + "Exec=firefox %u\n" + "Icon=firefox\n" + >>, + ?assertEqual( + {ok, #desktop_entry{ + name = <<"Firefox">>, exec = <<"firefox">>, icon = <<"firefox">>, hidden = false + }}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +icon_is_optional_test() -> + Bin = << + "[Desktop Entry]\n" + "Name=Foo\n" + "Exec=foo\n" + >>, + ?assertEqual( + {ok, #desktop_entry{name = <<"Foo">>, exec = <<"foo">>, icon = undefined, hidden = false}}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +missing_name_test() -> + Bin = <<"[Desktop Entry]\nExec=foo\n">>, + ?assertEqual({error, missing_name}, er_xdg_pipe_menu_desktop_entry:parse(Bin)). + +missing_exec_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\n">>, + ?assertEqual({error, missing_exec}, er_xdg_pipe_menu_desktop_entry:parse(Bin)). + +ignores_other_groups_test() -> + Bin = << + "[Desktop Entry]\n" + "Name=Foo\n" + "Exec=foo\n" + "\n" + "[Desktop Action new-window]\n" + "Name=New Window\n" + "Exec=foo --new-window\n" + >>, + ?assertEqual( + {ok, #desktop_entry{name = <<"Foo">>, exec = <<"foo">>, icon = undefined, hidden = false}}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +ignores_comments_and_blank_lines_test() -> + Bin = << + "# a comment\n" + "\n" + "[Desktop Entry]\n" + "# another comment\n" + "Name=Foo\n" + "\n" + "Exec=foo\n" + >>, + ?assertEqual( + {ok, #desktop_entry{name = <<"Foo">>, exec = <<"foo">>, icon = undefined, hidden = false}}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +strips_multiple_field_codes_test() -> + Bin = << + "[Desktop Entry]\n" + "Name=Foo\n" + "Exec=foo %f --flag %i %c\n" + >>, + ?assertEqual( + {ok, #desktop_entry{name = <<"Foo">>, exec = <<"foo --flag">>, icon = undefined, hidden = false}}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +literal_percent_is_unescaped_test() -> + Bin = << + "[Desktop Entry]\n" + "Name=Foo\n" + "Exec=foo --progress=%%\n" + >>, + ?assertEqual( + {ok, #desktop_entry{ + name = <<"Foo">>, exec = <<"foo --progress=%">>, icon = undefined, hidden = false + }}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +unescapes_value_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\\sBar\nExec=foo\n">>, + ?assertEqual( + {ok, #desktop_entry{name = <<"Foo Bar">>, exec = <<"foo">>, icon = undefined, hidden = false}}, + er_xdg_pipe_menu_desktop_entry:parse(Bin) + ). + +no_display_is_hidden_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\nExec=foo\nNoDisplay=true\n">>, + {ok, Entry} = er_xdg_pipe_menu_desktop_entry:parse(Bin), + ?assertEqual(true, Entry#desktop_entry.hidden). + +hidden_key_is_hidden_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\nExec=foo\nHidden=true\n">>, + {ok, Entry} = er_xdg_pipe_menu_desktop_entry:parse(Bin), + ?assertEqual(true, Entry#desktop_entry.hidden). + +non_application_type_is_hidden_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\nExec=foo\nType=Link\n">>, + {ok, Entry} = er_xdg_pipe_menu_desktop_entry:parse(Bin), + ?assertEqual(true, Entry#desktop_entry.hidden). + +application_type_is_not_hidden_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\nExec=foo\nType=Application\n">>, + {ok, Entry} = er_xdg_pipe_menu_desktop_entry:parse(Bin), + ?assertEqual(false, Entry#desktop_entry.hidden). + +missing_type_is_not_hidden_test() -> + Bin = <<"[Desktop Entry]\nName=Foo\nExec=foo\n">>, + {ok, Entry} = er_xdg_pipe_menu_desktop_entry:parse(Bin), + ?assertEqual(false, Entry#desktop_entry.hidden). + +false_values_are_not_hidden_test() -> + Bin = << + "[Desktop Entry]\n" + "Name=Foo\n" + "Exec=foo\n" + "NoDisplay=false\n" + "Hidden=false\n" + "Type=Application\n" + >>, + {ok, Entry} = er_xdg_pipe_menu_desktop_entry:parse(Bin), + ?assertEqual(false, Entry#desktop_entry.hidden). + +parse_file_test() -> + Path = filename:join(test_tmp_dir(), "eunit-desktop-entry-test.desktop"), + ok = file:write_file(Path, <<"[Desktop Entry]\nName=Foo\nExec=foo\n">>), + Result = er_xdg_pipe_menu_desktop_entry:parse_file(Path), + ok = file:delete(Path), + ?assertEqual( + {ok, #desktop_entry{name = <<"Foo">>, exec = <<"foo">>, icon = undefined, hidden = false}}, + Result + ). + +parse_file_missing_test() -> + ?assertEqual( + {error, enoent}, + er_xdg_pipe_menu_desktop_entry:parse_file("/nonexistent/does-not-exist.desktop") + ). + +test_tmp_dir() -> + case os:getenv("TMPDIR") of + false -> "/tmp"; + Dir -> Dir + end. -- cgit v1.2.3