scorecard/utests/utlib.go
Azeem Shaikh 2e3e505a8c
Simplify DetailLogger interface (#1628)
Co-authored-by: Azeem Shaikh <azeems@google.com>
2022-02-11 15:48:58 -08:00

146 lines
3.8 KiB
Go

// Copyright 2021 Security Scorecard Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package utests defines util fns for Scorecard unit testing.
package utests
import (
"errors"
"fmt"
"log"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/ossf/scorecard/v4/checker"
)
// TestReturn encapsulates expected CheckResult return values.
type TestReturn struct {
Error error
Score int
NumberOfWarn int
NumberOfInfo int
NumberOfDebug int
}
// TestDetailLogger implements `checker.DetailLogger`.
type TestDetailLogger struct {
messages []checker.CheckDetail
}
// Info implements DetailLogger.Info.
func (l *TestDetailLogger) Info(msg *checker.LogMessage) {
cd := checker.CheckDetail{
Type: checker.DetailInfo,
Msg: *msg,
}
cd.Msg.Version = 3
l.messages = append(l.messages, cd)
}
// Warn implements DetailLogger.Warn.
func (l *TestDetailLogger) Warn(msg *checker.LogMessage) {
cd := checker.CheckDetail{
Type: checker.DetailWarn,
Msg: *msg,
}
cd.Msg.Version = 3
l.messages = append(l.messages, cd)
}
// Debug implements DetailLogger.Debug.
func (l *TestDetailLogger) Debug(msg *checker.LogMessage) {
cd := checker.CheckDetail{
Type: checker.DetailDebug,
Msg: *msg,
}
cd.Msg.Version = 3
l.messages = append(l.messages, cd)
}
// Flush implements DetailLogger.Flush.
func (l *TestDetailLogger) Flush() []checker.CheckDetail {
ret := l.messages
l.messages = nil
return ret
}
func getTestReturn(cr *checker.CheckResult, logger *TestDetailLogger) (*TestReturn, error) {
ret := new(TestReturn)
for _, v := range logger.messages {
switch v.Type {
default:
// nolint: goerr113
return nil, fmt.Errorf("invalid type %v", v.Type)
case checker.DetailInfo:
ret.NumberOfInfo++
case checker.DetailDebug:
ret.NumberOfDebug++
case checker.DetailWarn:
ret.NumberOfWarn++
}
}
ret.Score = cr.Score
ret.Error = cr.Error
return ret, nil
}
func errCmp(e1, e2 error) bool {
return errors.Is(e1, e2) || errors.Is(e2, e1)
}
// ValidateTestReturn validates expected TestReturn with actual checker.CheckResult values.
// nolint: thelper
func ValidateTestReturn(t *testing.T, name string, expected *TestReturn,
actual *checker.CheckResult, logger *TestDetailLogger) bool {
actualTestReturn, err := getTestReturn(actual, logger)
if err != nil {
panic(err)
}
if !cmp.Equal(*expected, *actualTestReturn, cmp.Comparer(errCmp)) {
log.Println(name+":", cmp.Diff(*expected, *actualTestReturn))
return false
}
return true
}
// ValidateLogMessage tests that at least one log message returns true for isExpectedMessage.
func ValidateLogMessage(isExpectedMessage func(checker.LogMessage, checker.DetailType) bool,
dl *TestDetailLogger) bool {
for _, message := range dl.messages {
if isExpectedMessage(message.Msg, message.Type) {
return true
}
}
return false
}
// ValidateLogMessageOffsets tests that the log message offsets match those
// in the passed in slice.
func ValidateLogMessageOffsets(dl *TestDetailLogger, offsets []uint) bool {
if len(dl.messages) != len(offsets) {
log.Println(cmp.Diff(dl.messages, offsets))
return false
}
for i, message := range dl.messages {
expectedOffset := offsets[i]
if expectedOffset != message.Msg.Offset {
log.Println(cmp.Diff(message.Msg.Offset, expectedOffset))
return false
}
}
return true
}