From 70aef6a44ea0adebbf81693f4321877a0baf9d1e Mon Sep 17 00:00:00 2001 From: Adrien CABARBAYE Date: Fri, 10 Jan 2025 14:30:33 +0000 Subject: [PATCH 1/2] :sparkles: `[filesystem]` Support for `tarfs` (#545) ### Description - Add support for tarfs ### Test Coverage - [x] This change is covered by existing or additional automated tests. - [ ] Manual testing has been performed (and evidence provided) as automated testing was not feasible. - [ ] Additional tests are not required for this change (e.g. documentation update). --- changes/20250110140409.feature | 1 + utils/filesystem/filesystem.go | 18 ++ utils/filesystem/tar.go | 48 +++++ utils/filesystem/tarfs.go | 35 ++++ utils/filesystem/tarfs_test.go | 235 ++++++++++++++++++++++++ utils/filesystem/testdata/t.tar | Bin 0 -> 30720 bytes utils/filesystem/testdata/testuntar.tar | Bin 0 -> 6656 bytes 7 files changed, 337 insertions(+) create mode 100644 changes/20250110140409.feature create mode 100644 utils/filesystem/tar.go create mode 100644 utils/filesystem/tarfs.go create mode 100644 utils/filesystem/tarfs_test.go create mode 100644 utils/filesystem/testdata/t.tar create mode 100644 utils/filesystem/testdata/testuntar.tar diff --git a/changes/20250110140409.feature b/changes/20250110140409.feature new file mode 100644 index 0000000000..243038d152 --- /dev/null +++ b/changes/20250110140409.feature @@ -0,0 +1 @@ +:sparkles: `[filesystem]` Support for `tarfs` diff --git a/utils/filesystem/filesystem.go b/utils/filesystem/filesystem.go index 7e393b2fdb..1380937329 100644 --- a/utils/filesystem/filesystem.go +++ b/utils/filesystem/filesystem.go @@ -23,6 +23,7 @@ const ( Embed Custom ZipFS + TarFS ) var ( @@ -62,6 +63,23 @@ func NewZipFileSystemFromStandardFileSystem(source string, limits ILimits) (IClo return NewZipFileSystem(NewStandardFileSystem(), source, limits) } +// NewTarFileSystem returns a filesystem over the contents of a .tar file. +// Warning: After use of the filesystem, it is crucial to close the tar file (tarFile) which has been opened from source for the entirety of the filesystem use session. +// fs corresponds to the filesystem to use to find the tar file. +func NewTarFileSystem(fs FS, source string, limits ILimits) (squashFS ICloseableFS, tarFile File, err error) { + wrapped, tarFile, err := newTarFSAdapterFromFilePath(fs, source, limits) + if err != nil { + return + } + squashFS = NewCloseableVirtualFileSystem(wrapped, TarFS, tarFile, fmt.Sprintf(".tar file `%v`", source), IdentityPathConverterFunc) + return +} + +// NewTarFileSystemFromStandardFileSystem returns a tar filesystem similar to NewTarFileSystem but assumes the tar file described by source can be found on the standard file system. +func NewTarFileSystemFromStandardFileSystem(source string, limits ILimits) (ICloseableFS, File, error) { + return NewTarFileSystem(NewStandardFileSystem(), source, limits) +} + func NewFs(fsType FilesystemType) FS { switch fsType { case StandardFS: diff --git a/utils/filesystem/tar.go b/utils/filesystem/tar.go new file mode 100644 index 0000000000..d82c20911f --- /dev/null +++ b/utils/filesystem/tar.go @@ -0,0 +1,48 @@ +package filesystem + +import ( + "archive/tar" + "fmt" + + "github.com/ARM-software/golang-utils/utils/commonerrors" +) + +func newTarReader(fs FS, source string, limits ILimits, currentDepth int64) (tarReader *tar.Reader, file File, err error) { + if fs == nil { + err = fmt.Errorf("%w: missing file system", commonerrors.ErrUndefined) + return + } + if limits == nil { + err = fmt.Errorf("%w: missing file system limits", commonerrors.ErrUndefined) + return + } + if limits.Apply() && limits.GetMaxDepth() >= 0 && currentDepth > limits.GetMaxDepth() { + err = fmt.Errorf("%w: depth [%v] of tar file [%v] is beyond allowed limits (max: %v)", commonerrors.ErrTooLarge, currentDepth, source, limits.GetMaxDepth()) + return + } + + if !fs.Exists(source) { + err = fmt.Errorf("%w: could not find archive [%v]", commonerrors.ErrNotFound, source) + return + } + + info, err := fs.Lstat(source) + if err != nil { + return + } + file, err = fs.GenericOpen(source) + if err != nil { + return + } + + tarFileSize := info.Size() + + if limits.Apply() && tarFileSize > limits.GetMaxFileSize() { + err = fmt.Errorf("%w: tar file [%v] is too big (%v B) and beyond limits (max: %v B)", commonerrors.ErrTooLarge, source, tarFileSize, limits.GetMaxFileSize()) + return + } + + tarReader = tar.NewReader(file) + + return +} diff --git a/utils/filesystem/tarfs.go b/utils/filesystem/tarfs.go new file mode 100644 index 0000000000..ce95b081f4 --- /dev/null +++ b/utils/filesystem/tarfs.go @@ -0,0 +1,35 @@ +package filesystem + +import ( + "archive/tar" + "fmt" + + "github.com/spf13/afero" + "github.com/spf13/afero/tarfs" + + "github.com/ARM-software/golang-utils/utils/commonerrors" +) + +func newTarFSAdapterFromReader(reader *tar.Reader) (afero.Fs, error) { + if reader == nil { + return nil, fmt.Errorf("%w: missing reader", commonerrors.ErrUndefined) + } + return afero.NewReadOnlyFs(tarfs.New(reader)), nil +} + +func newTarFSAdapterFromFilePath(fs FS, tarFilePath string, limits ILimits) (tarFs afero.Fs, tarFile File, err error) { + if fs == nil { + err = fmt.Errorf("%w: missing filesystem to use for finding the tar file", commonerrors.ErrUndefined) + return + } + tarReader, tarFile, err := newTarReader(fs, tarFilePath, limits, 0) + if err != nil && tarFile != nil { + subErr := tarFile.Close() + if subErr == nil { + tarFile = nil + } + return + } + tarFs, err = newTarFSAdapterFromReader(tarReader) + return +} diff --git a/utils/filesystem/tarfs_test.go b/utils/filesystem/tarfs_test.go new file mode 100644 index 0000000000..1f036d77b0 --- /dev/null +++ b/utils/filesystem/tarfs_test.go @@ -0,0 +1,235 @@ +package filesystem + +import ( + "io/fs" + "os" + "path/filepath" + "sort" + "testing" + + "github.com/go-faker/faker/v4" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/ARM-software/golang-utils/utils/commonerrors/errortest" +) + +const tarTestFileContent = "Test names:\r\nGeorge\r\nGeoffrey\r\nGonzo" + +var ( + aferoTarTestContentTree = []string{ + string(globalFileSystem.PathSeparator()), + filepath.Join("/", "sub"), + filepath.Join("/", "sub", "testDir2"), + filepath.Join("/", "sub", "testDir2", "testFile"), + filepath.Join("/", "testDir1"), + filepath.Join("/", "testDir1", "testFile"), + filepath.Join("/", "testFile"), + } +) + +func Test_tarFs_Close(t *testing.T) { + fs, zipFile, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { + if zipFile != nil { + _ = zipFile.Close() + } + }() + require.NotNil(t, zipFile) + _, err = fs.Stat("testuntar/test.txt") + assert.NoError(t, err) + require.NoError(t, fs.Close()) + _, err = fs.Stat("testuntar/test.txt") + errortest.AssertErrorDescription(t, err, "closed") + require.NoError(t, fs.Close()) + require.NoError(t, fs.Close()) +} + +func Test_tarFs_Exists(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + assert.False(t, fs.Exists(faker.DomainName())) + // FIXME: enable when issue in afero is fixed https://github.com/spf13/afero/issues/395 + // assert.True(t, fs.Exists(string(filepath.Separator))) + // assert.True(t, fs.Exists("/")) + assert.True(t, fs.Exists("testuntar/test.txt")) + assert.True(t, fs.Exists("testuntar/child.zip")) + assert.True(t, fs.Exists("testuntar/ไป ไหน มา.txt")) + require.NoError(t, fs.Close()) +} + +func Test_tarFs_Exists_usingAferoTestTar(t *testing.T) { + // using afero test zip file + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "t.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + assert.False(t, fs.Exists(faker.DomainName())) + assert.True(t, fs.Exists(string(filepath.Separator))) + assert.True(t, fs.Exists("/")) + + assert.True(t, fs.Exists("testDir1")) + assert.True(t, fs.Exists("testFile")) + assert.True(t, fs.Exists("testDir1/testFile")) + require.NoError(t, fs.Close()) +} + +func Test_tarFs_FileInfo(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + zfile, err := fs.Stat("/") + require.NoError(t, err) + assert.Equal(t, string(filepath.Separator), zfile.Name()) + assert.True(t, zfile.IsDir()) + assert.Zero(t, zfile.Size()) + + zfile, err = fs.Stat("testuntar/test.txt") + require.NoError(t, err) + assert.Equal(t, "test.txt", zfile.Name()) + assert.False(t, zfile.IsDir()) + assert.NotZero(t, zfile.Size()) + + require.NoError(t, fs.Close()) +} + +func Test_tarFs_Browsing(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + empty, err := fs.IsEmpty(faker.DomainName()) + require.NoError(t, err) + assert.True(t, empty) + empty, err = fs.IsEmpty("testuntar/test.txt") + require.NoError(t, err) + assert.False(t, empty) + empty, err = fs.IsEmpty("testuntar/child.zip") + require.NoError(t, err) + assert.False(t, empty) + empty, err = fs.IsEmpty("testuntar/ไป ไหน มา.txt") + require.NoError(t, err) + assert.True(t, empty) + require.NoError(t, fs.Close()) +} + +func Test_tarFs_Browsing_usingAferoTestTar(t *testing.T) { + tarFs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "t.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = tarFs.Close() }() + + // Warning: this assumes the walk function is executed in the same goroutine and not concurrently. + // If not, this list should be created with some thread access protection in place. + pathList := []string{} + + var wFunc = func(path string, info fs.FileInfo, err error) error { + pathList = append(pathList, path) + return nil + } + + require.NoError(t, tarFs.Walk("/", wFunc)) + require.NoError(t, tarFs.Close()) + + sort.Strings(pathList) + sort.Strings(aferoTarTestContentTree) + assert.Equal(t, aferoTarTestContentTree, pathList) +} + +func Test_tarFs_LS(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "t.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + files, err := fs.Ls("/") + require.NoError(t, err) + assert.NotZero(t, files) + assert.Contains(t, files, "testFile") + + files, err = fs.Ls("sub/") + require.NoError(t, err) + assert.NotZero(t, files) + assert.Contains(t, files, "testDir2") + require.NoError(t, fs.Close()) +} + +func Test_tarFs_itemType(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + isFile, err := fs.IsFile("unzip") + require.NoError(t, err) + assert.False(t, isFile) + // FIXME: Enable when issue in afero is fixed https://github.com/spf13/afero/issues/395 + // isDir, err := fs.IsDir("unzip") + // require.NoError(t, err) + // assert.True(t, isDir) + isFile, err = fs.IsFile("testuntar/test.txt") + require.NoError(t, err) + assert.True(t, isFile) + isDir, err := fs.IsDir("testuntar/test.txt") + require.NoError(t, err) + assert.False(t, isDir) + require.NoError(t, fs.Close()) +} + +func Test_tarFs_itemType_usingAferoTestTar(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "t.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + isFile, err := fs.IsFile("testDir1") + require.NoError(t, err) + assert.False(t, isFile) + isDir, err := fs.IsDir("testDir1") + require.NoError(t, err) + assert.True(t, isDir) + isFile, err = fs.IsFile("testDir1/testFile") + require.NoError(t, err) + assert.True(t, isFile) + isDir, err = fs.IsDir("testDir1/testFile") + require.NoError(t, err) + assert.False(t, isDir) + require.NoError(t, fs.Close()) +} + +func Test_tarFs_Read(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + c, err := fs.ReadFile("testuntar/test.txt") + require.NoError(t, err) + assert.Equal(t, tarTestFileContent, string(c)) + + require.NoError(t, fs.Close()) +} + +func Test_tarFs_not_supported(t *testing.T) { + fs, _, err := NewTarFileSystemFromStandardFileSystem(filepath.Join("testdata", "testuntar.tar"), NoLimits()) + require.NoError(t, err) + defer func() { _ = fs.Close() }() + + _, err = fs.TempDir("testdata", "aaaa") + errortest.AssertErrorDescription(t, err, "operation not permitted") + + f, err := fs.OpenFile("testuntar/test.txt", os.O_RDWR, os.FileMode(0600)) + defer func() { + if f != nil { + _ = f.Close() + } + }() + require.Error(t, err) + errortest.AssertErrorDescription(t, err, "operation not permitted") + + err = fs.Chmod("testuntar/test.txt", os.FileMode(0600)) + require.Error(t, err) + errortest.AssertErrorDescription(t, err, "operation not permitted") + + require.NoError(t, fs.Close()) + +} diff --git a/utils/filesystem/testdata/t.tar b/utils/filesystem/testdata/t.tar new file mode 100644 index 0000000000000000000000000000000000000000..d5b9aa0fb5ebdcc5f85b3921202f8b4d5d513af2 GIT binary patch literal 30720 zcmeI)OHPA800v-=(i;dfJT73oM_Y+$qAvQ_czS1y5>04i0j9xkHZ)8+!1oWkKb$VR zyJF!{rqMM`kq%YYl;4keDvzRyp^Rl6n>Ni?p$xIuGz;I?$MTocd3)S!itW?krGM?; zu3huD`D_2X@$;vY|G7V%?+?eY)JDhwPt3m#U7hleO=#u+7hC5?{&^!j9G?4`FP!sl z>y+#0`ycZ3@cy?kru>^wrToj8?_+eifBydO4FUuR5FkK+009C72oNAZVD$oz1_1&D z2oNAZfB*pk1PBlyuzG>d?oYK;_a}EQa=~MA|ECSP|JB7x|7WzXGV{qH|Ao3gU-?h& z|CC+Izpg{0|1)#cqs#3K0t5&UAV7cs0RjXF5FkKc^#ZFeIC($<1PBlyK!5-N0t5&U zAV6ThVC`e*8|41a#QsMaWA1;1E`R^Y_g@CW#eQrJ0t5&UAV7cs0RjXF5FkKc^#ZFe qxOhMU1PBlyK!5-N0t5&UAV6TZSh2vu1PBlyK!5-N0t5)$oWMKSbAGu1 literal 0 HcmV?d00001 diff --git a/utils/filesystem/testdata/testuntar.tar b/utils/filesystem/testdata/testuntar.tar new file mode 100644 index 0000000000000000000000000000000000000000..570d3077eb9be452c5fe985f80d900e8c3bba6d4 GIT binary patch literal 6656 zcmeHL&ubGw6rNfuCDL23!U&?LraPP29q=Gtq-fNpQ1vEklSwnOyOYlDO64r2WN;imhW*KI7XWzH;zBk|e*qBFg62_id zL=#IEB*qxhC8WVJeMTswmTi|T;y^pR%@B4b)}ub)Bm(C#?t5~is_}mOs}14TikqUD zyZ<`CqvG#UhDeDymPHA%U1SmB+5}-M_nr&^JmT@Ml&6o+r?kA+mZB>SjZ4Vz;G;+J2FubPD0?*QyU4#HID0^SOZR@baagF-#Gn4J0g@Y*P$+@ z1X(sM!8Bm>KbqEDTC@0P!#x+^K9I5Tr|$mz4{Ly){~4nu{(TfqtonQN`ounOfd4WB znf`mrR{k9tuYa29pTYTn5Qo72-|YYLkHsM3s$SwlFW}Mnxxx~cYMrNLy{`B@sL60s z4%(Re|A&D-{qJo+^Y?A6{#`rMzn!lC4r~LA{__vT0mMH)L>+)I5B^~WGW~boJm_{F zF6Bm+MIkFh Date: Fri, 10 Jan 2025 14:31:10 +0000 Subject: [PATCH 2/2] :sparkles: `[collection]` add helpers easier to use with slices (#547) ### Description add utilities to the collection module ### Test Coverage - [x] This change is covered by existing or additional automated tests. - [ ] Manual testing has been performed (and evidence provided) as automated testing was not feasible. - [ ] Additional tests are not required for this change (e.g. documentation update). --- changes/20250108150125.feature | 1 + utils/collection/search.go | 36 ++++++++++++++++++++++++++++++ utils/collection/search_test.go | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 changes/20250108150125.feature diff --git a/changes/20250108150125.feature b/changes/20250108150125.feature new file mode 100644 index 0000000000..6e0ae8c127 --- /dev/null +++ b/changes/20250108150125.feature @@ -0,0 +1 @@ +:sparkles: `[collection]` add helpers easier to use with slices diff --git a/utils/collection/search.go b/utils/collection/search.go index e47c33404b..678d8e0d62 100644 --- a/utils/collection/search.go +++ b/utils/collection/search.go @@ -59,6 +59,9 @@ func UniqueEntries[T comparable](slice []T) []T { // Any returns true if there is at least one element of the slice which is true. func Any(slice []bool) bool { + if len(slice) == 0 { + return false + } for i := range slice { if slice[i] { return true @@ -67,6 +70,21 @@ func Any(slice []bool) bool { return false } +// AnyTrue returns whether there is a value set to true +func AnyTrue(values ...bool) bool { + return Any(values) +} + +// AnyFunc returns whether there is at least one element of slice s for which f() returns true. +func AnyFunc[S ~[]E, E any](s S, f func(E) bool) bool { + all := make([]bool, 0, len(s)) + for i := range s { + all = append(all, f(s[i])) + + } + return Any(all) +} + // AnyEmpty returns whether there is one entry in the slice which is empty. // If strict, then whitespaces are considered as empty strings func AnyEmpty(strict bool, slice []string) bool { @@ -76,6 +94,9 @@ func AnyEmpty(strict bool, slice []string) bool { // All returns true if all items of the slice are true. func All(slice []bool) bool { + if len(slice) == 0 { + return false + } for i := range slice { if !slice[i] { return false @@ -84,6 +105,21 @@ func All(slice []bool) bool { return true } +// AllTrue returns whether all values are true. +func AllTrue(values ...bool) bool { + return All(values) +} + +// AllFunc returns whether f returns true for all the elements of slice s. +func AllFunc[S ~[]E, E any](s S, f func(E) bool) bool { + all := make([]bool, 0, len(s)) + for i := range s { + all = append(all, f(s[i])) + + } + return All(all) +} + // AllNotEmpty returns whether all elements of the slice are not empty. // If strict, then whitespaces are considered as empty strings func AllNotEmpty(strict bool, slice []string) bool { diff --git a/utils/collection/search_test.go b/utils/collection/search_test.go index 7cc0b6984e..115fe8fb53 100644 --- a/utils/collection/search_test.go +++ b/utils/collection/search_test.go @@ -58,19 +58,58 @@ func TestFindInSlice(t *testing.T) { } func TestAny(t *testing.T) { + assert.False(t, Any([]bool{})) assert.True(t, Any([]bool{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false})) assert.False(t, Any([]bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false})) assert.True(t, Any([]bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true})) assert.True(t, Any([]bool{true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, true})) } +func TestAnyTrue(t *testing.T) { + assert.False(t, AnyTrue()) + assert.True(t, AnyTrue(false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false)) + assert.False(t, AnyTrue(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false)) + assert.True(t, AnyTrue(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true)) + assert.True(t, AnyTrue(true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, true)) +} + +func TestAnyFunc(t *testing.T) { + f := func(v bool) bool { + return v + } + assert.False(t, AnyFunc([]bool{}, f)) + assert.True(t, AnyFunc([]bool{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, f)) + assert.False(t, AnyFunc([]bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, f)) + assert.True(t, AnyFunc([]bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, f)) + assert.True(t, AnyFunc([]bool{true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, true}, f)) +} + func TestAll(t *testing.T) { + assert.False(t, All([]bool{})) assert.False(t, All([]bool{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false})) assert.False(t, All([]bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false})) assert.True(t, All([]bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true})) assert.False(t, All([]bool{true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, true})) } +func TestAllTrue(t *testing.T) { + assert.False(t, AllTrue(false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false)) + assert.False(t, AllTrue(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false)) + assert.True(t, AllTrue(true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true)) + assert.False(t, AllTrue(true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, true)) +} + +func TestAllFunc(t *testing.T) { + f := func(v bool) bool { + return v + } + assert.False(t, AllFunc([]bool{}, f)) + assert.False(t, AllFunc([]bool{false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false}, f)) + assert.False(t, AllFunc([]bool{false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}, f)) + assert.True(t, AllFunc([]bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true}, f)) + assert.False(t, AllFunc([]bool{true, true, true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, true, true, true}, f)) +} + func TestAnyEmpty(t *testing.T) { assert.True(t, AnyEmpty(false, []string{faker.Username(), faker.Name(), faker.Sentence(), ""})) assert.False(t, AnyEmpty(false, []string{faker.Username(), " ", faker.Name(), faker.Sentence()}))