Merge pull request #16 from sundowndev/feat/progressbar

Add progress bar for shredding operations
pull/17/head
Raphaël 2023-01-22 15:11:47 +04:00 committed by GitHub
commit 42e2a99adb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 161 additions and 10 deletions

View File

@ -54,7 +54,7 @@ lint:
.PHONY: install-tools
install-tools:
$(GOINSTALL) gotest.tools/gotestsum@v1.6.3
$(GOINSTALL) github.com/vektra/mockery/v2@v2.8.0
$(GOINSTALL) github.com/vektra/mockery/v2@v2.16.0
go.mod: FORCE
$(GOMOD) tidy

View File

@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"github.com/schollz/progressbar/v3"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/sundowndev/covermyass/v2/build"
@ -10,6 +11,7 @@ import (
"github.com/sundowndev/covermyass/v2/lib/output"
"github.com/sundowndev/covermyass/v2/lib/shred"
"os"
"time"
)
type RootCmdOptions struct {
@ -68,9 +70,27 @@ covermyass --write -z -n 5
a.Write(os.Stdout)
if opts.Write {
output.Printf("\n")
bar := progressbar.NewOptions64(
-1,
progressbar.OptionSetDescription("Shredding files..."),
progressbar.OptionSetWriter(output.GetPrinter()),
progressbar.OptionShowBytes(true),
progressbar.OptionSetWidth(10),
progressbar.OptionThrottle(65*time.Millisecond),
progressbar.OptionShowCount(),
progressbar.OptionOnCompletion(func() {
_, _ = fmt.Fprint(output.GetPrinter(), "\n")
}),
progressbar.OptionSpinnerType(11),
progressbar.OptionFullWidth(),
progressbar.OptionSetRenderBlankState(true),
)
shredOptions := &shred.ShredderOptions{
Zero: opts.Zero,
Iterations: opts.Iterations,
Bar: bar,
}
s := shred.New(shredOptions)
for _, result := range a.Results() {
@ -81,7 +101,8 @@ covermyass --write -z -n 5
return fmt.Errorf("error writing file %s: %s", result.Path, err)
}
}
output.Printf("\nShredded %d files %d times\n", len(a.Results()), opts.Iterations)
_ = bar.Finish()
output.Printf("\nSuccessfully shredded %d files %d times\n", len(a.Results()), opts.Iterations)
}
return nil

7
go.mod
View File

