From 639f2b4d794ce848050c483fd21d773ea3ebe7c4 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sat, 22 Feb 2025 16:29:49 +0100 Subject: [PATCH 01/10] feat: Add support showing size of absent git-annex'ed files git-annex encodes the apparent size of files their symlinks. gdu now is capable of extracting those sizes from the broken symlinks to calculate the total size of git-annex repositories. Note: real usage remains zero. gdu needs to be invoked via: gdu --follow-symlinks --show-apparent-size --show-annexed-size --- cmd/gdu/app/app.go | 5 +++ cmd/gdu/main.go | 4 +++ internal/common/analyze.go | 1 + internal/common/ui.go | 5 +++ internal/common/ui_test.go | 17 +++++++++- pkg/analyze/parallel.go | 23 ++++---------- pkg/analyze/sequential.go | 8 ++++- pkg/analyze/stored.go | 5 +++ pkg/analyze/symlink.go | 39 +++++++++++++++++++++++ pkg/annex/annex.go | 65 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 154 insertions(+), 18 deletions(-) create mode 100644 pkg/analyze/symlink.go create mode 100644 pkg/annex/annex.go diff --git a/cmd/gdu/app/app.go b/cmd/gdu/app/app.go index 135a4a7b3..7797b95bd 100644 --- a/cmd/gdu/app/app.go +++ b/cmd/gdu/app/app.go @@ -36,6 +36,7 @@ type UI interface { SetIgnoreFromFile(ignoreFile string) error SetIgnoreHidden(value bool) SetFollowSymlinks(value bool) + SetShowAnnexedSize(value bool) SetAnalyzer(analyzer common.Analyzer) StartUILoop() error } @@ -54,6 +55,7 @@ type Flags struct { ShowDisks bool `yaml:"-"` ShowApparentSize bool `yaml:"show-apparent-size"` ShowRelativeSize bool `yaml:"show-relative-size"` + ShowAnnexedSize bool `yaml:"show-annexed-size"` ShowVersion bool `yaml:"-"` ShowItemCount bool `yaml:"show-item-count"` ShowMTime bool `yaml:"show-mtime"` @@ -183,6 +185,9 @@ func (a *App) Run() error { if a.Flags.FollowSymlinks { ui.SetFollowSymlinks(true) } + if a.Flags.ShowAnnexedSize { + ui.SetShowAnnexedSize(true) + } if err := a.setNoCross(path); err != nil { return err } diff --git a/cmd/gdu/main.go b/cmd/gdu/main.go index b165f9212..6190a6263 100644 --- a/cmd/gdu/main.go +++ b/cmd/gdu/main.go @@ -59,6 +59,10 @@ func init() { &af.FollowSymlinks, "follow-symlinks", "L", false, "Follow symlinks for files, i.e. show the size of the file to which symlink points to (symlinks to directories are not followed)", ) + flags.BoolVarP( + &af.ShowAnnexedSize, "show-annexed-size", "A", false, + "Use apparent size of git-annex'ed files in case files are not present locally (real usage is zero)", + ) flags.BoolVarP(&af.NoCross, "no-cross", "x", false, "Do not cross filesystem boundaries") flags.BoolVarP(&af.ConstGC, "const-gc", "g", false, "Enable memory garbage collection during analysis with constant level set by GOGC") flags.BoolVar(&af.Profiling, "enable-profiling", false, "Enable collection of profiling data and provide it on http://localhost:6060/debug/pprof/") diff --git a/internal/common/analyze.go b/internal/common/analyze.go index bb08b3c10..bef4a4fee 100644 --- a/internal/common/analyze.go +++ b/internal/common/analyze.go @@ -16,6 +16,7 @@ type ShouldDirBeIgnored func(name, path string) bool type Analyzer interface { AnalyzeDir(path string, ignore ShouldDirBeIgnored, constGC bool) fs.Item SetFollowSymlinks(bool) + SetShowAnnexedSize(bool) GetProgressChan() chan CurrentProgress GetDone() SignalGroup ResetProgress() diff --git a/internal/common/ui.go b/internal/common/ui.go index 2caefd3b1..8f9303951 100644 --- a/internal/common/ui.go +++ b/internal/common/ui.go @@ -29,6 +29,11 @@ func (ui *UI) SetFollowSymlinks(v bool) { ui.Analyzer.SetFollowSymlinks(v) } +// SetShowAnnexedSize sets whether to use annexed size of git-annex files +func (ui *UI) SetShowAnnexedSize(v bool) { + ui.Analyzer.SetShowAnnexedSize(v) +} + // binary multiplies prefixes (IEC) const ( _ float64 = 1 << (10 * iota) diff --git a/internal/common/ui_test.go b/internal/common/ui_test.go index e669a61df..c0ff88a2e 100644 --- a/internal/common/ui_test.go +++ b/internal/common/ui_test.go @@ -21,8 +21,18 @@ func TestSetFollowSymlinks(t *testing.T) { assert.Equal(t, true, ui.Analyzer.(*MockedAnalyzer).FollowSymlinks) } +func TestSetShowAnnexedSize(t *testing.T) { + ui := UI{ + Analyzer: &MockedAnalyzer{}, + } + ui.SetShowAnnexedSize(true) + + assert.Equal(t, true, ui.Analyzer.(*MockedAnalyzer).ShowAnnexedSize) +} + type MockedAnalyzer struct { - FollowSymlinks bool + FollowSymlinks bool + ShowAnnexedSize bool } // AnalyzeDir returns dir with files with different size exponents @@ -51,3 +61,8 @@ func (a *MockedAnalyzer) ResetProgress() {} func (a *MockedAnalyzer) SetFollowSymlinks(v bool) { a.FollowSymlinks = v } + +// SetShowAnnexedSize does nothing +func (a *MockedAnalyzer) SetShowAnnexedSize(v bool) { + a.ShowAnnexedSize = v +} diff --git a/pkg/analyze/parallel.go b/pkg/analyze/parallel.go index 7fa517a6c..62dd9f653 100644 --- a/pkg/analyze/parallel.go +++ b/pkg/analyze/parallel.go @@ -23,6 +23,7 @@ type ParallelAnalyzer struct { wait *WaitGroup ignoreDir common.ShouldDirBeIgnored followSymlinks bool + gitAnnexedSize bool } // CreateAnalyzer returns Analyzer @@ -45,6 +46,11 @@ func (a *ParallelAnalyzer) SetFollowSymlinks(v bool) { a.followSymlinks = v } +// SetShowAnnexedSize sets whether to use annexed size of git-annex files +func (a *ParallelAnalyzer) SetShowAnnexedSize(v bool) { + a.gitAnnexedSize = v +} + // GetProgressChan returns channel for getting progress func (a *ParallelAnalyzer) GetProgressChan() chan common.CurrentProgress { return a.progressOutChan @@ -140,7 +146,7 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir { continue } if a.followSymlinks && info.Mode()&os.ModeSymlink != 0 { - infoF, err := followSymlink(entryPath) + infoF, err := followSymlink(entryPath, a.gitAnnexedSize) if err != nil { log.Print(err.Error()) dir.Flag = '!' @@ -219,18 +225,3 @@ func getFlag(f os.FileInfo) rune { } return ' ' } - -func followSymlink(path string) (os.FileInfo, error) { - target, err := filepath.EvalSymlinks(path) - if err != nil { - return nil, err - } - tInfo, err := os.Lstat(target) - if err != nil { - return nil, err - } - if !tInfo.IsDir() { - return tInfo, nil - } - return nil, nil -} diff --git a/pkg/analyze/sequential.go b/pkg/analyze/sequential.go index 507ebfd98..acbdb13bd 100644 --- a/pkg/analyze/sequential.go +++ b/pkg/analyze/sequential.go @@ -20,6 +20,7 @@ type SequentialAnalyzer struct { wait *WaitGroup ignoreDir common.ShouldDirBeIgnored followSymlinks bool + gitAnnexedSize bool } // CreateSeqAnalyzer returns Analyzer @@ -42,6 +43,11 @@ func (a *SequentialAnalyzer) SetFollowSymlinks(v bool) { a.followSymlinks = v } +// SetShowAnnexedSize sets whether to use annexed size of git-annex files +func (a *SequentialAnalyzer) SetShowAnnexedSize(v bool) { + a.gitAnnexedSize = v +} + // GetProgressChan returns channel for getting progress func (a *SequentialAnalyzer) GetProgressChan() chan common.CurrentProgress { return a.progressOutChan @@ -127,7 +133,7 @@ func (a *SequentialAnalyzer) processDir(path string) *Dir { continue } if a.followSymlinks && info.Mode()&os.ModeSymlink != 0 { - infoF, err := followSymlink(entryPath) + infoF, err := followSymlink(entryPath, a.gitAnnexedSize) if err != nil { log.Print(err.Error()) dir.Flag = '!' diff --git a/pkg/analyze/stored.go b/pkg/analyze/stored.go index 2f9fde595..3b706c347 100644 --- a/pkg/analyze/stored.go +++ b/pkg/analyze/stored.go @@ -25,6 +25,7 @@ type StoredAnalyzer struct { wait *WaitGroup ignoreDir common.ShouldDirBeIgnored followSymlinks bool + gitAnnexedSize bool } // CreateStoredAnalyzer returns Analyzer @@ -57,6 +58,10 @@ func (a *StoredAnalyzer) SetFollowSymlinks(v bool) { a.followSymlinks = v } +func (a *StoredAnalyzer) SetShowAnnexedSize(v bool) { + a.gitAnnexedSize = v +} + // ResetProgress returns progress func (a *StoredAnalyzer) ResetProgress() { a.progress = &common.CurrentProgress{} diff --git a/pkg/analyze/symlink.go b/pkg/analyze/symlink.go new file mode 100644 index 000000000..531290c3f --- /dev/null +++ b/pkg/analyze/symlink.go @@ -0,0 +1,39 @@ +package analyze + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/dundee/gdu/v5/pkg/annex" +) + +func followSymlink(path string, gitAnnexedSize bool) (tInfo os.FileInfo, err error) { + target, err := os.Readlink(path) + if err != nil { + fmt.Println(path, err) + return nil, err + } + + tInfo, err = os.Lstat(target) + if err != nil { + if os.IsNotExist(err) && gitAnnexedSize && strings.Contains(target, ".git/annex/objects") { + tInfo, err = os.Lstat(path) + if err != nil { + return nil, err + } + + name := filepath.Base(target) + tInfo = annex.AnnexedFileInfo(tInfo, name) + } else { + return nil, err + } + } + + if tInfo.IsDir() { + return nil, nil + } + + return tInfo, nil +} diff --git a/pkg/annex/annex.go b/pkg/annex/annex.go new file mode 100644 index 000000000..67ce41087 --- /dev/null +++ b/pkg/annex/annex.go @@ -0,0 +1,65 @@ +package annex + +import ( + "fmt" + "io/fs" + "log" + "strconv" + "strings" +) + +// SizeFromKey returns size from git-annex key. +func SizeFromKey(name string) (int64, error) { + nameParts := strings.SplitN(name, "--", 2) + backendKVs := nameParts[0] + backendKVParts := strings.Split(backendKVs, "-") + + if len(backendKVParts) < 2 { + return 0, fmt.Errorf("key is is missing backend") + } + + for _, p := range backendKVParts[1:] { + if len(p) == 0 || p[0] != 's' { + continue + } + + size, err := strconv.ParseInt(p[1:], 10, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse size: %w", err) + } + + return size, nil + } + + return 0, fmt.Errorf("size not found in key") +} + +// AnnexedFileInfo returns a new FileInfo with size from git-annex key. +func AnnexedFileInfo(fi fs.FileInfo, name string) *FileInfo { + size, err := SizeFromKey(name) + if err != nil { + log.Print(err.Error()) + return &FileInfo{FileInfo: fi} + } + + afi := &FileInfo{ + FileInfo: fi, + size: size, + } + + return afi +} + +var _ fs.FileInfo = (*FileInfo)(nil) + +// FileInfo is a wrapper around fs.FileInfo to overwrite the size. +type FileInfo struct { + fs.FileInfo + + size int64 +} + +// Length in bytes for regular files; system-dependent for others +func (fi *FileInfo) Size() int64 { + return int64(fi.size) +} From b55373a33df508de92c11408be5343a08a9ca140 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Fri, 28 Feb 2025 17:34:07 +0100 Subject: [PATCH 02/10] test: fix mock --- internal/testanalyze/analyze.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/testanalyze/analyze.go b/internal/testanalyze/analyze.go index 22a8f9ca1..0f72aae78 100644 --- a/internal/testanalyze/analyze.go +++ b/internal/testanalyze/analyze.go @@ -84,6 +84,9 @@ func (a *MockedAnalyzer) ResetProgress() {} // SetFollowSymlinks does nothing func (a *MockedAnalyzer) SetFollowSymlinks(v bool) {} +// SetShowAnnexedSize does nothing +func (a *MockedAnalyzer) SetShowAnnexedSize(v bool) {} + // ItemFromDirWithErr returns error func ItemFromDirWithErr(dir, file fs.Item) error { return errors.New("Failed") From 9ab889d7fb4b7d3327d916289b05fda863366900 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Fri, 28 Feb 2025 17:48:43 +0100 Subject: [PATCH 03/10] fix: lint check --- pkg/annex/annex.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/annex/annex.go b/pkg/annex/annex.go index 67ce41087..ceda8478f 100644 --- a/pkg/annex/annex.go +++ b/pkg/annex/annex.go @@ -19,7 +19,7 @@ func SizeFromKey(name string) (int64, error) { } for _, p := range backendKVParts[1:] { - if len(p) == 0 || p[0] != 's' { + if p == "" || p[0] != 's' { continue } From 90399bff1be355958e371ebdfd122be6e03390c9 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Fri, 28 Feb 2025 17:49:02 +0100 Subject: [PATCH 04/10] fix: use EvalSymlinks to get proper path --- pkg/analyze/symlink.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/analyze/symlink.go b/pkg/analyze/symlink.go index 531290c3f..6365075a5 100644 --- a/pkg/analyze/symlink.go +++ b/pkg/analyze/symlink.go @@ -1,7 +1,6 @@ package analyze import ( - "fmt" "os" "path/filepath" "strings" @@ -10,9 +9,8 @@ import ( ) func followSymlink(path string, gitAnnexedSize bool) (tInfo os.FileInfo, err error) { - target, err := os.Readlink(path) + target, err := filepath.EvalSymlinks(path) if err != nil { - fmt.Println(path, err) return nil, err } From 68e146d57cb8e81ce1716bfd10d8b5e75c9ed028 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Fri, 21 Mar 2025 22:40:24 +0100 Subject: [PATCH 05/10] fix: use both EvalSymlinks and Readlink --- pkg/analyze/symlink.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pkg/analyze/symlink.go b/pkg/analyze/symlink.go index 6365075a5..ff659e0f3 100644 --- a/pkg/analyze/symlink.go +++ b/pkg/analyze/symlink.go @@ -11,12 +11,11 @@ import ( func followSymlink(path string, gitAnnexedSize bool) (tInfo os.FileInfo, err error) { target, err := filepath.EvalSymlinks(path) if err != nil { - return nil, err - } - - tInfo, err = os.Lstat(target) - if err != nil { - if os.IsNotExist(err) && gitAnnexedSize && strings.Contains(target, ".git/annex/objects") { + target, err := os.Readlink(path) + if err != nil { + return nil, err + } + if gitAnnexedSize && strings.Contains(target, ".git/annex/objects") { tInfo, err = os.Lstat(path) if err != nil { return nil, err @@ -24,11 +23,15 @@ func followSymlink(path string, gitAnnexedSize bool) (tInfo os.FileInfo, err err name := filepath.Base(target) tInfo = annex.AnnexedFileInfo(tInfo, name) - } else { - return nil, err + return tInfo, nil } } + tInfo, err = os.Lstat(target) + if err != nil { + return nil, err + } + if tInfo.IsDir() { return nil, nil } From e728a0c139a9a4edf48d438b54eeaf3d07d85070 Mon Sep 17 00:00:00 2001 From: Steffen Vogel Date: Sat, 22 Mar 2025 11:15:25 +0100 Subject: [PATCH 06/10] fix: Do not shadow variable --- pkg/analyze/symlink.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/analyze/symlink.go b/pkg/analyze/symlink.go index ff659e0f3..5d7abefcc 100644 --- a/pkg/analyze/symlink.go +++ b/pkg/analyze/symlink.go @@ -11,7 +11,7 @@ import ( func followSymlink(path string, gitAnnexedSize bool) (tInfo os.FileInfo, err error) { target, err := filepath.EvalSymlinks(path) if err != nil { - target, err := os.Readlink(path) + target, err = os.Readlink(path) if err != nil { return nil, err } From 80933b6d79ca4f187368b53cc97f84154d6813a8 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 25 Mar 2025 11:22:23 +0100 Subject: [PATCH 07/10] test: cover annex logic --- pkg/analyze/dir_test.go | 37 +++++++++++++++++++++++++++++++++++++ pkg/annex/annex_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 pkg/annex/annex_test.go diff --git a/pkg/analyze/dir_test.go b/pkg/analyze/dir_test.go index f4de59191..bd8390bff 100644 --- a/pkg/analyze/dir_test.go +++ b/pkg/analyze/dir_test.go @@ -165,6 +165,43 @@ func TestFollowSymlink(t *testing.T) { assert.Equal(t, 'e', dir.Files[1].GetFlag()) } +func TestGitAnnexSymlink(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + + err := os.Mkdir("test_dir/empty", 0o644) + assert.Nil(t, err) + + err = os.Symlink( + ".git/annex/objects/qx/qX/SHA256E-s967858083--"+ + "3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4/SHA256E-s967858083--"+ + "3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4", + "test_dir/nested/file3") + assert.Nil(t, err) + + analyzer := CreateAnalyzer() + analyzer.SetFollowSymlinks(true) + analyzer.SetShowAnnexedSize(true) + dir := analyzer.AnalyzeDir( + "test_dir", func(_, _ string) bool { return false }, false, + ).(*Dir) + analyzer.GetDone().Wait() + dir.UpdateStats(make(fs.HardLinkedItems)) + + sort.Sort(sort.Reverse(dir.Files)) + + assert.Equal(t, int64(967858083+7+4096*4), dir.Size) + assert.Equal(t, 7, dir.ItemCount) + + // test file3 + assert.Equal(t, "nested", dir.Files[0].GetName()) + assert.Equal(t, "file3", dir.Files[0].(*Dir).Files[1].GetName()) + assert.Equal(t, int64(967858083), dir.Files[0].(*Dir).Files[1].GetSize()) + assert.Equal(t, '@', dir.Files[0].(*Dir).Files[1].GetFlag()) + + assert.Equal(t, 'e', dir.Files[1].GetFlag()) +} + func TestBrokenSymlinkSkipped(t *testing.T) { fin := testdir.CreateTestDir() defer fin() diff --git a/pkg/annex/annex_test.go b/pkg/annex/annex_test.go new file mode 100644 index 000000000..5b8c0726b --- /dev/null +++ b/pkg/annex/annex_test.go @@ -0,0 +1,39 @@ +package annex + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestAnnexedFileInfo(t *testing.T) { + fi := &FileInfo{} + fi = AnnexedFileInfo(fi, "SHA256E-s967858083--3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4") + + assert.Equal(t, int64(967858083), fi.Size()) +} + +func TestAnnexedFileInfoErr(t *testing.T) { + fi := &FileInfo{} + fi = AnnexedFileInfo(fi, "xxx") + + assert.Equal(t, int64(0), fi.Size()) +} + +func TestSizeFromKeyErr(t *testing.T) { + _, err := SizeFromKey("xxx") + assert.Error(t, err) + assert.ErrorContains(t, err, "key is is missing backend") + + _, err = SizeFromKey("SHA256E-sXXX--3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4") + assert.Error(t, err) + assert.ErrorContains(t, err, "failed to parse size") + + _, err = SizeFromKey("SHA256E-s--3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4") + assert.Error(t, err) + assert.ErrorContains(t, err, "failed to parse size") + + _, err = SizeFromKey("SHA256E-a-b-c--3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4") + assert.Error(t, err) + assert.ErrorContains(t, err, "size not found in key") +} From 9e28b4b8d506a23b19adce5ec5c1e51445c5fd17 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 25 Mar 2025 11:39:09 +0100 Subject: [PATCH 08/10] test: cover follow symlink errors --- pkg/analyze/symlink_test.go | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 pkg/analyze/symlink_test.go diff --git a/pkg/analyze/symlink_test.go b/pkg/analyze/symlink_test.go new file mode 100644 index 000000000..1c2c33533 --- /dev/null +++ b/pkg/analyze/symlink_test.go @@ -0,0 +1,42 @@ +package analyze + +import ( + "os" + "testing" + + "github.com/dundee/gdu/v5/internal/testdir" + "github.com/stretchr/testify/assert" +) + +func TestFollowSymlinkErr(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + + err := os.Mkdir("test_dir/empty", 0o644) + assert.Nil(t, err) + + err = os.Symlink( + ".git/annex/objects/qx/qX/SHA256E-s967858083--"+ + "3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4/SHA256E-s967858083--"+ + "3e54803fded8dc3a9ea68b106f7b51e04e33c79b4a7b32a860f0b22d89af5c65.mp4", + "test_dir/nested/file3") + assert.Nil(t, err) + + err = os.Symlink( + "test_dir/nested", + "test_dir/some_dir") + assert.Nil(t, err) + + _, err = followSymlink("xxx", false) + assert.ErrorContains(t, err, "no such file or directory") + + _, err = followSymlink("test_dir/nested/file3", false) + assert.ErrorContains(t, err, "no such file or directory") + + _, err = followSymlink("test_dir/nested/file3", true) + assert.NoError(t, err) + + res, err := followSymlink("some_dir", true) + assert.Equal(t, nil, res) + assert.NoError(t, err) +} From 917a6ecc16f07d942ea26725c82d28a11e6353cd Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 25 Mar 2025 11:41:14 +0100 Subject: [PATCH 09/10] test: fix --- pkg/analyze/symlink_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/analyze/symlink_test.go b/pkg/analyze/symlink_test.go index 1c2c33533..77033655f 100644 --- a/pkg/analyze/symlink_test.go +++ b/pkg/analyze/symlink_test.go @@ -36,7 +36,7 @@ func TestFollowSymlinkErr(t *testing.T) { _, err = followSymlink("test_dir/nested/file3", true) assert.NoError(t, err) - res, err := followSymlink("some_dir", true) + res, err := followSymlink("test_dir/some_dir", true) assert.Equal(t, nil, res) assert.NoError(t, err) } From 13bc733ca69501ffd898313510455856b78b2169 Mon Sep 17 00:00:00 2001 From: Daniel Milde Date: Tue, 25 Mar 2025 11:46:18 +0100 Subject: [PATCH 10/10] test: cover app --- cmd/gdu/app/app_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/gdu/app/app_test.go b/cmd/gdu/app/app_test.go index 377a140f4..aa743371d 100644 --- a/cmd/gdu/app/app_test.go +++ b/cmd/gdu/app/app_test.go @@ -77,6 +77,21 @@ func TestFollowSymlinks(t *testing.T) { assert.Nil(t, err) } +func TestShowAnnexedSize(t *testing.T) { + fin := testdir.CreateTestDir() + defer fin() + + out, err := runApp( + &Flags{LogFile: "/dev/null", ShowAnnexedSize: true}, + []string{"test_dir"}, + false, + testdev.DevicesInfoGetterMock{}, + ) + + assert.Contains(t, out, "nested") + assert.Nil(t, err) +} + func TestAnalyzePathProfiling(t *testing.T) { fin := testdir.CreateTestDir() defer fin()