github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/os-readdir_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "fmt" 22 "os" 23 "path" 24 "path/filepath" 25 "runtime" 26 "sort" 27 "testing" 28 ) 29 30 // Test to check for different input arguments. 31 func TestReadDirFail(t *testing.T) { 32 // Check non existent directory. 33 if _, err := readDir("/tmp/non-existent-directory"); err != errFileNotFound { 34 t.Fatalf("expected = %s, got: %s", errFileNotFound, err) 35 } 36 37 file := path.Join(os.TempDir(), "issue") 38 if err := os.WriteFile(file, []byte(""), 0o644); err != nil { 39 t.Fatal(err) 40 } 41 defer os.RemoveAll(file) 42 43 // Check if file is given. 44 if _, err := readDir(path.Join(file, "mydir")); err != errFileNotFound { 45 t.Fatalf("expected = %s, got: %s", errFileNotFound, err) 46 } 47 48 // Only valid for linux. 49 if runtime.GOOS == "linux" { 50 permDir := path.Join(os.TempDir(), "perm-dir") 51 if err := os.MkdirAll(permDir, os.FileMode(0o200)); err != nil { 52 t.Fatal(err) 53 } 54 defer os.RemoveAll(permDir) 55 56 // Check if permission denied. 57 if _, err := readDir(permDir); err == nil { 58 t.Fatalf("expected = an error, got: nil") 59 } 60 } 61 } 62 63 // Represents data type for all the test results. 64 type result struct { 65 dir string 66 entries []string 67 } 68 69 // Test to read empty directory. 70 func setupTestReadDirEmpty(t *testing.T) (testResults []result) { 71 // Add empty entry slice for this test directory. 72 testResults = append(testResults, result{t.TempDir(), []string{}}) 73 return testResults 74 } 75 76 // Test to read non-empty directory with only files. 77 func setupTestReadDirFiles(t *testing.T) (testResults []result) { 78 dir := t.TempDir() 79 entries := []string{} 80 for i := 0; i < 10; i++ { 81 name := fmt.Sprintf("file-%d", i) 82 if err := os.WriteFile(filepath.Join(dir, name), []byte{}, os.ModePerm); err != nil { 83 // For cleanup, its required to add these entries into test results. 84 testResults = append(testResults, result{dir, entries}) 85 t.Fatalf("Unable to create file, %s", err) 86 } 87 entries = append(entries, name) 88 } 89 90 // Keep entries sorted for easier comparison. 91 sort.Strings(entries) 92 93 // Add entries slice for this test directory. 94 testResults = append(testResults, result{dir, entries}) 95 return testResults 96 } 97 98 // Test to read non-empty directory with directories and files. 99 func setupTestReadDirGeneric(t *testing.T) (testResults []result) { 100 dir := t.TempDir() 101 if err := os.MkdirAll(filepath.Join(dir, "mydir"), 0o777); err != nil { 102 t.Fatalf("Unable to create prefix directory \"mydir\", %s", err) 103 } 104 entries := []string{"mydir/"} 105 for i := 0; i < 10; i++ { 106 name := fmt.Sprintf("file-%d", i) 107 if err := os.WriteFile(filepath.Join(dir, "mydir", name), []byte{}, os.ModePerm); err != nil { 108 // For cleanup, its required to add these entries into test results. 109 testResults = append(testResults, result{dir, entries}) 110 t.Fatalf("Unable to write file, %s", err) 111 } 112 } 113 // Keep entries sorted for easier comparison. 114 sort.Strings(entries) 115 116 // Add entries slice for this test directory. 117 testResults = append(testResults, result{dir, entries}) 118 return testResults 119 } 120 121 // Test to read non-empty directory with symlinks. 122 func setupTestReadDirSymlink(t *testing.T) (testResults []result) { 123 if runtime.GOOS == globalWindowsOSName { 124 t.Skip("symlinks not available on windows") 125 return nil 126 } 127 dir := t.TempDir() 128 entries := []string{} 129 for i := 0; i < 10; i++ { 130 name1 := fmt.Sprintf("file-%d", i) 131 name2 := fmt.Sprintf("file-%d", i+10) 132 if err := os.WriteFile(filepath.Join(dir, name1), []byte{}, os.ModePerm); err != nil { 133 // For cleanup, its required to add these entries into test results. 134 testResults = append(testResults, result{dir, entries}) 135 t.Fatalf("Unable to create a file, %s", err) 136 } 137 // Symlink will not be added to entries. 138 if err := os.Symlink(filepath.Join(dir, name1), filepath.Join(dir, name2)); err != nil { 139 t.Fatalf("Unable to create a symlink, %s", err) 140 } 141 // Add to entries. 142 entries = append(entries, name1) 143 // Symlinks are preserved for regular files 144 entries = append(entries, name2) 145 } 146 if err := os.MkdirAll(filepath.Join(dir, "mydir"), 0o777); err != nil { 147 t.Fatalf("Unable to create \"mydir\", %s", err) 148 } 149 entries = append(entries, "mydir/") 150 151 // Keep entries sorted for easier comparison. 152 sort.Strings(entries) 153 154 // Add entries slice for this test directory. 155 testResults = append(testResults, result{dir, entries}) 156 return testResults 157 } 158 159 // checkResult - checks whether entries are got are same as expected entries. 160 func checkResult(expected []string, got []string) bool { 161 // If length of expected and got slice are different, the test actually failed. 162 if len(expected) != len(got) { 163 return false 164 } 165 166 for i := range expected { 167 // If entry in expected is not same as entry it got, the test is failed. 168 if expected[i] != got[i] { 169 return false 170 } 171 } 172 173 // expected and got have same entries. 174 return true 175 } 176 177 // teardown - cleans up test directories. 178 func teardown(testResults []result) { 179 for _, r := range testResults { 180 os.RemoveAll(r.dir) 181 } 182 } 183 184 // TestReadDir - test function to run various readDir() tests. 185 func TestReadDir(t *testing.T) { 186 var testResults []result 187 188 // Setup and capture test results for empty directory. 189 testResults = append(testResults, setupTestReadDirEmpty(t)...) 190 // Setup and capture test results for directory with only files. 191 testResults = append(testResults, setupTestReadDirFiles(t)...) 192 // Setup and capture test results for directory with files and directories. 193 testResults = append(testResults, setupTestReadDirGeneric(t)...) 194 // Setup and capture test results for directory with files and symlink. 195 testResults = append(testResults, setupTestReadDirSymlink(t)...) 196 197 // Remove all dirs once tests are over. 198 defer teardown(testResults) 199 200 // Validate all the results. 201 for _, r := range testResults { 202 if entries, err := readDir(r.dir); err != nil { 203 t.Fatal("failed to run test.", err) 204 } else { 205 // Keep entries sorted for easier comparison. 206 sort.Strings(entries) 207 if !checkResult(r.entries, entries) { 208 t.Fatalf("expected = %s, got: %s", r.entries, entries) 209 } 210 } 211 } 212 } 213 214 func TestReadDirN(t *testing.T) { 215 testCases := []struct { 216 numFiles int 217 n int 218 expectedNum int 219 }{ 220 {0, 0, 0}, 221 {0, 1, 0}, 222 {1, 0, 0}, 223 {0, -1, 0}, 224 {1, -1, 1}, 225 {10, -1, 10}, 226 {1, 1, 1}, 227 {2, 1, 1}, 228 {10, 9, 9}, 229 {10, 10, 10}, 230 {10, 11, 10}, 231 } 232 233 for i, testCase := range testCases { 234 dir := t.TempDir() 235 236 for c := 1; c <= testCase.numFiles; c++ { 237 err := os.WriteFile(filepath.Join(dir, fmt.Sprintf("%d", c)), []byte{}, os.ModePerm) 238 if err != nil { 239 os.RemoveAll(dir) 240 t.Fatalf("Unable to create a file, %s", err) 241 } 242 } 243 entries, err := readDirN(dir, testCase.n) 244 if err != nil { 245 os.RemoveAll(dir) 246 t.Fatalf("Unable to read entries, %s", err) 247 } 248 if len(entries) != testCase.expectedNum { 249 os.RemoveAll(dir) 250 t.Fatalf("Test %d: unexpected number of entries, waiting for %d, but found %d", 251 i+1, testCase.expectedNum, len(entries)) 252 } 253 os.RemoveAll(dir) 254 } 255 }