github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/vfs/mem_fs_test.go (about)

     1  // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package vfs
     6  
     7  import (
     8  	"io"
     9  	"io/ioutil"
    10  	"os"
    11  	"sort"
    12  	"strconv"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func runTestCases(t *testing.T, testCases []string, fs *MemFS) {
    20  	var f File
    21  	for _, tc := range testCases {
    22  		s := strings.Split(tc, " ")[1:]
    23  
    24  		saveF := s[0] == "f" && s[1] == "="
    25  		if saveF {
    26  			s = s[2:]
    27  		}
    28  
    29  		fails := s[len(s)-1] == "fails"
    30  		if fails {
    31  			s = s[:len(s)-1]
    32  		}
    33  
    34  		var (
    35  			fi  os.FileInfo
    36  			g   File
    37  			err error
    38  		)
    39  		switch s[0] {
    40  		case "create":
    41  			g, err = fs.Create(s[1])
    42  		case "link":
    43  			err = fs.Link(s[1], s[2])
    44  		case "open":
    45  			g, err = fs.Open(s[1])
    46  		case "openDir":
    47  			g, err = fs.OpenDir(s[1])
    48  		case "mkdirall":
    49  			err = fs.MkdirAll(s[1], 0755)
    50  		case "remove":
    51  			err = fs.Remove(s[1])
    52  		case "rename":
    53  			err = fs.Rename(s[1], s[2])
    54  		case "reuseForWrite":
    55  			g, err = fs.ReuseForWrite(s[1], s[2])
    56  		case "resetToSynced":
    57  			fs.ResetToSyncedState()
    58  		case "ignoreSyncs":
    59  			fs.SetIgnoreSyncs(true)
    60  		case "stopIgnoringSyncs":
    61  			fs.SetIgnoreSyncs(false)
    62  		case "f.write":
    63  			_, err = f.Write([]byte(s[1]))
    64  		case "f.sync":
    65  			err = f.Sync()
    66  		case "f.read":
    67  			n, _ := strconv.Atoi(s[1])
    68  			buf := make([]byte, n)
    69  			_, err = io.ReadFull(f, buf)
    70  			if err != nil {
    71  				break
    72  			}
    73  			if got, want := string(buf), s[3]; got != want {
    74  				t.Fatalf("%q: got %q, want %q", tc, got, want)
    75  			}
    76  		case "f.readat":
    77  			n, _ := strconv.Atoi(s[1])
    78  			off, _ := strconv.Atoi(s[2])
    79  			buf := make([]byte, n)
    80  			_, err = f.ReadAt(buf, int64(off))
    81  			if err != nil {
    82  				break
    83  			}
    84  			if got, want := string(buf), s[4]; got != want {
    85  				t.Fatalf("%q: got %q, want %q", tc, got, want)
    86  			}
    87  		case "f.close":
    88  			f, err = nil, f.Close()
    89  		case "f.stat.name":
    90  			fi, err = f.Stat()
    91  			if err != nil {
    92  				break
    93  			}
    94  			if got, want := fi.Name(), s[2]; got != want {
    95  				t.Fatalf("%q: got %q, want %q", tc, got, want)
    96  			}
    97  		default:
    98  			t.Fatalf("bad test case: %q", tc)
    99  		}
   100  
   101  		if saveF {
   102  			f, g = g, nil
   103  		} else if g != nil {
   104  			g.Close()
   105  		}
   106  
   107  		if fails {
   108  			if err == nil {
   109  				t.Fatalf("%q: got nil error, want non-nil", tc)
   110  			}
   111  		} else {
   112  			if err != nil {
   113  				t.Fatalf("%q: %v", tc, err)
   114  			}
   115  		}
   116  	}
   117  
   118  	// Both "" and "/" are allowed to be used to refer to the root of the FS
   119  	// for the purposes of cloning.
   120  	checkClonedIsEquivalent(t, fs, "")
   121  	checkClonedIsEquivalent(t, fs, "/")
   122  }
   123  
   124  // Test that the FS can be cloned and that the clone serializes identically.
   125  func checkClonedIsEquivalent(t *testing.T, fs *MemFS, path string) {
   126  	t.Helper()
   127  	clone := NewMem()
   128  	cloned, err := Clone(fs, clone, path, path)
   129  	require.NoError(t, err)
   130  	require.True(t, cloned)
   131  	require.Equal(t, fs.String(), clone.String())
   132  }
   133  
   134  func TestBasics(t *testing.T) {
   135  	fs := NewMem()
   136  	testCases := []string{
   137  		// Create a top-level file.
   138  		"1a: create /foo",
   139  		// Create a child of that file. It should fail, since /foo is not a directory.
   140  		"2a: create /foo/x fails",
   141  		// Create a third-level file. It should fail, since /bar has not been created.
   142  		// Similarly, opening that file should fail.
   143  		"3a: create /bar/baz/y fails",
   144  		"3b: open /bar/baz/y fails",
   145  		// Make the /bar/baz directory; create a third-level file. Creation should now succeed.
   146  		"4a: mkdirall /bar/baz",
   147  		"4b: f = create /bar/baz/y",
   148  		"4c: f.stat.name == y",
   149  		// Write some data; read it back.
   150  		"5a: f.write abcde",
   151  		"5b: f.close",
   152  		"5c: f = open /bar/baz/y",
   153  		"5d: f.read 5 == abcde",
   154  		"5e: f.readat 2 1 == bc",
   155  		"5f: f.close",
   156  		// Link /bar/baz/y to /bar/baz/z. We should be able to read from both files
   157  		// and remove them independently.
   158  		"6a: link /bar/baz/y /bar/baz/z",
   159  		"6b: f = open /bar/baz/z",
   160  		"6c: f.read 5 == abcde",
   161  		"6d: f.close",
   162  		"6e: remove /bar/baz/z",
   163  		"6f: f = open /bar/baz/y",
   164  		"6g: f.read 5 == abcde",
   165  		"6h: f.close",
   166  		// Remove the file twice. The first should succeed, the second should fail.
   167  		"7a: remove /bar/baz/y",
   168  		"7b: remove /bar/baz/y fails",
   169  		"7c: open /bar/baz/y fails",
   170  		// Rename /foo to /goo. Trying to open /foo should succeed before the rename and
   171  		// fail afterwards, and vice versa for /goo.
   172  		"8a: open /foo",
   173  		"8b: open /goo fails",
   174  		"8c: rename /foo /goo",
   175  		"8d: open /foo fails",
   176  		"8e: open /goo",
   177  		// Create /bar/baz/z and rename /bar/baz to /bar/caz.
   178  		"9a: create /bar/baz/z",
   179  		"9b: open /bar/baz/z",
   180  		"9c: open /bar/caz/z fails",
   181  		"9d: rename /bar/baz /bar/caz",
   182  		"9e: open /bar/baz/z fails",
   183  		"9f: open /bar/caz/z",
   184  		// ReuseForWrite
   185  		"10a: reuseForWrite /bar/caz/z /bar/z",
   186  		"10b: open /bar/caz/z fails",
   187  		"10c: open /bar/z",
   188  		// Opening the root directory works.
   189  		"11a: f = open /",
   190  		"11b: f.stat.name == /",
   191  	}
   192  	runTestCases(t, testCases, fs)
   193  }
   194  
   195  func TestList(t *testing.T) {
   196  	fs := NewMem()
   197  
   198  	dirnames := []string{
   199  		"/bar",
   200  		"/foo/2",
   201  	}
   202  	for _, dirname := range dirnames {
   203  		err := fs.MkdirAll(dirname, 0755)
   204  		if err != nil {
   205  			t.Fatalf("MkdirAll %q: %v", dirname, err)
   206  		}
   207  	}
   208  
   209  	filenames := []string{
   210  		"/a",
   211  		"/bar/baz",
   212  		"/foo/0",
   213  		"/foo/1",
   214  		"/foo/2/a",
   215  		"/foo/2/b",
   216  		"/foo/3",
   217  		"/foot",
   218  	}
   219  	for _, filename := range filenames {
   220  		f, err := fs.Create(filename)
   221  		if err != nil {
   222  			t.Fatalf("Create %q: %v", filename, err)
   223  		}
   224  		if err := f.Close(); err != nil {
   225  			t.Fatalf("Close %q: %v", filename, err)
   226  		}
   227  	}
   228  
   229  	{
   230  		got := fs.String()
   231  		const want = `          /
   232         0    a
   233              bar/
   234         0      baz
   235              foo/
   236         0      0
   237         0      1
   238                2/
   239         0        a
   240         0        b
   241         0      3
   242         0    foot
   243  `
   244  		if got != want {
   245  			t.Fatalf("String:\n----got----\n%s----want----\n%s", got, want)
   246  		}
   247  	}
   248  
   249  	testCases := []string{
   250  		"/:a bar foo foot",
   251  		"/bar:baz",
   252  		"/bar/:baz",
   253  		"/baz:",
   254  		"/baz/:",
   255  		"/foo:0 1 2 3",
   256  		"/foo/:0 1 2 3",
   257  		"/foo/1:",
   258  		"/foo/1/:",
   259  		"/foo/2:a b",
   260  		"/foo/2/:a b",
   261  		"/foot:",
   262  		"/foot/:",
   263  	}
   264  	for _, tc := range testCases {
   265  		s := strings.Split(tc, ":")
   266  		list, _ := fs.List(s[0])
   267  		sort.Strings(list)
   268  		got := strings.Join(list, " ")
   269  		want := s[1]
   270  		if got != want {
   271  			t.Errorf("List %q: got %q, want %q", s[0], got, want)
   272  		}
   273  	}
   274  }
   275  
   276  func TestMemFile(t *testing.T) {
   277  	want := "foo"
   278  	f := NewMemFile([]byte(want))
   279  	buf, err := ioutil.ReadAll(f)
   280  	if err != nil {
   281  		t.Fatalf("%v", err)
   282  	}
   283  	if got := string(buf); got != want {
   284  		t.Fatalf("got %q, want %q", got, want)
   285  	}
   286  }
   287  
   288  func TestStrictFS(t *testing.T) {
   289  	fs := NewStrictMem()
   290  	testCases := []string{
   291  		// Created file disappears if directory is not synced.
   292  		"1a: create /foo",
   293  		"1b: open /foo",
   294  		"1c: resetToSynced",
   295  		"1d: open /foo fails",
   296  
   297  		// Create directory and a file in it and write and read from it.
   298  		"2a: mkdirall /bar",
   299  		"2b: f = create /bar/y",
   300  		"2c: f.stat.name == y",
   301  		// Write some data; read it back.
   302  		"2d: f.write abcde",
   303  		"2e: f.close",
   304  		"2f: f = open /bar/y",
   305  		"2g: f.read 5 == abcde",
   306  		"2h: f.close",
   307  		"2i: open /bar",
   308  
   309  		// Resetting causes both the directory and file to disappear.
   310  		"3a: resetToSynced",
   311  		"3b: openDir /bar fails",
   312  		"3c: open /bar/y fails",
   313  
   314  		// Create the directory and file again. Link the file to another file in the same dir,
   315  		// and to a file in the root dir. Sync the root dir. After reset, the created dir and the
   316  		// file in the root dir are the only ones visible.
   317  		"4a: mkdirall /bar",
   318  		"4b: create /bar/y",
   319  		"4c: f = openDir /",
   320  		"4d: f.sync",
   321  		"4e: f.close",
   322  		"4f: link /bar/y /bar/z",
   323  		"4g: link /bar/y /z",
   324  		"4h: f = openDir /",
   325  		"4i: f.sync",
   326  		"4j: f.close",
   327  		"4k: resetToSynced",
   328  		"4l: openDir /bar",
   329  		"4m: open /bar/y fails",
   330  		"4n: open /bar/z fails",
   331  		"4o: open /z",
   332  
   333  		// Create the file in the directory again and this time sync /bar directory. The file is
   334  		// preserved after reset.
   335  		"5a: create /bar/y",
   336  		"5b: f = openDir /bar",
   337  		"5c: f.sync",
   338  		"5d: f.close",
   339  		"5e: resetToSynced",
   340  		"5f: openDir /bar",
   341  		"5g: open /bar/y",
   342  
   343  		// Unsynced data in the file is lost on reset.
   344  		"5a: f = create /bar/y",
   345  		"5b: f.write a",
   346  		"5c: f.sync",
   347  		"5d: f.write b",
   348  		"5e: f.close",
   349  		"5f: f = openDir /bar",
   350  		"5g: f.sync",
   351  		"5h: f.close",
   352  		"5i: resetToSynced",
   353  		"5j: f = open /bar/y",
   354  		"5k: f.read 1 = a",
   355  		"5l: f.read 1 fails",
   356  		"5m: f.close",
   357  
   358  		// reuseForWrite works correctly in strict mode in that unsynced data does not overwrite
   359  		// previous contents when a reset happens.
   360  		"6a: f = create /z",
   361  		"6b: f.write abcdefgh",
   362  		"6c: f.sync",
   363  		"6d: f.close",
   364  		"6e: f = reuseForWrite /z /y",
   365  		"6f: f.write x",
   366  		"6g: f.sync",
   367  		"6h: f.write y", // will be lost
   368  		"6i: f.close",
   369  		"6j: f = openDir /",
   370  		"6k: f.sync",
   371  		"6l: f.close",
   372  		"6m: resetToSynced",
   373  		"6n: f = open /y",
   374  		"6o: f.read 8 = xbcdefgh",
   375  		"6p: f.close",
   376  
   377  		// Ignore syncs.
   378  		"7a: f = create /z",
   379  		"7b: f.write a",
   380  		"7c: f.sync",
   381  		"7d: ignoreSyncs",
   382  		"7e: f.write b",
   383  		"7f: f.sync",
   384  		"7g: f.close",
   385  		"7h: stopIgnoringSyncs",
   386  		"7e: f = openDir /",
   387  		"7f: f.sync",
   388  		"7g: f.close",
   389  		"7h: resetToSynced",
   390  		"7i: f = open /z",
   391  		"7j: f.read 1 = a",
   392  		"7k: f.read 1 fails",
   393  		"7l: f.close",
   394  	}
   395  	runTestCases(t, testCases, fs)
   396  }