github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libgit/browser_test.go (about) 1 // Copyright 2018 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libgit 6 7 import ( 8 "io" 9 "os" 10 "path" 11 "testing" 12 "time" 13 14 "github.com/keybase/client/go/kbfs/data" 15 "github.com/keybase/client/go/kbfs/libfs" 16 "github.com/keybase/client/go/kbfs/libkbfs" 17 "github.com/keybase/client/go/kbfs/tlf" 18 "github.com/keybase/client/go/kbfs/tlfhandle" 19 "github.com/keybase/client/go/protocol/keybase1" 20 "github.com/stretchr/testify/require" 21 gogit "gopkg.in/src-d/go-git.v4" 22 "gopkg.in/src-d/go-git.v4/plumbing/object" 23 ) 24 25 func testBrowser(t *testing.T, sharedCache sharedInBrowserCache) { 26 ctx, config, cancel, tempdir := initConfigForAutogit(t) 27 defer cancel() 28 defer os.RemoveAll(tempdir) 29 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 30 31 h, err := tlfhandle.ParseHandle( 32 ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private) 33 require.NoError(t, err) 34 rootFS, err := libfs.NewFS( 35 ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal) 36 require.NoError(t, err) 37 38 t.Log("Init a new repo directly into KBFS.") 39 dotgitFS, _, err := GetOrCreateRepoAndID(ctx, config, h, "test", "") 40 require.NoError(t, err) 41 42 t.Log("Check that the browser for an uninitialized repo works.") 43 b, err := NewBrowser(dotgitFS, config.Clock(), "", sharedCache) 44 require.NoError(t, err) 45 fis, err := b.ReadDir("") 46 require.NoError(t, err) 47 require.Len(t, fis, 0) 48 49 err = rootFS.MkdirAll("worktree", 0600) 50 require.NoError(t, err) 51 worktreeFS, err := rootFS.Chroot("worktree") 52 require.NoError(t, err) 53 dotgitStorage, err := NewGitConfigWithoutRemotesStorer(dotgitFS) 54 require.NoError(t, err) 55 repo, err := gogit.Init(dotgitStorage, worktreeFS) 56 require.NoError(t, err) 57 58 t.Log("Check that the browser for an empty master branch works.") 59 b, err = NewBrowser(dotgitFS, config.Clock(), "", sharedCache) 60 require.NoError(t, err) 61 fis, err = b.ReadDir("") 62 require.NoError(t, err) 63 require.Len(t, fis, 0) 64 65 addFileToWorktreeAndCommit( 66 ctx, t, config, h, repo, worktreeFS, "foo", "hello") 67 addFileToWorktreeAndCommit( 68 ctx, t, config, h, repo, worktreeFS, "dir/foo", "olleh") 69 70 t.Log("Browse the repo and verify the data.") 71 b, err = NewBrowser(dotgitFS, config.Clock(), "", sharedCache) 72 require.NoError(t, err) 73 74 if sharedCache != (noopSharedInBrowserCache{}) { 75 t.Log("Before anything, cache should be empty") 76 _, ok := sharedCache.getFileInfo(b.commitHash, path.Join(b.root, "foo")) 77 require.False(t, ok) 78 } 79 80 fi, err := b.Stat("foo") 81 require.NoError(t, err) 82 require.Equal(t, "foo", fi.Name()) 83 84 if sharedCache != (noopSharedInBrowserCache{}) { 85 t.Log("After a Stat call, make sure cache is populated for foo") 86 fi, ok := sharedCache.getFileInfo( 87 b.commitHash, path.Join(b.root, "foo")) 88 require.True(t, ok) 89 require.Equal(t, "foo", fi.Name()) 90 } 91 92 t.Log("Verify the data in foo.") 93 f, err := b.Open("foo") 94 require.NoError(t, err) 95 defer f.Close() 96 data, err := io.ReadAll(f) 97 require.NoError(t, err) 98 require.Equal(t, "hello", string(data)) 99 100 fis, err = b.ReadDir("dir") 101 require.NoError(t, err) 102 require.Len(t, fis, 1) 103 104 if sharedCache != (noopSharedInBrowserCache{}) { 105 t.Logf("After a ReadDir, " + 106 "make sure cache is populated for dir and dir/foo") 107 childrenPaths, ok := sharedCache.getChildrenFileInfos( 108 b.commitHash, path.Join(b.root, "dir")) 109 require.True(t, ok) 110 require.Len(t, childrenPaths, 1) 111 require.Equal(t, "foo", childrenPaths[0].Name()) 112 fi, ok := sharedCache.getFileInfo( 113 b.commitHash, path.Join(b.root, "dir", "foo")) 114 require.True(t, ok) 115 require.Equal(t, "foo", fi.Name()) 116 } 117 118 t.Log("Use ReadAt with a small buffer.") 119 bf, ok := f.(*browserFile) 120 require.True(t, ok) 121 bf.maxBufSize = 1 122 buf := make([]byte, 3) 123 n, err := f.ReadAt(buf, 2) 124 require.NoError(t, err) 125 require.Equal(t, 3, n) 126 require.Equal(t, "llo", string(buf)) 127 128 addSymlink := func(target, link string) { 129 err = worktreeFS.Symlink(target, link) 130 require.NoError(t, err) 131 wt, err := repo.Worktree() 132 require.NoError(t, err) 133 _, err = wt.Add(link) 134 require.NoError(t, err) 135 _, err = wt.Commit("sym commit", &gogit.CommitOptions{ 136 Author: &object.Signature{ 137 Name: "me", 138 Email: "me@keyba.se", 139 When: time.Now(), 140 }, 141 }) 142 require.NoError(t, err) 143 b, err = NewBrowser(dotgitFS, config.Clock(), "", sharedCache) 144 require.NoError(t, err) 145 fi, err := b.Lstat(link) 146 require.NoError(t, err) 147 require.NotZero(t, fi.Mode()&os.ModeSymlink) 148 fi, err = b.Stat(link) 149 require.NoError(t, err) 150 readTarget, err := b.Readlink(link) 151 require.NoError(t, err) 152 require.Equal(t, target, readTarget) 153 require.Zero(t, fi.Mode()&os.ModeSymlink) 154 f2, err := b.Open(link) 155 require.NoError(t, err) 156 defer f2.Close() 157 data, err = io.ReadAll(f2) 158 require.NoError(t, err) 159 require.Equal(t, "hello", string(data)) 160 } 161 t.Log("Add and read a symlink.") 162 addSymlink("foo", "symfoo") 163 164 t.Log("Add and read a second symlink in a chain.") 165 err = worktreeFS.MkdirAll("dir", 0700) 166 require.NoError(t, err) 167 addSymlink("../symfoo", "dir/symfoo") 168 } 169 170 func TestBrowserNoCache(t *testing.T) { 171 testBrowser(t, noopSharedInBrowserCache{}) 172 } 173 174 func TestBrowserWithCache(t *testing.T) { 175 cache, err := newLRUSharedInBrowserCache() 176 require.NoError(t, err) 177 testBrowser(t, cache) 178 }