LCOV - code coverage report
Current view: top level - src/file - share.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 107 108 99.1 %
Date: 2022-06-15 20:16:21 Functions: 10 11 90.9 %
Branches: 83 132 62.9 %

           Branch data     Line data    Source code
       1                 :            : #ifdef RUN_TESTS
       2                 :            : # include <gtest/gtest.h>
       3                 :            : #endif
       4                 :            : 
       5                 :            : /** @file file/share.cc
       6                 :            :  * @brief Facilities for finding share-files (files supplied with program)
       7                 :            :  * and cache-files (files generated by program).
       8                 :            :  */
       9                 :            : 
      10                 :            : #include <filesystem>
      11                 :            : #include <optional>
      12                 :            : #include <fstream>
      13                 :            : #include <string_view>
      14                 :            : #include <string>
      15                 :            : #include <vector>
      16                 :            : 
      17                 :            : #include <unistd.h> // For getuid()
      18                 :            : 
      19                 :            : #include "share.hh"
      20                 :            : 
      21                 :            : using std::filesystem::path;
      22                 :            : 
      23                 :            : static const char* arg0 = nullptr;
      24                 :            : static path arg0_path;
      25                 :            : 
      26                 :          3 : void SaveArg0(const char* a)
      27                 :            : {
      28                 :          3 :     arg0 = a;
      29                 :            : 
      30                 :          3 :     path p = arg0;
      31   [ +  -  +  - ]:          9 :     arg0_path = std::filesystem::canonical(p).parent_path();
      32                 :          3 : }
      33                 :            : 
      34                 :            : /*
      35                 :            : std::pair<path, std::filesystem::file_status>
      36                 :            :     FindShareFile(std::string_view file_to_find, std::initializer_list<std::string_view> extra_paths)
      37                 :            : {
      38                 :            :     return FindShareFile( std::filesystem::path(file_to_find), std::move(extra_paths));
      39                 :            : }
      40                 :            : */
      41                 :            : 
      42                 :            : template<typename T, typename V>
      43                 :         93 : static std::pair<path, std::filesystem::file_status> FindFileCommon(
      44                 :            :     const std::filesystem::path& file_to_find,
      45                 :            :     V&& components,
      46                 :            :     T&& try_path_fun)
      47                 :            : {
      48                 :         93 :     path first;
      49                 :         93 :     std::vector<path> specifics;
      50                 :         93 :     bool has_first = false;
      51         [ +  + ]:        606 :     for(auto s: components)
      52                 :            :     {
      53         [ +  + ]:        601 :         if(!has_first)
      54                 :            :         {
      55         [ +  - ]:        137 :             first = s;
      56                 :            :             has_first = true;
      57                 :            :         }
      58         [ +  + ]:        464 :         else if(!s.empty())
      59         [ +  - ]:        327 :             specifics.emplace_back(s);
      60                 :            :         else
      61                 :            :         {
      62         [ +  + ]:        137 :             if(!first.empty())
      63                 :            :             {
      64                 :            :                 try {
      65   [ +  -  +  -  :        246 :                     auto temp = try_path_fun(first, specifics);
                   +  + ]
      66         [ +  + ]:        123 :                     if(temp.has_value())
      67                 :            :                     {
      68         [ +  - ]:         88 :                         return std::move(temp).value();
      69                 :            :                     }
      70         [ -  - ]:        123 :                 } catch(...)
      71                 :            :                 {
      72                 :            :                 }
      73                 :            :             }
      74                 :         49 :             has_first = false;
      75                 :         49 :             specifics.clear();
      76                 :            :         }
      77                 :            :     }
      78         [ +  - ]:         10 :     return { file_to_find, std::filesystem::status(file_to_find) };
      79                 :        186 : }
      80                 :            : 
      81                 :            : std::pair<path, std::filesystem::file_status>
      82                 :         73 :     FindShareFile(const std::filesystem::path& file_to_find,
      83                 :            :                   std::initializer_list<std::string_view> extra_paths)
      84                 :            : {
      85                 :         73 :     std::vector<std::pair<path, std::vector<path>>> tries;
      86                 :            : 
      87                 :         73 :     const char* file    = file_to_find.c_str();
      88         [ +  + ]:         73 :     const char* homedir = std::getenv("HOME"); if(!homedir) homedir="";
      89         [ +  + ]:         73 :     const char* user    = std::getenv("USER"); if(!user)    user="";
      90                 :         73 :     const char* name    = "that_terminal";
      91         [ +  - ]:         73 :     std::vector<std::string_view> components
      92                 :            :     {
      93                 :            :         arg0_path.c_str(), "share",              file, "",
      94                 :            :         homedir,           ".local/share", name, file, "",
      95                 :            :         "/home", user,     ".local/share", name, file, "",
      96                 :            :         "/usr/local/share",                name, file, "",
      97                 :            :         "/usr/share",                      name, file, "",
      98         [ +  - ]:         73 :     };
      99         [ +  + ]:         86 :     for(auto s: extra_paths)
     100         [ +  - ]:         13 :         components.insert(components.end(), std::initializer_list<std::string_view>{s, file, ""});
     101                 :            : 
     102                 :         73 :     using o = std::optional<std::pair<path, std::filesystem::file_status>>;
     103                 :         73 :     return FindFileCommon(file_to_find, std::move(components),
     104                 :         95 :         [](path test, const std::vector<path>& com) -> o
     105                 :            :         {
     106                 :            :             /* Append all directory components */
     107         [ +  + ]:        292 :             for(auto i = com.begin(); i != com.end(); ++i)
     108                 :        197 :                  test /= *i;
     109                 :            : 
     110                 :         95 :             auto status = std::filesystem::status(test);
     111         [ -  + ]:        122 :             if(std::filesystem::exists(status))
     112                 :         68 :                 return o{ std::in_place_t{}, std::move(test), std::move(status) };
     113                 :         27 :             return {};
     114         [ +  - ]:        146 :         });
     115                 :         73 : }
     116                 :            : 
     117                 :            : std::pair<path, std::filesystem::file_status>
     118                 :         20 :     FindCacheFile(const std::filesystem::path& file_to_find, bool is_file)
     119                 :            : {
     120                 :         20 :     const char* file    = file_to_find.c_str();
     121         [ +  + ]:         20 :     const char* homedir = std::getenv("HOME"); if(!homedir) homedir="";
     122         [ +  + ]:         20 :     const char* user    = std::getenv("USER"); if(!user)    user=".";
     123                 :         20 :     const char* name    = "that_terminal";
     124                 :         20 :     std::string uid = std::to_string(getuid());
     125   [ +  -  +  -  :         40 :     std::string pu  = name + std::string("-") + uid;
                   +  - ]
     126         [ +  - ]:         20 :     auto temp       = std::filesystem::temp_directory_path();
     127                 :            : 
     128         [ +  - ]:         20 :     std::array<std::string_view,23> components
     129                 :            :     {
     130                 :            :         homedir,           ".cache", name, file, "",
     131                 :            :         "/home", user,     ".cache", name, file, "",
     132                 :            :         "/run/user",        uid.c_str(),   file, "",
     133                 :            :         "/run",             uid.c_str(),   file, "",
     134                 :            :         temp.c_str(),       pu.c_str(),    file, ""
     135         [ +  - ]:         20 :     };
     136                 :            : 
     137                 :         20 :     using o = std::optional<std::pair<path, std::filesystem::file_status>>;
     138                 :         20 :     return FindFileCommon(file_to_find, std::move(components),
     139                 :        136 :         [is_file](path test, const std::vector<path>& com) -> o
     140                 :            :         {
     141                 :         28 :             std::error_code err;
     142                 :            :             /* Append all directory components */
     143         [ +  + ]:        116 :             for(auto i = com.begin(); i != com.end(); ++i)
     144   [ +  +  +  + ]:         88 :                 if(!is_file || std::next(i) != com.end())
     145                 :         71 :                     test /= *i;
     146                 :            : 
     147                 :         28 :             std::filesystem::create_directories(test, err);
     148                 :         28 :             auto status = std::filesystem::status(test);
     149         [ +  + ]:         28 :             if(!err && std::filesystem::exists(status))
     150                 :            :             {
     151   [ +  +  -  + ]:         20 :                 if(!is_file && std::filesystem::is_directory(status))
     152                 :          6 :                     return o{ std::in_place_t{}, std::move(test), std::move(status) };
     153         [ +  - ]:         14 :                 if(is_file)
     154                 :            :                 {
     155                 :            :                     /* Test if the file exists in this directory */
     156                 :         14 :                     auto test2 = test / *std::next(com.begin(), com.size()-1);
     157         [ +  - ]:         14 :                     status = std::filesystem::status(test2);
     158   [ +  -  +  + ]:         28 :                     if(std::filesystem::exists(status) && !std::filesystem::is_directory(status))
     159                 :          6 :                         return o{ std::in_place_t{}, std::move(test2), std::move(status) };
     160                 :            : 
     161                 :            :                     /* Test if the file could be created in this directory */
     162         [ +  - ]:          8 :                     std::ofstream testfile(test2);
     163         [ +  - ]:          8 :                     auto status2 = std::filesystem::status(test2);
     164                 :            :                     /* Return the old status, before it existed */
     165         [ +  - ]:          8 :                     if(std::filesystem::exists(status2))
     166                 :          8 :                         return o{ std::in_place_t{}, std::move(test2), std::move(status) };
     167                 :         22 :                 }
     168                 :            :             }
     169                 :         28 :             return {};
     170         [ +  - ]:         20 :         });
     171                 :         20 : }
     172                 :            : 
     173                 :            : #undef try_path
     174                 :            : 
     175                 :            : #ifdef RUN_TESTS
     176                 :          3 : static void RunTests()
     177                 :            : {
     178                 :            :     // Reconstruct a plausible argv[0].
     179                 :          3 :     char Buf[4096]{};
     180                 :          3 :     getcwd(Buf, sizeof(Buf)-1);
     181                 :          3 :     strcat(Buf, "/test");
     182                 :          3 :     SaveArg0(Buf);
     183                 :            : 
     184                 :            :     // These tests do not actually test anything,
     185                 :            :     // except that the program does not crash.
     186                 :            :     // They exist for coverage purposes.
     187                 :            : 
     188         [ +  - ]:          6 :     FindShareFile("unicode/UnicodeData.txt", {"/usr/local/share", "/usr/share"});
     189         [ +  - ]:          6 :     FindShareFile("notfound.txt", {"/abc"});
     190         [ +  - ]:          6 :     FindShareFile("notfound.txt", {});
     191         [ +  - ]:          6 :     FindCacheFile("deleteme.dat", false);
     192         [ +  - ]:          6 :     FindCacheFile("similarities.dat", true);
     193         [ +  - ]:          6 :     FindCacheFile("similarities.dat", false);
     194                 :            : 
     195   [ +  -  -  + ]:          6 :     {auto [path, status] = FindCacheFile("deleteme.dat", true);
     196                 :          3 :     if(std::filesystem::exists(status)) //LCOV_EXCL_BR_LINE
     197                 :            :     {
     198         [ +  - ]:          3 :         std::filesystem::remove(path);
     199                 :          0 :     }}
     200   [ +  -  +  - ]:          6 :     {auto [path, status] = FindCacheFile("similarities.dat", true);
     201                 :          3 :     if(std::filesystem::exists(status)) //LCOV_EXCL_BR_LINE
     202                 :            :     {
     203         [ +  - ]:          3 :         std::filesystem::remove(path);
     204                 :          3 :     }}
     205                 :          3 : }
     206                 :          3 : TEST(fileshare, sharetest)
     207                 :            : {
     208                 :          1 :     RunTests();
     209   [ +  -  +  -  :          1 :     static std::string home = std::string("HOME=") + getenv("HOME");
             +  -  +  - ]
     210   [ +  -  +  -  :          1 :     static std::string user = std::string("USER=") + getenv("USER");
             +  -  +  - ]
     211                 :          1 :     putenv(const_cast<char*>("HOME")); RunTests();
     212                 :          1 :     putenv(const_cast<char*>("USER")); RunTests();
     213                 :          1 :     putenv(const_cast<char*>(home.c_str()));
     214                 :          1 :     putenv(const_cast<char*>(user.c_str()));
     215                 :          1 : }
     216                 :            : #endif

Generated by: LCOV version 1.16