@ -4,6 +4,7 @@ go 1.18
require (
github.com/bmatcuk/doublestar/v4 v4.2.0
github.com/schollz/progressbar/v3 v3.13.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.0
github.com/stretchr/testify v1.8.1
@ -12,9 +13,13 @@ require (
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/term v0.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

18
go.sum
View File

@ -6,9 +6,20 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8=
github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
@ -19,13 +30,18 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -8,10 +8,15 @@ import (
type Printer interface {
Printf(string, ...interface{})
Println(string)
Write([]byte) (int, error)
}
var globalPrinter Printer = &VoidPrinter{}
func GetPrinter() Printer {
return globalPrinter
}
func ChangePrinter(printer Printer) {
globalPrinter = printer
}
@ -38,8 +43,16 @@ func (c *ConsolePrinter) Println(format string) {
_, _ = fmt.Fprintln(os.Stdout, format)
}
func (c *ConsolePrinter) Write(b []byte) (int, error) {
return os.Stdout.Write(b)
}
type VoidPrinter struct{}
func (v *VoidPrinter) Printf(_ string, _ ...interface{}) {}
func (v *VoidPrinter) Println(_ string) {}
func (v *VoidPrinter) Write(b []byte) (int, error) {
return 0, nil
}

View File

@ -26,9 +26,16 @@ type File interface {
Close() error
}
type ProgressBar interface {
Add(int) error
Write([]byte) (int, error)
Finish() error
}
type ShredderOptions struct {
Zero bool
Iterations int
Bar ProgressBar
}
type Shredder struct {
@ -90,6 +97,9 @@ func (s *Shredder) shred(fstat FileInfo, file File) error {
if err != nil {
return err
}
// Add written bytes to progress bar
_, _ = s.options.Bar.Write(junkBuf)
}
return nil

View File

@ -74,7 +74,7 @@ func TestShredder_shred(t *testing.T) {
cases := []struct {
name string
options ShredderOptions
mocks func(*mocks.FileInfo, *mocks.File)
mocks func(*mocks.FileInfo, *mocks.File, *mocks.ProgressBar)
wantError error
}{
{
@ -83,7 +83,7 @@ func TestShredder_shred(t *testing.T) {
Zero: false,
Iterations: 3,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
fakeFileInfo.On("Size").Return(int64(0)).Times(1)
},
},
@ -93,13 +93,17 @@ func TestShredder_shred(t *testing.T) {
Zero: false,
Iterations: 3,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
fakeFileInfo.On("Size").Return(int64(64)).Times(2)
fakeFile.On("Sync").Return(nil).Times(3)
fakeFile.On("WriteAt", mock.MatchedBy(func(b []byte) bool {
return len(b) == 64
}), int64(0)).Return(0, nil).Times(3)
fakeBar.On("Write", mock.MatchedBy(func(b []byte) bool {
return len(b) == 64
})).Return(1, nil).Times(3)
},
},
{
@ -108,13 +112,17 @@ func TestShredder_shred(t *testing.T) {
Zero: false,
Iterations: 10,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
fakeFileInfo.On("Size").Return(int64(2000000)).Times(2)
fakeFile.On("Sync").Return(nil).Times(10)
fakeFile.On("WriteAt", mock.MatchedBy(func(b []byte) bool {
return len(b) == 2000000
}), int64(0)).Return(0, nil).Times(10)
fakeBar.On("Write", mock.MatchedBy(func(b []byte) bool {
return len(b) == 2000000
})).Return(1, nil).Times(10)
},
},
{
@ -123,7 +131,7 @@ func TestShredder_shred(t *testing.T) {
Zero: false,
Iterations: 3,
},
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File) {
mocks: func(fakeFileInfo *mocks.FileInfo, fakeFile *mocks.File, fakeBar *mocks.ProgressBar) {
fakeFileInfo.On("Size").Return(int64(2000)).Times(2)
fakeFile.On("WriteAt", mock.MatchedBy(func(b []byte) bool {
@ -136,11 +144,14 @@ func TestShredder_shred(t *testing.T) {
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
fakeBar := &mocks.ProgressBar{}
tt.options.Bar = fakeBar
s := New(&tt.options)
fakeFileInfo := &mocks.FileInfo{}
fakeFile := &mocks.File{}
tt.mocks(fakeFileInfo, fakeFile)
tt.mocks(fakeFileInfo, fakeFile, fakeBar)
err := s.shred(fakeFileInfo, fakeFile)
if tt.wantError == nil {
@ -151,6 +162,7 @@ func TestShredder_shred(t *testing.T) {
fakeFileInfo.AssertExpectations(t)
fakeFile.AssertExpectations(t)
fakeBar.AssertExpectations(t)
})
}
}

74
mocks/ProgressBar.go Normal file
View File

@ -0,0 +1,74 @@
// Code generated by mockery v2.16.0. DO NOT EDIT.
package mocks
import mock "github.com/stretchr/testify/mock"
// ProgressBar is an autogenerated mock type for the ProgressBar type
type ProgressBar struct {
mock.Mock
}
// Add provides a mock function with given fields: _a0
func (_m *ProgressBar) Add(_a0 int) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(int) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// Finish provides a mock function with given fields:
func (_m *ProgressBar) Finish() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Write provides a mock function with given fields: _a0
func (_m *ProgressBar) Write(_a0 []byte) (int, error) {
ret := _m.Called(_a0)
var r0 int
if rf, ok := ret.Get(0).(func([]byte) int); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func([]byte) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewProgressBar interface {
mock.TestingT
Cleanup(func())
}
// NewProgressBar creates a new instance of ProgressBar. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewProgressBar(t mockConstructorTestingTNewProgressBar) *ProgressBar {
mock := &ProgressBar{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}