UserShortcut.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #include <catch2/catch_test_macros.hpp>
  2. #include <chrono>
  3. #include <filesystem>
  4. #include <fstream>
  5. #include <thread>
  6. #include <unistd.h>
  7. #include "WallpaperEngine/Desktop/UserShortcut.h"
  8. using WallpaperEngine::Desktop::UserShortcut;
  9. namespace {
  10. std::filesystem::path scratchDir () {
  11. auto dir = std::filesystem::temp_directory_path () / ("lwe-shortcut-test-" + std::to_string (getpid ()));
  12. std::filesystem::create_directories (dir);
  13. return dir;
  14. }
  15. bool waitForFile (const std::filesystem::path& file) {
  16. for (int i = 0; i < 100; i++) {
  17. if (std::filesystem::exists (file)) {
  18. return true;
  19. }
  20. std::this_thread::sleep_for (std::chrono::milliseconds (20));
  21. }
  22. return false;
  23. }
  24. } // namespace
  25. TEST_CASE ("User shortcuts parse the real engine's JSON") {
  26. const auto web = UserShortcut::parse (R"({"isbound":true,"commandtype":"web","file":"https://example.org"})");
  27. REQUIRE (web.has_value ());
  28. CHECK (web->type == UserShortcut::Type::Web);
  29. CHECK (web->target == "https://example.org");
  30. const auto command = UserShortcut::parse (R"({"commandtype":"command","file":"echo","arguments":"hi"})");
  31. REQUIRE (command.has_value ());
  32. CHECK (command->type == UserShortcut::Type::Command);
  33. CHECK (command->arguments == "hi");
  34. CHECK_FALSE (UserShortcut::parse (R"({"isbound":false,"commandtype":"web","file":"https://example.org"})"));
  35. CHECK_FALSE (UserShortcut::parse (R"({"commandtype":"unknown","file":"x"})"));
  36. CHECK_FALSE (UserShortcut::parse (""));
  37. CHECK_FALSE (UserShortcut::parse ("{not json"));
  38. }
  39. TEST_CASE ("Plain shortcut values pick their type") {
  40. const auto dir = scratchDir ();
  41. CHECK (UserShortcut::parse ("https://example.org")->type == UserShortcut::Type::Web);
  42. CHECK (UserShortcut::parse (dir.string ())->type == UserShortcut::Type::Directory);
  43. CHECK (UserShortcut::parse ((dir / "notes.txt").string ())->type == UserShortcut::Type::File);
  44. CHECK (UserShortcut::parse ((dir / "app.desktop").string ())->type == UserShortcut::Type::Application);
  45. // a command is never guessed from a plain string
  46. CHECK (UserShortcut::parse ("rm -rf something")->type == UserShortcut::Type::File);
  47. std::filesystem::remove_all (dir);
  48. }
  49. TEST_CASE ("Command shortcuts run through the shell") {
  50. const auto dir = scratchDir ();
  51. const auto marker = dir / "ran";
  52. const auto shortcut = UserShortcut::parse (
  53. R"({"commandtype":"command","file":"touch","arguments":")" + marker.string () + R"("})"
  54. );
  55. REQUIRE (shortcut.has_value ());
  56. CHECK (shortcut->launch ());
  57. CHECK (waitForFile (marker));
  58. std::filesystem::remove_all (dir);
  59. }
  60. TEST_CASE ("Application shortcuts expand the desktop file's Exec line") {
  61. const auto dir = scratchDir ();
  62. const auto marker = dir / "app ran";
  63. const auto desktop = dir / "test.desktop";
  64. std::ofstream (desktop) << "[Desktop Entry]\nType=Application\nName=Test\nExec=touch %U \"" << marker.string ()
  65. << "\"\n";
  66. const auto shortcut = UserShortcut::parse (desktop.string ());
  67. REQUIRE (shortcut.has_value ());
  68. CHECK (shortcut->launch ());
  69. CHECK (waitForFile (marker));
  70. std::filesystem::remove_all (dir);
  71. }
  72. TEST_CASE ("A program that doesn't exist is reported") {
  73. const auto dir = scratchDir ();
  74. const auto desktop = dir / "missing.desktop";
  75. std::ofstream (desktop) << "[Desktop Entry]\nType=Application\nName=Missing\nExec=lwe-no-such-program-here\n";
  76. CHECK_FALSE (UserShortcut::parse (desktop.string ())->launch ());
  77. std::filesystem::remove_all (dir);
  78. }