911975059a
Before, there was a `log/buffer.go`, but that design is not general, and it introduces a lot of irrelevant `Content() (string, error) ` and `return "", fmt.Errorf("not supported")` . And the old `log/buffer.go` is difficult to use, developers have to write a lot of `Contains` and `Sleep` code. The new `LogChecker` is designed to be a general approach to help to assert some messages appearing or not appearing in logs.
116 lines
2.4 KiB
Go
116 lines
2.4 KiB
Go
// Copyright 2023 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package test
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/log"
|
|
)
|
|
|
|
type LogChecker struct {
|
|
logger *log.MultiChannelledLogger
|
|
loggerName string
|
|
eventLoggerName string
|
|
|
|
filterMessages []string
|
|
filtered []bool
|
|
|
|
stopMark string
|
|
stopped bool
|
|
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func (lc *LogChecker) LogEvent(event *log.Event) error {
|
|
lc.mu.Lock()
|
|
defer lc.mu.Unlock()
|
|
for i, msg := range lc.filterMessages {
|
|
if strings.Contains(event.GetMsg(), msg) {
|
|
lc.filtered[i] = true
|
|
}
|
|
}
|
|
if strings.Contains(event.GetMsg(), lc.stopMark) {
|
|
lc.stopped = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (lc *LogChecker) Close() {}
|
|
|
|
func (lc *LogChecker) Flush() {}
|
|
|
|
func (lc *LogChecker) GetLevel() log.Level {
|
|
return log.TRACE
|
|
}
|
|
|
|
func (lc *LogChecker) GetStacktraceLevel() log.Level {
|
|
return log.NONE
|
|
}
|
|
|
|
func (lc *LogChecker) GetName() string {
|
|
return lc.eventLoggerName
|
|
}
|
|
|
|
func (lc *LogChecker) ReleaseReopen() error {
|
|
return nil
|
|
}
|
|
|
|
var checkerIndex int64
|
|
|
|
func NewLogChecker(loggerName string) (logChecker *LogChecker, cancel func()) {
|
|
logger := log.GetLogger(loggerName)
|
|
newCheckerIndex := atomic.AddInt64(&checkerIndex, 1)
|
|
lc := &LogChecker{
|
|
logger: logger,
|
|
loggerName: loggerName,
|
|
eventLoggerName: "TestLogChecker-" + strconv.FormatInt(newCheckerIndex, 10),
|
|
}
|
|
if err := logger.AddLogger(lc); err != nil {
|
|
panic(err) // it's impossible
|
|
}
|
|
return lc, func() { _, _ = logger.DelLogger(lc.GetName()) }
|
|
}
|
|
|
|
// Filter will make the `Check` function to check if these logs are outputted.
|
|
func (lc *LogChecker) Filter(msgs ...string) *LogChecker {
|
|
lc.mu.Lock()
|
|
defer lc.mu.Unlock()
|
|
lc.filterMessages = make([]string, len(msgs))
|
|
copy(lc.filterMessages, msgs)
|
|
lc.filtered = make([]bool, len(lc.filterMessages))
|
|
return lc
|
|
}
|
|
|
|
func (lc *LogChecker) StopMark(msg string) *LogChecker {
|
|
lc.mu.Lock()
|
|
defer lc.mu.Unlock()
|
|
lc.stopMark = msg
|
|
lc.stopped = false
|
|
return lc
|
|
}
|
|
|
|
// Check returns the filtered slice and whether the stop mark is reached.
|
|
func (lc *LogChecker) Check(d time.Duration) (filtered []bool, stopped bool) {
|
|
stop := time.Now().Add(d)
|
|
|
|
for {
|
|
lc.mu.Lock()
|
|
stopped = lc.stopped
|
|
lc.mu.Unlock()
|
|
|
|
if time.Now().After(stop) || stopped {
|
|
lc.mu.Lock()
|
|
f := make([]bool, len(lc.filtered))
|
|
copy(f, lc.filtered)
|
|
lc.mu.Unlock()
|
|
return f, stopped
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}
|