Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <glob.h>
     18 
     19 #include <dirent.h>
     20 #include <gtest/gtest.h>
     21 
     22 #include <string>
     23 #include <vector>
     24 
     25 #include "TemporaryFile.h"
     26 
     27 #if defined(__BIONIC__)
     28 #define ASSERT_MATCH_COUNT(n_,g_) ASSERT_EQ(n_, g_.gl_matchc)
     29 #else
     30 #define ASSERT_MATCH_COUNT(n_,g_)
     31 #endif
     32 
     33 //
     34 // Helper for use with GLOB_ALTDIRFUNC to iterate over the elements of `fake_dir`.
     35 //
     36 
     37 static std::vector<std::string> fake_dir;
     38 static size_t fake_dir_offset;
     39 static void fake_closedir(void*) {
     40 }
     41 static dirent* fake_readdir(void*) {
     42   static dirent d;
     43   if (fake_dir_offset >= fake_dir.size()) return nullptr;
     44   strcpy(d.d_name, fake_dir[fake_dir_offset++].c_str());
     45   return &d;
     46 }
     47 static void* fake_opendir(const char* path) {
     48   fake_dir_offset = 0;
     49   if (strcmp(path, "/opendir-fail/") == 0) {
     50     errno = EINVAL;
     51     return nullptr;
     52   }
     53   return &fake_dir;
     54 }
     55 static int fake_lstat(const char*, struct stat*) {
     56   return 0;
     57 }
     58 static int fake_stat(const char*, struct stat*) {
     59   return 0;
     60 }
     61 static void InstallFake(glob_t* g) {
     62   g->gl_closedir = fake_closedir;
     63   g->gl_readdir = fake_readdir;
     64   g->gl_opendir = fake_opendir;
     65   g->gl_lstat = fake_lstat;
     66   g->gl_stat = fake_stat;
     67 }
     68 
     69 TEST(glob, glob_result_GLOB_NOMATCH) {
     70   glob_t g = {};
     71   ASSERT_EQ(GLOB_NOMATCH, glob("/will/match/nothing", 0, nullptr, &g));
     72   ASSERT_EQ(0U, g.gl_pathc);
     73   ASSERT_MATCH_COUNT(0U, g);
     74 }
     75 
     76 TEST(glob, glob_GLOB_APPEND) {
     77   glob_t g = {};
     78   ASSERT_EQ(0, glob("/proc/version", 0, nullptr, &g));
     79   ASSERT_EQ(1U, g.gl_pathc);
     80   ASSERT_MATCH_COUNT(1U, g);
     81   ASSERT_STREQ("/proc/version", g.gl_pathv[0]);
     82   ASSERT_EQ(nullptr, g.gl_pathv[1]);
     83   ASSERT_EQ(0, glob("/proc/version", GLOB_APPEND, nullptr, &g));
     84   ASSERT_EQ(2U, g.gl_pathc);
     85   ASSERT_MATCH_COUNT(1U, g);
     86   ASSERT_STREQ("/proc/version", g.gl_pathv[0]);
     87   ASSERT_STREQ("/proc/version", g.gl_pathv[1]);
     88   ASSERT_EQ(nullptr, g.gl_pathv[2]);
     89   globfree(&g);
     90 }
     91 
     92 TEST(glob, glob_GLOB_DOOFFS) {
     93   glob_t g = {};
     94   g.gl_offs = 2;
     95   ASSERT_EQ(0, glob("/proc/version", GLOB_DOOFFS, nullptr, &g));
     96   ASSERT_EQ(1U, g.gl_pathc);
     97   ASSERT_MATCH_COUNT(1U, g);
     98   ASSERT_EQ(nullptr, g.gl_pathv[0]);
     99   ASSERT_EQ(nullptr, g.gl_pathv[1]);
    100   ASSERT_STREQ("/proc/version", g.gl_pathv[2]);
    101   ASSERT_EQ(nullptr, g.gl_pathv[3]);
    102   globfree(&g);
    103 }
    104 
    105 static std::string g_failure_path;
    106 static int g_failure_errno;
    107 static int test_error_callback_result;
    108 static int test_error_callback(const char* failure_path, int failure_errno) {
    109   g_failure_path = failure_path;
    110   g_failure_errno = failure_errno;
    111   return test_error_callback_result;
    112 }
    113 
    114 TEST(glob, glob_gl_errfunc) {
    115   glob_t g = {};
    116   InstallFake(&g);
    117 
    118   test_error_callback_result = 0;
    119   g_failure_errno = 0;
    120   ASSERT_EQ(GLOB_NOMATCH, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, test_error_callback, &g));
    121   ASSERT_EQ("/opendir-fail/", g_failure_path);
    122   ASSERT_EQ(EINVAL, g_failure_errno);
    123 
    124   test_error_callback_result = 1;
    125   g_failure_errno = 0;
    126   ASSERT_EQ(GLOB_ABORTED, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, test_error_callback, &g));
    127   ASSERT_EQ("/opendir-fail/", g_failure_path);
    128   ASSERT_EQ(EINVAL, g_failure_errno);
    129 }
    130 
    131 TEST(glob, glob_GLOB_ERR) {
    132   glob_t g = {};
    133   InstallFake(&g);
    134 
    135   ASSERT_EQ(GLOB_NOMATCH, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC, nullptr, &g));
    136 
    137   ASSERT_EQ(GLOB_ABORTED, glob("/opendir-fail/x*", GLOB_ALTDIRFUNC | GLOB_ERR, nullptr, &g));
    138 }
    139 
    140 TEST(glob, glob_GLOB_MARK) {
    141   TemporaryDir td;
    142   // The pattern we're about to pass doesn't have a trailing '/'...
    143   ASSERT_NE('/', std::string(td.dirname).back());
    144 
    145   glob_t g = {};
    146   // Using GLOB_MARK gets you a trailing '/' on a directory...
    147   ASSERT_EQ(0, glob(td.dirname, GLOB_MARK, nullptr, &g));
    148   ASSERT_EQ(1U, g.gl_pathc);
    149   ASSERT_MATCH_COUNT(1U, g);
    150   ASSERT_EQ(std::string(td.dirname) + "/", g.gl_pathv[0]);
    151   ASSERT_EQ(nullptr, g.gl_pathv[1]);
    152 
    153   TemporaryFile tf;
    154   // But not on a file...
    155   ASSERT_EQ(0, glob(tf.filename, GLOB_MARK, nullptr, &g));
    156   ASSERT_EQ(1U, g.gl_pathc);
    157   ASSERT_MATCH_COUNT(1U, g);
    158   ASSERT_STREQ(tf.filename, g.gl_pathv[0]);
    159   ASSERT_EQ(nullptr, g.gl_pathv[1]);
    160 
    161   globfree(&g);
    162 }
    163 
    164 TEST(glob, glob_GLOB_NOCHECK) {
    165   glob_t g = {};
    166   ASSERT_EQ(0, glob("/will/match/nothing", GLOB_NOCHECK, nullptr, &g));
    167   ASSERT_EQ(1U, g.gl_pathc);
    168   ASSERT_MATCH_COUNT(0U, g);
    169   ASSERT_STREQ("/will/match/nothing", g.gl_pathv[0]);
    170   ASSERT_EQ(nullptr, g.gl_pathv[1]);
    171   globfree(&g);
    172 }
    173 
    174 TEST(glob, glob_GLOB_NOSORT) {
    175   fake_dir = { "c", "a", "d", "b" };
    176 
    177   glob_t g = {};
    178   InstallFake(&g);
    179 
    180   ASSERT_EQ(0, glob("*", GLOB_ALTDIRFUNC, nullptr, &g));
    181   ASSERT_EQ(4U, g.gl_pathc);
    182   ASSERT_MATCH_COUNT(4U, g);
    183   ASSERT_STREQ("a", g.gl_pathv[0]);
    184   ASSERT_STREQ("b", g.gl_pathv[1]);
    185   ASSERT_STREQ("c", g.gl_pathv[2]);
    186   ASSERT_STREQ("d", g.gl_pathv[3]);
    187   ASSERT_EQ(nullptr, g.gl_pathv[4]);
    188 
    189   ASSERT_EQ(0, glob("*", GLOB_ALTDIRFUNC | GLOB_NOSORT, nullptr, &g));
    190   ASSERT_EQ(4U, g.gl_pathc);
    191   ASSERT_MATCH_COUNT(4U, g);
    192   ASSERT_STREQ("c", g.gl_pathv[0]);
    193   ASSERT_STREQ("a", g.gl_pathv[1]);
    194   ASSERT_STREQ("d", g.gl_pathv[2]);
    195   ASSERT_STREQ("b", g.gl_pathv[3]);
    196   ASSERT_EQ(nullptr, g.gl_pathv[4]);
    197 }
    198 
    199 TEST(glob, glob_GLOB_MAGCHAR) {
    200   glob_t g = {};
    201   ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist", 0, nullptr, &g));
    202   ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) == 0);
    203   ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist*", 0, nullptr, &g));
    204   ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) != 0);
    205 
    206   // We can lie, but glob(3) will turn that into truth...
    207   ASSERT_EQ(GLOB_NOMATCH, glob("/does-not-exist", GLOB_MAGCHAR, nullptr, &g));
    208   ASSERT_TRUE((g.gl_flags & GLOB_MAGCHAR) == 0);
    209 }
    210 
    211 static void CheckGlob(const char* pattern, const std::vector<std::string>& expected_matches) {
    212   glob_t g = {};
    213   InstallFake(&g);
    214 
    215   int expected_result = expected_matches.empty() ? GLOB_NOMATCH : 0;
    216   ASSERT_EQ(expected_result, glob(pattern, GLOB_ALTDIRFUNC, nullptr, &g)) << pattern;
    217   ASSERT_EQ(expected_matches.size(), g.gl_pathc);
    218   ASSERT_MATCH_COUNT(expected_matches.size(), g);
    219   for (size_t i = 0; i < expected_matches.size(); ++i) {
    220     ASSERT_EQ(expected_matches[i], g.gl_pathv[i]);
    221   }
    222   if (!expected_matches.empty()) {
    223     ASSERT_EQ(nullptr, g.gl_pathv[expected_matches.size()]);
    224   }
    225   globfree(&g);
    226 }
    227 
    228 TEST(glob, glob_globbing) {
    229   fake_dir = { "f1", "f2", "f30", "f40" };
    230 
    231   CheckGlob("f?", { "f1", "f2" });
    232   CheckGlob("f??", { "f30", "f40" });
    233   CheckGlob("f*", { "f1", "f2", "f30", "f40" });
    234 }
    235 
    236 TEST(glob, glob_globbing_rsc) {
    237   // https://research.swtch.com/glob
    238   fake_dir = { "axbxcxdxe" };
    239   CheckGlob("a*b*c*d*e*", { "axbxcxdxe" });
    240   fake_dir = { "axbxcxdxexxx" };
    241   CheckGlob("a*b*c*d*e*", { "axbxcxdxexxx" });
    242   fake_dir = { "abxbbxdbxebxczzx" };
    243   CheckGlob("a*b?c*x", { "abxbbxdbxebxczzx" });
    244   fake_dir = { "abxbbxdbxebxczzy" };
    245   CheckGlob("a*b?c*x", {});
    246 
    247   fake_dir = { std::string(100, 'a') };
    248   CheckGlob("a*a*a*a*b", {});
    249 }
    250