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  }