...

Source file src/sigs.k8s.io/kustomize/kyaml/filesys/fsnode_test.go

Documentation: sigs.k8s.io/kustomize/kyaml/filesys

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  //go:build !windows
     5  // +build !windows
     6  
     7  package filesys
     8  
     9  import (
    10  	"fmt"
    11  	"io"
    12  	"math/rand"
    13  	"os"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"testing"
    18  
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  const content = `
    23  Lorem ipsum dolor sit amet,
    24  consectetur adipiscing elit,
    25  sed do eiusmod tempor incididunt
    26  ut labore et dolore magna aliqua.
    27  `
    28  const shortContent = "hi"
    29  
    30  var topCases = []pathCase{
    31  	{
    32  		what:   "dotdot",
    33  		arg:    ParentDir,
    34  		errStr: "illegal name '..' in file creation",
    35  	},
    36  	{
    37  		what: "colon",
    38  		arg:  "a:b",
    39  		name: "a:b",
    40  		path: "a:b",
    41  	},
    42  	{
    43  		what:   "empty",
    44  		arg:    "",
    45  		name:   "",
    46  		errStr: "illegal name '.' in file creation",
    47  	},
    48  	{
    49  		what: "simple",
    50  		arg:  "bob",
    51  		name: "bob",
    52  		path: "bob",
    53  	},
    54  	{
    55  		what: "longer",
    56  		arg:  filepath.Join("longer", "bob"),
    57  		name: "bob",
    58  		path: filepath.Join("longer", "bob"),
    59  	},
    60  	{
    61  		what: "longer yet",
    62  		arg:  filepath.Join("longer", "foo", "bar", "beans", "bob"),
    63  		name: "bob",
    64  		path: filepath.Join("longer", "foo", "bar", "beans", "bob"),
    65  	},
    66  	{
    67  		what: "tricky",
    68  		arg:  filepath.Join("bob", ParentDir, "sally"),
    69  		name: "sally",
    70  		path: "sally",
    71  	},
    72  	{
    73  		what: "trickier",
    74  		arg:  filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"),
    75  		name: "jean",
    76  		path: "jean",
    77  	},
    78  }
    79  
    80  func TestMakeEmptyDirInMemory(t *testing.T) {
    81  	n := MakeEmptyDirInMemory()
    82  	if !n.isNodeADir() {
    83  		t.Fatalf("not a directory")
    84  	}
    85  	if n.Size() != 0 {
    86  		t.Fatalf("unexpected size %d", n.Size())
    87  	}
    88  	if n.Name() != "" {
    89  		t.Fatalf("unexpected name '%s'", n.Name())
    90  	}
    91  	if n.Path() != "" {
    92  		t.Fatalf("unexpected path '%s'", n.Path())
    93  	}
    94  	runBasicOperations(
    95  		t, "MakeEmptyDirInMemory", false, topCases, n)
    96  }
    97  
    98  func TestMakeFsInMemory(t *testing.T) {
    99  	runBasicOperations(
   100  		t, "MakeFsInMemory", true, topCases, MakeFsInMemory())
   101  }
   102  
   103  func runBasicOperations(
   104  	t *testing.T, tName string, isFSysRooted bool,
   105  	cases []pathCase, fSys FileSystem) {
   106  	t.Helper()
   107  	for _, c := range cases {
   108  		err := fSys.WriteFile(c.arg, []byte(content))
   109  		if c.errStr != "" {
   110  			if err == nil {
   111  				t.Fatalf("%s; expected error writing to  '%s'!", c.what, c.arg)
   112  			}
   113  			if !strings.Contains(err.Error(), c.errStr) {
   114  				t.Fatalf("%s; expected err containing '%s', got '%v'",
   115  					c.what, c.errStr, err)
   116  			}
   117  			continue
   118  		}
   119  		if err != nil {
   120  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   121  		}
   122  		if !fSys.Exists(c.path) {
   123  			t.Fatalf("%s; expect existence of '%s'", c.what, c.path)
   124  		}
   125  		stuff, err := fSys.ReadFile(c.path)
   126  		if err != nil {
   127  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   128  		}
   129  		if string(stuff) != content {
   130  			t.Fatalf("%s; unexpected content '%s'", c.what, stuff)
   131  		}
   132  		f, err := fSys.Open(c.arg)
   133  		if err != nil {
   134  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   135  		}
   136  		fi, err := f.Stat()
   137  		if err != nil {
   138  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   139  		}
   140  		if fi.Name() != c.name {
   141  			t.Fatalf("%s; expected name '%s', got '%s'", c.what, c.name, fi.Name())
   142  		}
   143  		buff, err := io.ReadAll(f)
   144  		if err != nil {
   145  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   146  		}
   147  		if string(buff) != content {
   148  			t.Fatalf("%s; unexpected buff '%s'", c.what, buff)
   149  		}
   150  		count, err := f.Write([]byte(shortContent))
   151  		if err != nil {
   152  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   153  		}
   154  		if count != len(shortContent) {
   155  			t.Fatalf("%s; unexpected count: %d", c.what, len(shortContent))
   156  		}
   157  		if err := f.Close(); err != nil {
   158  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   159  		}
   160  		stuff, err = fSys.ReadFile(c.path)
   161  		if err != nil {
   162  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   163  		}
   164  		both := content + shortContent
   165  		if string(stuff) != both {
   166  			t.Fatalf("%s; unexpected content '%s', expected '%s'", c.what, stuff, both)
   167  		}
   168  
   169  		content := []byte(shortContent)
   170  		if err := fSys.WriteFile(c.path, content); err != nil {
   171  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   172  		}
   173  		// This ensures that modifying the original slice does not change the contents of the file.
   174  		content[0] = '@'
   175  		stuff, err = fSys.ReadFile(c.path)
   176  		if err != nil {
   177  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   178  		}
   179  		if string(stuff) != shortContent {
   180  			t.Fatalf("%s; unexpected content '%s', expected '%s'", c.what, stuff, shortContent)
   181  		}
   182  	}
   183  
   184  	var actualPaths []string
   185  	var err error
   186  	prefix := ""
   187  	{
   188  		root := SelfDir
   189  		if isFSysRooted {
   190  			root = Separator
   191  			prefix = Separator
   192  		}
   193  		err = fSys.Walk(root, func(path string, info os.FileInfo, err error) error {
   194  			if err != nil {
   195  				fmt.Printf("err '%v' at path %q\n", err, path)
   196  				return nil
   197  			}
   198  			if !info.IsDir() {
   199  				actualPaths = append(actualPaths, path)
   200  			}
   201  			return nil
   202  		})
   203  	}
   204  	if err != nil {
   205  		t.Fatalf("unexpected error %v", err)
   206  	}
   207  	var expectedPaths []string
   208  	for _, c := range cases {
   209  		if c.errStr == "" {
   210  			expectedPaths = append(expectedPaths, prefix+c.path)
   211  		}
   212  	}
   213  	sort.Strings(expectedPaths)
   214  	assertEqualStringSlices(t, expectedPaths, actualPaths, tName)
   215  }
   216  
   217  type pathCase struct {
   218  	what   string
   219  	arg    string
   220  	name   string
   221  	path   string
   222  	errStr string
   223  }
   224  
   225  func TestAddDir(t *testing.T) {
   226  	cases := []pathCase{
   227  		{
   228  			what:   "dotdot",
   229  			arg:    ParentDir,
   230  			errStr: "cannot add a directory above ''",
   231  		},
   232  		{
   233  			what: "empty",
   234  			arg:  "",
   235  			name: "",
   236  			path: "",
   237  		},
   238  		{
   239  			what: "simple",
   240  			arg:  "bob",
   241  			name: "bob",
   242  			path: "bob",
   243  		},
   244  		{
   245  			what: "longer",
   246  			arg:  filepath.Join("longer", "bob"),
   247  			name: "bob",
   248  			path: filepath.Join("longer", "bob"),
   249  		},
   250  		{
   251  			what: "longer yet",
   252  			arg:  filepath.Join("longer", "foo", "bar", "beans", "bob"),
   253  			name: "bob",
   254  			path: filepath.Join("longer", "foo", "bar", "beans", "bob"),
   255  		},
   256  		{
   257  			what: "tricky",
   258  			arg:  filepath.Join("bob", ParentDir, "sally"),
   259  			name: "sally",
   260  			path: "sally",
   261  		},
   262  		{
   263  			what: "trickier",
   264  			arg:  filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"),
   265  			name: "jean",
   266  			path: "jean",
   267  		},
   268  	}
   269  	for _, c := range cases {
   270  		n := MakeEmptyDirInMemory()
   271  		f, err := n.AddDir(c.arg)
   272  		if c.errStr != "" {
   273  			if err == nil {
   274  				t.Fatalf("%s; expected error!", c.what)
   275  			}
   276  			if !strings.Contains(err.Error(), c.errStr) {
   277  				t.Fatalf(
   278  					"%s; expected error with '%s', got '%v'",
   279  					c.what, c.errStr, err)
   280  			}
   281  			continue
   282  		}
   283  		if err != nil {
   284  			t.Fatalf("%s; unexpected error: %v", c.what, err)
   285  		}
   286  		checkNode(t, c.what, f, c.name, 0, true, c.path)
   287  		checkOsStat(t, c.what, f, f.Name(), 0, true)
   288  	}
   289  }
   290  
   291  var bagOfCases = []pathCase{
   292  	{
   293  		what:   "empty",
   294  		arg:    "",
   295  		errStr: "illegal name '.' in file creation",
   296  	},
   297  	{
   298  		what: "simple",
   299  		arg:  "bob",
   300  		name: "bob",
   301  		path: "bob",
   302  	},
   303  	{
   304  		what: "longer",
   305  		arg:  filepath.Join("longer", "bob"),
   306  		name: "bob",
   307  		path: filepath.Join("longer", "bob"),
   308  	},
   309  	{
   310  		what: "longer",
   311  		arg:  filepath.Join("longer", "sally"),
   312  		name: "sally",
   313  		path: filepath.Join("longer", "sally"),
   314  	},
   315  	{
   316  		what: "even longer",
   317  		arg:  filepath.Join("longer", "than", "the", "other", "bob"),
   318  		name: "bob",
   319  		path: filepath.Join("longer", "than", "the", "other", "bob"),
   320  	},
   321  	{
   322  		what: "even longer",
   323  		arg:  filepath.Join("even", "much", "longer", "than", "the", "other", "bob"),
   324  		name: "bob",
   325  		path: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"),
   326  	},
   327  }
   328  
   329  func TestAddFile(t *testing.T) {
   330  	n := MakeEmptyDirInMemory()
   331  	if n.FileCount() != 0 {
   332  		t.Fatalf("expected no files, got %d", n.FileCount())
   333  	}
   334  	expectedFileCount := 0
   335  	for _, c := range bagOfCases {
   336  		f, err := n.AddFile(c.arg, []byte(content))
   337  		if c.errStr != "" {
   338  			if err == nil {
   339  				t.Fatalf("%s; expected error!", c.what)
   340  			}
   341  			if !strings.Contains(err.Error(), c.errStr) {
   342  				t.Fatalf("%s; expected err containing '%s', got '%v'",
   343  					c.what, c.errStr, err)
   344  			}
   345  			continue
   346  		}
   347  		if err != nil {
   348  			t.Fatalf("%s; unexpected error %v", c.what, err)
   349  		}
   350  		checkNode(t, c.what, f, c.name, len(content), false, c.path)
   351  		checkOsStat(t, c.what, f, f.Name(), len(content), false)
   352  
   353  		result, err := n.Find(c.arg)
   354  		if err != nil {
   355  			t.Fatalf("%s; unexpected find error %v", c.what, err)
   356  		}
   357  		if result != f {
   358  			t.Fatalf("%s; unexpected find result %v", c.what, result)
   359  		}
   360  
   361  		result, err = n.Find(filepath.Join("longer", "bogus"))
   362  		if err != nil {
   363  			t.Fatalf("%s; unexpected find error %v", c.what, err)
   364  		}
   365  		if result != nil {
   366  			t.Fatalf("%s; unexpected find result %v", c.what, result)
   367  		}
   368  
   369  		expectedFileCount++
   370  		fc := n.FileCount()
   371  		if fc != expectedFileCount {
   372  			t.Fatalf("expected file count %d, got %d",
   373  				expectedFileCount, fc)
   374  		}
   375  	}
   376  }
   377  
   378  func checkNode(
   379  	t *testing.T, what string, f *fsNode, name string,
   380  	size int, isDir bool, path string) {
   381  	t.Helper()
   382  	if f.isNodeADir() != isDir {
   383  		t.Fatalf("%s; unexpected isNodeADir = %v", what, f.isNodeADir())
   384  	}
   385  	if f.Size() != int64(size) {
   386  		t.Fatalf("%s; unexpected size %d", what, f.Size())
   387  	}
   388  	if name != f.Name() {
   389  		t.Fatalf("%s; expected name '%s', got '%s'", what, name, f.Name())
   390  	}
   391  	if path != f.Path() {
   392  		t.Fatalf("%s; expected path '%s', got '%s'", what, path, f.Path())
   393  	}
   394  }
   395  
   396  func checkOsStat(
   397  	t *testing.T, what string, f File, name string,
   398  	size int, isDir bool) {
   399  	t.Helper()
   400  	info, err := f.Stat()
   401  	if err != nil {
   402  		t.Fatalf("%s; unexpected stat error %v", what, err)
   403  	}
   404  	if info.IsDir() != isDir {
   405  		t.Fatalf("%s; unexpected info.isNodeADir = %v", what, info.IsDir())
   406  	}
   407  	if info.Size() != int64(size) {
   408  		t.Fatalf("%s; unexpected info.size %d", what, info.Size())
   409  	}
   410  	if info.Name() != name {
   411  		t.Fatalf("%s; expected name '%s', got info.Name '%s'", what, name, info.Name())
   412  	}
   413  }
   414  
   415  var bunchOfFiles = []struct {
   416  	path     string
   417  	addAsDir bool
   418  }{
   419  	{
   420  		path: filepath.Join("b", "e", "a", "c", "g"),
   421  	},
   422  	{
   423  		path: filepath.Join("z", "r", "a", "b", "g"),
   424  	},
   425  	{
   426  		path: filepath.Join("b", "q", "a", "c", "g"),
   427  	},
   428  	{
   429  		path:     filepath.Join("b", "a", "a", "m", "g"),
   430  		addAsDir: true,
   431  	},
   432  	{
   433  		path: filepath.Join("b", "w"),
   434  	},
   435  	{
   436  		path: filepath.Join("b", "d", "a", "c", "m"),
   437  	},
   438  	{
   439  		path: filepath.Join("b", "d", "z"),
   440  	},
   441  	{
   442  		path: filepath.Join("b", "d", "y"),
   443  	},
   444  	{
   445  		path: filepath.Join("b", "d", "ignore", "c", "n"),
   446  	},
   447  	{
   448  		path: filepath.Join("b", "d", "x"),
   449  	},
   450  	{
   451  		path: filepath.Join("b", "d", "ignore", "c", "o"),
   452  	},
   453  	{
   454  		path: filepath.Join("b", "d", "ignore", "c", "m"),
   455  	},
   456  	{
   457  		path:     filepath.Join("b", "d", "a", "c", "i"),
   458  		addAsDir: true,
   459  	},
   460  	{
   461  		path: "x",
   462  	},
   463  	{
   464  		path: "y",
   465  	},
   466  	{
   467  		path: filepath.Join("b", "d", "a", "c", "i", "beans"),
   468  	},
   469  	{
   470  		path:     filepath.Join("b", "d", "a", "c", "r", "w"),
   471  		addAsDir: true,
   472  	},
   473  	{
   474  		path: filepath.Join("b", "d", "a", "c", "u"),
   475  	},
   476  	{
   477  		path: filepath.Join("b", "d", ".hidden_file"),
   478  	},
   479  	{
   480  		path:     filepath.Join("b", "d", ".hidden_dir"),
   481  		addAsDir: true,
   482  	},
   483  }
   484  
   485  func makeLoadedFileTree(t *testing.T) *fsNode {
   486  	t.Helper()
   487  	n := MakeEmptyDirInMemory()
   488  	var err error
   489  	expectedFileCount := 0
   490  	for _, item := range bunchOfFiles {
   491  		if item.addAsDir {
   492  			_, err = n.AddDir(item.path)
   493  		} else {
   494  			_, err = n.AddFile(item.path, []byte(content))
   495  			expectedFileCount++
   496  		}
   497  		if err != nil {
   498  			t.Fatalf("unexpected error %v", err)
   499  		}
   500  	}
   501  	if fc := n.FileCount(); fc != expectedFileCount {
   502  		t.Fatalf("expected file count %d, got %d",
   503  			expectedFileCount, fc)
   504  	}
   505  	return n
   506  }
   507  
   508  func TestWalkMe(t *testing.T) {
   509  	n := makeLoadedFileTree(t)
   510  	var actualPaths []string
   511  	err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
   512  		if err != nil {
   513  			fmt.Printf("err '%v' at path %q\n", err, path)
   514  			return nil
   515  		}
   516  		if info.IsDir() {
   517  			if info.Name() == "ignore" {
   518  				return filepath.SkipDir
   519  			}
   520  		} else {
   521  			actualPaths = append(actualPaths, path)
   522  		}
   523  		return nil
   524  	})
   525  	if err != nil {
   526  		t.Fatalf("unexpected error %v", err)
   527  	}
   528  	var expectedPaths []string
   529  	for _, c := range bunchOfFiles {
   530  		if !c.addAsDir && !strings.Contains(c.path, "ignore") {
   531  			expectedPaths = append(expectedPaths, c.path)
   532  		}
   533  	}
   534  	sort.Strings(expectedPaths)
   535  	assertEqualStringSlices(t, expectedPaths, actualPaths, "testWalkMe")
   536  }
   537  
   538  func TestRemove(t *testing.T) {
   539  	n := makeLoadedFileTree(t)
   540  	orgCount := n.FileCount()
   541  
   542  	// Remove the "ignore" directory and everything below it.
   543  	path := filepath.Join("b", "d", "ignore")
   544  	result, err := n.Find(path)
   545  	if err != nil {
   546  		t.Fatalf("%s; unexpected error %v", path, err)
   547  	}
   548  	if result == nil {
   549  		t.Fatalf("%s; expected to find '%s'", path, path)
   550  	}
   551  	if !result.isNodeADir() {
   552  		t.Fatalf("%s; expected to find a directory", path)
   553  	}
   554  	err = result.Remove()
   555  	if err != nil {
   556  		t.Fatalf("%s; unable to remove: %v", path, err)
   557  	}
   558  	result, err = n.Find(path)
   559  	if err != nil {
   560  		// Just because it's gone doesn't mean error.
   561  		t.Fatalf("%s; unexpected error %v", path, err)
   562  	}
   563  	if result != nil {
   564  		t.Fatalf("%s; should not have been able to find '%s'", path, path)
   565  	}
   566  
   567  	// There were three files below "ignore".
   568  	orgCount -= 3
   569  
   570  	// Now drop one more for a total of four dropped.
   571  	result, _ = n.Find("y")
   572  	err = result.Remove()
   573  	if err != nil {
   574  		t.Fatalf("%s; unable to remove: %v", path, err)
   575  	}
   576  	orgCount -= 1
   577  
   578  	if fc := n.FileCount(); fc != orgCount {
   579  		t.Fatalf("expected file count %d, got %d",
   580  			orgCount, fc)
   581  	}
   582  }
   583  
   584  func TestExists(t *testing.T) {
   585  	n := makeLoadedFileTree(t)
   586  	path := filepath.Join("b", "d", "a")
   587  	if !n.Exists(path) {
   588  		t.Fatalf("expected existence at %s", path)
   589  	}
   590  	if !n.IsDir(path) {
   591  		t.Fatalf("expected directory at %s", path)
   592  	}
   593  }
   594  
   595  func TestRegExpGlob(t *testing.T) {
   596  	n := makeLoadedFileTree(t)
   597  	expected := []string{
   598  		filepath.Join("b", "d", ".hidden_file"),
   599  		filepath.Join("b", "d", "a", "c", "i", "beans"),
   600  		filepath.Join("b", "d", "a", "c", "m"),
   601  		filepath.Join("b", "d", "a", "c", "u"),
   602  		filepath.Join("b", "d", "ignore", "c", "m"),
   603  		filepath.Join("b", "d", "ignore", "c", "n"),
   604  		filepath.Join("b", "d", "ignore", "c", "o"),
   605  		filepath.Join("b", "d", "x"),
   606  		filepath.Join("b", "d", "y"),
   607  		filepath.Join("b", "d", "z"),
   608  	}
   609  	paths, err := n.RegExpGlob("b/d/*")
   610  	if err != nil {
   611  		t.Fatalf("glob error: %v", err)
   612  	}
   613  	assertEqualStringSlices(t, expected, paths, "glob test")
   614  }
   615  
   616  func TestGlob(t *testing.T) {
   617  	n := makeLoadedFileTree(t)
   618  
   619  	tests := map[string]struct {
   620  		globPattern   string
   621  		expectedFiles []string
   622  	}{
   623  		"VisibleFiles": {
   624  			globPattern: "b/d/*",
   625  			expectedFiles: []string{
   626  				filepath.Join("b", "d", "x"),
   627  				filepath.Join("b", "d", "y"),
   628  				filepath.Join("b", "d", "z"),
   629  			},
   630  		},
   631  		"HiddenFiles": {
   632  			globPattern: "b/d/.*",
   633  			expectedFiles: []string{
   634  				filepath.Join("b", "d", ".hidden_file"),
   635  			},
   636  		},
   637  	}
   638  
   639  	for test, c := range tests {
   640  		t.Run(test, func(t *testing.T) {
   641  			paths, err := n.Glob(c.globPattern)
   642  			if err != nil {
   643  				t.Fatalf("glob error: %v", err)
   644  			}
   645  			assertEqualStringSlices(t, c.expectedFiles, paths, "glob test")
   646  		})
   647  	}
   648  }
   649  
   650  func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) {
   651  	t.Helper()
   652  	if len(expected) != len(actual) {
   653  		t.Fatalf(
   654  			"%s; unequal sizes; len(expected)=%d, len(actual)=%d\n%+v\n%+v\n",
   655  			message, len(expected), len(actual), expected, actual)
   656  	}
   657  	for i := range expected {
   658  		if expected[i] != actual[i] {
   659  			t.Fatalf(
   660  				"%s; unequal entries; expected=%s, actual=%s",
   661  				message, expected[i], actual[i])
   662  		}
   663  	}
   664  }
   665  
   666  func TestFind(t *testing.T) {
   667  	cases := []struct {
   668  		what       string
   669  		arg        string
   670  		expectDir  bool
   671  		expectFile bool
   672  		errStr     string
   673  	}{
   674  		{
   675  			what: "garbage",
   676  			arg:  "///1(*&SA",
   677  		},
   678  		{
   679  			what: "simple",
   680  			arg:  "bob",
   681  		},
   682  		{
   683  			what: "no directory",
   684  			arg:  filepath.Join("b", "rrrrrr"),
   685  		},
   686  		{
   687  			what:      "is a directory",
   688  			arg:       filepath.Join("b", "d", "ignore"),
   689  			expectDir: true,
   690  		},
   691  		{
   692  			what:       "longer, ending in file",
   693  			arg:        filepath.Join("b", "d", "x"),
   694  			expectFile: true,
   695  		},
   696  		{
   697  			what:       "moar longer, ending in file",
   698  			arg:        filepath.Join("b", "d", "a", "c", "u"),
   699  			expectFile: true,
   700  		},
   701  		{
   702  			what:      "directory",
   703  			arg:       "b",
   704  			expectDir: true,
   705  		},
   706  		{
   707  			// Querying for the empty string could
   708  			// 1) be an error,
   709  			// 2) return no result (and no error) as with
   710  			//    any illegal and therefore non-existent
   711  			//    file name,
   712  			// 3) return the node itself, like running
   713  			//    'ls' with no argument.
   714  			// Going with option 2 (no result, no error),
   715  			// since at this low level it makes more sense
   716  			// if the results for the empty string query
   717  			// differ from the results for the "." query.
   718  			what: "empty name",
   719  			arg:  "",
   720  		},
   721  		{
   722  			what:      "self dir",
   723  			arg:       SelfDir,
   724  			expectDir: true,
   725  		},
   726  		{
   727  			what: "parent dir - doesn't exist",
   728  			arg:  ParentDir,
   729  		},
   730  		{
   731  			what: "many parents - doesn't exist",
   732  			arg:  filepath.Join(ParentDir, ParentDir, ParentDir),
   733  		},
   734  	}
   735  
   736  	n := makeLoadedFileTree(t)
   737  	for _, item := range cases {
   738  		result, err := n.Find(item.arg)
   739  		if item.errStr != "" {
   740  			if err == nil {
   741  				t.Fatalf("%s; expected error", item.what)
   742  			}
   743  			if !strings.Contains(err.Error(), item.errStr) {
   744  				t.Fatalf("%s; expected err containing '%s', got '%v'",
   745  					item.what, item.errStr, err)
   746  			}
   747  			continue
   748  		}
   749  		if err != nil {
   750  			t.Fatalf("%s; unexpected error: %v", item.what, err)
   751  		}
   752  		if result == nil {
   753  			if item.expectDir {
   754  				t.Fatalf(
   755  					"%s; expected to find directory '%s'", item.what, item.arg)
   756  			}
   757  			if item.expectFile {
   758  				t.Fatalf(
   759  					"%s; expected to find file '%s'", item.what, item.arg)
   760  			}
   761  			continue
   762  		}
   763  		if item.expectDir {
   764  			if !result.isNodeADir() {
   765  				t.Fatalf(
   766  					"%s; expected '%s' to be a directory", item.what, item.arg)
   767  			}
   768  			continue
   769  		}
   770  		if item.expectFile {
   771  			if result.isNodeADir() {
   772  				t.Fatalf("%s; expected '%s' to be a file", item.what, item.arg)
   773  			}
   774  			continue
   775  		}
   776  		t.Fatalf(
   777  			"%s; expected nothing for '%s', but got '%s'",
   778  			item.what, item.arg, result.Path())
   779  	}
   780  }
   781  
   782  func TestCleanedAbs(t *testing.T) {
   783  	cases := []struct {
   784  		what   string
   785  		full   string
   786  		cDir   string
   787  		name   string
   788  		errStr string
   789  	}{
   790  		{
   791  			what:   "empty",
   792  			full:   "",
   793  			errStr: "doesn't exist",
   794  		},
   795  		{
   796  			what:   "simple",
   797  			full:   "bob",
   798  			errStr: "'bob' doesn't exist",
   799  		},
   800  		{
   801  			what:   "no directory",
   802  			full:   filepath.Join("b", "rrrrrr"),
   803  			errStr: "'b/rrrrrr' doesn't exist",
   804  		},
   805  		{
   806  			what: "longer, ending in file",
   807  			full: filepath.Join("b", "d", "x"),
   808  			cDir: filepath.Join("b", "d"),
   809  			name: "x",
   810  		},
   811  		{
   812  			what: "moar longer, ending in file",
   813  			full: filepath.Join("b", "d", "a", "c", "u"),
   814  			cDir: filepath.Join("b", "d", "a", "c"),
   815  			name: "u",
   816  		},
   817  		{
   818  			what: "directory",
   819  			full: filepath.Join("b", "d"),
   820  			cDir: filepath.Join("b", "d"),
   821  			name: "",
   822  		},
   823  	}
   824  
   825  	n := makeLoadedFileTree(t)
   826  	for _, item := range cases {
   827  		cDir, name, err := n.CleanedAbs(item.full)
   828  		if item.errStr != "" {
   829  			if err == nil {
   830  				t.Fatalf("%s; expected error", item.what)
   831  			}
   832  			if !strings.Contains(err.Error(), item.errStr) {
   833  				t.Fatalf("%s; expected err containing '%s', got '%v'",
   834  					item.what, item.errStr, err)
   835  			}
   836  			continue
   837  		}
   838  		if err != nil {
   839  			t.Fatalf("%s; unexpected error: %v", item.what, err)
   840  		}
   841  		if cDir != ConfirmedDir(item.cDir) {
   842  			t.Fatalf("%s; expected cDir=%s, got '%s'", item.what, item.cDir, cDir)
   843  		}
   844  		if name != item.name {
   845  			t.Fatalf("%s; expected name=%s, got '%s'", item.what, item.name, name)
   846  		}
   847  	}
   848  }
   849  
   850  func TestConfirmDirMemRoot(t *testing.T) {
   851  	fSys := MakeFsInMemory()
   852  	actual, err := ConfirmDir(fSys, Separator)
   853  	require.NoError(t, err)
   854  	require.Equal(t, Separator, actual.String())
   855  }
   856  
   857  func TestConfirmDirRelativeNode(t *testing.T) {
   858  	req := require.New(t)
   859  	fSysEmpty := MakeEmptyDirInMemory()
   860  
   861  	fSysRoot, err := fSysEmpty.AddDir("a")
   862  	req.NoError(err)
   863  	fSysSub, err := fSysRoot.AddDir("b")
   864  	req.NoError(err)
   865  	err = fSysSub.Mkdir("c")
   866  	req.NoError(err)
   867  
   868  	expected := filepath.Join("a", "b", "c")
   869  	req.Truef(fSysEmpty.Exists(expected), existMsg, expected)
   870  
   871  	actual, err := ConfirmDir(fSysSub, "c")
   872  	req.NoError(err)
   873  	req.Equal(expected, actual.String())
   874  }
   875  
   876  func TestFileOps(t *testing.T) {
   877  	const path = "foo.txt"
   878  	content := strings.Repeat("longest content", 100)
   879  
   880  	fs := MakeFsInMemory()
   881  	f, err := fs.Create(path)
   882  	if err != nil {
   883  		t.Fatalf("unexpected error: %v", err)
   884  	}
   885  	if _, err := fs.Open(path); err == nil {
   886  		t.Fatalf("expected already opened error, got nil")
   887  	}
   888  	if _, err := fmt.Fprint(f, content); err != nil {
   889  		t.Fatalf("unexpected error: %v", err)
   890  	}
   891  
   892  	if err := f.Close(); err != nil {
   893  		t.Fatalf("unexpected error: %v", err)
   894  	}
   895  	if err := f.Close(); err == nil {
   896  		t.Fatalf("expected already closed error, got nil")
   897  	}
   898  
   899  	f, err = fs.Open(path)
   900  	if err != nil {
   901  		t.Fatalf("unexpected error: %v", err)
   902  	}
   903  	defer f.Close()
   904  
   905  	for {
   906  		buf := make([]byte, rand.Intn(10)) //nolint:gosec
   907  		n, err := f.Read(buf)
   908  		if err != nil && err != io.EOF {
   909  			t.Fatalf("unexpected error: %v", err)
   910  		}
   911  		if content[:n] != string(buf[:n]) {
   912  			t.Fatalf("unexpected read: expected %q got %q", content[:n], buf[:n])
   913  		}
   914  		content = content[n:]
   915  		if err != io.EOF {
   916  			continue
   917  		}
   918  		if len(content) == 0 {
   919  			break
   920  		}
   921  		t.Fatalf("unexpected EOF: remaining %d bytes", len(content))
   922  	}
   923  }
   924  

View as plain text