Skip to content

Commit

Permalink
Generalize CheckIfFileExists fn (#1668)
Browse files Browse the repository at this point in the history
Co-authored-by: Azeem Shaikh <[email protected]>
  • Loading branch information
azeemshaikh38 and azeemsgoogle authored Feb 22, 2022
1 parent c03085a commit f616278
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 175 deletions.
10 changes: 3 additions & 7 deletions checks/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@ import (
"errors"
)

//nolint
var (
errInternalInvalidDockerFile = errors.New("invalid Dockerfile")
errInternalInvalidYamlFile = errors.New("invalid yaml file")
errInternalFilenameMatch = errors.New("filename match error")
errInternalEmptyFile = errors.New("empty file")
errInvalidGitHubWorkflow = errors.New("invalid GitHub workflow")
errInternalNoReviews = errors.New("no reviews found")
errInternalNoCommits = errors.New("no commits found")
errInternalInvalidPermissions = errors.New("invalid permissions")
errInternalNameCannotBeEmpty = errors.New("name cannot be empty")
errInternalCheckFuncCannotBeNil = errors.New("checkFunc cannot be nil")
// TODO(#1245): these should be moved under `raw` package after migration.
errInvalidArgType = errors.New("invalid arg type")
errInvalidArgLength = errors.New("invalid arg length")
)
20 changes: 8 additions & 12 deletions checks/fileparser/listing.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,22 +168,19 @@ func CheckFilesContentV6(shellPathFnPattern string,
return nil
}

// FileCbV6 is the callback.
// The bool returned indicates whether the FileCbData
// should continue iterating over files or not.
type FileCbV6 func(path string, data FileCbData) (bool, error)
// DoWhileTrueOnFilename takes a filename and optional variadic args and returns
// true if the next filename should continue to be processed.
type DoWhileTrueOnFilename func(path string, args ...interface{}) (bool, error)

// CheckIfFileExistsV6 downloads the tar of the repository and calls the onFile() to check
// for the occurrence.
func CheckIfFileExistsV6(repoClient clients.RepoClient,
onFile FileCbV6, data FileCbData) error {
// OnAllFilesDo iterates through all files returned by `repoClient` and
// calls `onFile` fn on them until `onFile` returns error or a false value.
func OnAllFilesDo(repoClient clients.RepoClient, onFile DoWhileTrueOnFilename, args ...interface{}) error {
matchedFiles, err := repoClient.ListFiles(func(string) (bool, error) { return true, nil })
if err != nil {
// nolint: wrapcheck
return err
return fmt.Errorf("error during ListFiles: %w", err)
}
for _, filename := range matchedFiles {
continueIter, err := onFile(filename, data)
continueIter, err := onFile(filename, args...)
if err != nil {
return err
}
Expand All @@ -192,7 +189,6 @@ func CheckIfFileExistsV6(repoClient clients.RepoClient,
break
}
}

return nil
}

Expand Down
143 changes: 88 additions & 55 deletions checks/fileparser/listing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ import (
mockrepo "github.com/ossf/scorecard/v4/clients/mockclients"
)

var (
errInvalidArgType = errors.New("invalid arg type")
errInvalidArgLength = errors.New("invalid arg length")
errTest = errors.New("test")
)

func TestIsTemplateFile(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -572,81 +578,108 @@ func TestCheckFilesContent(t *testing.T) {
}
}

// TestCheckFilesContentV6 tests the CheckFilesContentV6 function.
func TestCheckIfFileExistsV6(t *testing.T) {
// TestOnAllFilesDo tests the OnAllFilesDo function.
// nolint:gocognit
func TestOnAllFilesDo(t *testing.T) {
t.Parallel()
//nolint
type args struct {
cbReturn bool
cbwantErr bool
listFilesReturnError error

type testArgsFn func(args ...interface{}) bool
validateCountIs := func(count int) testArgsFn {
return func(args ...interface{}) bool {
if len(args) == 0 {
return false
}
val, ok := args[0].(*int)
if !ok {
return false
}
return val != nil && *val == count
}
}
//nolint

incrementCount := func(path string, args ...interface{}) (bool, error) {
if len(args) < 1 {
return false, errInvalidArgLength
}
val, ok := args[0].(*int)
if !ok || val == nil {
return false, errInvalidArgType
}
(*val)++
if len(args) > 1 {
maxVal, ok := args[1].(int)
if !ok {
return false, errInvalidArgType
}
if *val >= maxVal {
return false, nil
}
}
return true, nil
}
alwaysFail := func(path string, args ...interface{}) (bool, error) {
return false, errTest
}
// nolint
tests := []struct {
name string
args args
wantErr bool
name string
onFile DoWhileTrueOnFilename
onFileArgs []interface{}
listFiles []string
errListFiles error
err error
testArgs testArgsFn
}{
{
name: "cb true and no error",
args: args{
cbReturn: true,
cbwantErr: false,
listFilesReturnError: nil,
},
wantErr: false,
name: "error during ListFiles",
errListFiles: errTest,
err: errTest,
onFile: alwaysFail,
},
{
name: "cb false and no error",
args: args{
cbReturn: false,
cbwantErr: false,
listFilesReturnError: nil,
},
wantErr: false,
name: "empty ListFiles",
listFiles: []string{},
onFile: incrementCount,
onFileArgs: []interface{}{new(int)},
testArgs: validateCountIs(0),
},
{
name: "cb wantErr and error",
args: args{
cbReturn: true,
cbwantErr: true,
listFilesReturnError: nil,
},
wantErr: true,
name: "onFile true and no error",
listFiles: []string{"foo", "bar"},
onFile: incrementCount,
onFileArgs: []interface{}{new(int)},
testArgs: validateCountIs(2),
},
{
name: "listFilesReturnError and error",
args: args{
cbReturn: true,
cbwantErr: true,
//nolint
listFilesReturnError: errors.New("test error"),
},
wantErr: true,
name: "onFile false and no error",
listFiles: []string{"foo", "bar"},
onFile: incrementCount,
onFileArgs: []interface{}{new(int), 1 /*maxVal*/},
testArgs: validateCountIs(1),
},
{
name: "onFile has error",
listFiles: []string{"foo", "bar"},
onFile: alwaysFail,
err: errTest,
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

x := func(path string, data FileCbData) (bool, error) {
if tt.args.cbwantErr {
//nolint
return false, errors.New("test error")
}
return tt.args.cbReturn, nil
}

ctrl := gomock.NewController(t)
mockRepo := mockrepo.NewMockRepoClient(ctrl)
mockRepo.EXPECT().ListFiles(gomock.Any()).Return([]string{"foo"}, nil).AnyTimes()
mockRepoClient := mockrepo.NewMockRepoClient(ctrl)
mockRepoClient.EXPECT().ListFiles(gomock.Any()).
Return(tt.listFiles, tt.errListFiles).AnyTimes()

err := CheckIfFileExistsV6(mockRepo, x, x)

if (err != nil) != tt.wantErr {
t.Errorf("CheckIfFileExistsV6() error = %v, wantErr %v for %v", err, tt.wantErr, tt.name)
return
err := OnAllFilesDo(mockRepoClient, tt.onFile, tt.onFileArgs...)
if !errors.Is(err, tt.err) {
t.Errorf("OnAllFilesDo() expected error = %v, got %v", tt.err, err)
}
if tt.testArgs != nil && !tt.testArgs(tt.onFileArgs...) {
t.Error("testArgs validation failed")
}
})
}
Expand Down
30 changes: 19 additions & 11 deletions checks/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package checks

import (
"fmt"
"regexp"
"strings"

Expand Down Expand Up @@ -105,17 +106,7 @@ func testLicenseCheck(name string) bool {
func LicenseCheck(c *checker.CheckRequest) checker.CheckResult {
var s string

onFile := func(name string, data fileparser.FileCbData) (bool, error) {
if checkLicense(name) {
if strData, ok := data.(*string); ok && strData != nil {
*strData = name
}
return false, nil
}
return true, nil
}

err := fileparser.CheckIfFileExistsV6(c.RepoClient, onFile, &s)
err := fileparser.OnAllFilesDo(c.RepoClient, isLicenseFile, &s)
if err != nil {
return checker.CreateRuntimeErrorResult(CheckLicense, err)
}
Expand All @@ -130,6 +121,23 @@ func LicenseCheck(c *checker.CheckRequest) checker.CheckResult {
return checker.CreateMinScoreResult(CheckLicense, "license file not detected")
}

var isLicenseFile fileparser.DoWhileTrueOnFilename = func(name string, args ...interface{}) (bool, error) {
if len(args) != 1 {
return false, fmt.Errorf("isLicenseFile requires exactly one argument: %w", errInvalidArgLength)
}
s, ok := args[0].(*string)
if !ok {
return false, fmt.Errorf("isLicenseFile requires argument of type: *string: %w", errInvalidArgType)
}
if checkLicense(name) {
if s != nil {
*s = name
}
return false, nil
}
return true, nil
}

// CheckLicense to check whether the name parameter fulfill license file criteria.
func checkLicense(name string) bool {
for _, check := range regexChecks {
Expand Down
13 changes: 8 additions & 5 deletions checks/raw/dependency_update_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
// DependencyUpdateTool is the exported name for Depdendency-Update-Tool.
func DependencyUpdateTool(c clients.RepoClient) (checker.DependencyUpdateToolData, error) {
var tools []checker.Tool
err := fileparser.CheckIfFileExistsV6(c, checkDependencyFileExists, &tools)
err := fileparser.OnAllFilesDo(c, checkDependencyFileExists, &tools)
if err != nil {
return checker.DependencyUpdateToolData{}, fmt.Errorf("%w", err)
}
Expand All @@ -35,11 +35,14 @@ func DependencyUpdateTool(c clients.RepoClient) (checker.DependencyUpdateToolDat
return checker.DependencyUpdateToolData{Tools: tools}, nil
}

func checkDependencyFileExists(name string, data fileparser.FileCbData) (bool, error) {
ptools, ok := data.(*[]checker.Tool)
var checkDependencyFileExists fileparser.DoWhileTrueOnFilename = func(name string, args ...interface{}) (bool, error) {
if len(args) != 1 {
return false, fmt.Errorf("checkDependencyFileExists requires exactly one argument: %w", errInvalidArgLength)
}
ptools, ok := args[0].(*[]checker.Tool)
if !ok {
// This never happens.
panic("invalid type")
return false, fmt.Errorf(
"checkDependencyFileExists requires an argument of type: *[]checker.Tool: %w", errInvalidArgType)
}

switch strings.ToLower(name) {
Expand Down
3 changes: 2 additions & 1 deletion checks/raw/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import (
"errors"
)

//nolint
var (
errInternalCommitishNil = errors.New("commitish is nil")
errInternalBranchNotFound = errors.New("branch not found")
errInvalidArgType = errors.New("invalid arg type")
errInvalidArgLength = errors.New("invalid arg length")
)
Loading

0 comments on commit f616278

Please sign in to comment.