gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package testlogger
  4. import (
  5. "fmt"
  6. "os"
  7. "runtime"
  8. "strings"
  9. "sync"
  10. "testing"
  11. "time"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/queue"
  14. "code.gitea.io/gitea/modules/util"
  15. )
  16. var (
  17. prefix string
  18. TestTimeout = 10 * time.Minute
  19. TestSlowRun = 10 * time.Second
  20. TestSlowFlush = 1 * time.Second
  21. )
  22. var WriterCloser = &testLoggerWriterCloser{}
  23. type testLoggerWriterCloser struct {
  24. sync.RWMutex
  25. t []testing.TB
  26. }
  27. func (w *testLoggerWriterCloser) pushT(t testing.TB) {
  28. w.Lock()
  29. w.t = append(w.t, t)
  30. w.Unlock()
  31. }
  32. func (w *testLoggerWriterCloser) Write(p []byte) (int, error) {
  33. // There was a data race problem: the logger system could still try to output logs after the runner is finished.
  34. // So we must ensure that the "t" in stack is still valid.
  35. w.RLock()
  36. defer w.RUnlock()
  37. var t testing.TB
  38. if len(w.t) > 0 {
  39. t = w.t[len(w.t)-1]
  40. }
  41. if len(p) > 0 && p[len(p)-1] == '\n' {
  42. p = p[:len(p)-1]
  43. }
  44. if t == nil {
  45. // if there is no running test, the log message should be outputted to console, to avoid losing important information.
  46. // the "???" prefix is used to match the "===" and "+++" in PrintCurrentTest
  47. return fmt.Fprintf(os.Stdout, "??? [TestLogger] %s\n", p)
  48. }
  49. t.Log(string(p))
  50. return len(p), nil
  51. }
  52. func (w *testLoggerWriterCloser) popT() {
  53. w.Lock()
  54. if len(w.t) > 0 {
  55. w.t = w.t[:len(w.t)-1]
  56. }
  57. w.Unlock()
  58. }
  59. func (w *testLoggerWriterCloser) Close() error {
  60. return nil
  61. }
  62. func (w *testLoggerWriterCloser) Reset() {
  63. w.Lock()
  64. if len(w.t) > 0 {
  65. for _, t := range w.t {
  66. if t == nil {
  67. continue
  68. }
  69. _, _ = fmt.Fprintf(os.Stdout, "Unclosed logger writer in test: %s", t.Name())
  70. t.Errorf("Unclosed logger writer in test: %s", t.Name())
  71. }
  72. w.t = nil
  73. }
  74. w.Unlock()
  75. }
  76. // Printf takes a format and args and prints the string to os.Stdout
  77. func Printf(format string, args ...any) {
  78. if !log.CanColorStdout {
  79. for i := range args {
  80. if c, ok := args[i].(*log.ColoredValue); ok {
  81. args[i] = c.Value()
  82. }
  83. }
  84. }
  85. _, _ = fmt.Fprintf(os.Stdout, format, args...)
  86. }
  87. // PrintCurrentTest prints the current test to os.Stdout
  88. func PrintCurrentTest(t testing.TB, skip ...int) func() {
  89. t.Helper()
  90. runStart := time.Now()
  91. actualSkip := util.OptionalArg(skip) + 1
  92. _, filename, line, _ := runtime.Caller(actualSkip)
  93. Printf("=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line)
  94. WriterCloser.pushT(t)
  95. timeoutChecker := time.AfterFunc(TestTimeout, func() {
  96. l := 128 * 1024
  97. var stack []byte
  98. for {
  99. stack = make([]byte, l)
  100. n := runtime.Stack(stack, true)
  101. if n <= l {
  102. stack = stack[:n]
  103. break
  104. }
  105. l = n
  106. }
  107. Printf("!!! %s ... timeout: %v ... stacktrace:\n%s\n\n", log.NewColoredValue(t.Name(), log.Bold, log.FgRed), TestTimeout, string(stack))
  108. })
  109. return func() {
  110. flushStart := time.Now()
  111. slowFlushChecker := time.AfterFunc(TestSlowFlush, func() {
  112. Printf("+++ %s ... still flushing after %v ...\n", log.NewColoredValue(t.Name(), log.Bold, log.FgRed), TestSlowFlush)
  113. })
  114. if err := queue.GetManager().FlushAll(t.Context(), -1); err != nil {
  115. t.Errorf("Flushing queues failed with error %v", err)
  116. }
  117. slowFlushChecker.Stop()
  118. timeoutChecker.Stop()
  119. runDuration := time.Since(runStart)
  120. flushDuration := time.Since(flushStart)
  121. if runDuration > TestSlowRun {
  122. Printf("+++ %s is a slow test (run: %v, flush: %v)\n", log.NewColoredValue(t.Name(), log.Bold, log.FgYellow), runDuration, flushDuration)
  123. }
  124. WriterCloser.popT()
  125. }
  126. }
  127. // TestLogEventWriter is a logger which will write to the testing log
  128. type TestLogEventWriter struct {
  129. *log.EventWriterBaseImpl
  130. }
  131. // newTestLoggerWriter creates a TestLogEventWriter as a log.LoggerProvider
  132. func newTestLoggerWriter(name string, mode log.WriterMode) log.EventWriter {
  133. w := &TestLogEventWriter{}
  134. w.EventWriterBaseImpl = log.NewEventWriterBase(name, "test-log-writer", mode)
  135. w.OutputWriteCloser = WriterCloser
  136. return w
  137. }
  138. func Init() {
  139. const relFilePath = "modules/testlogger/testlogger.go"
  140. _, filename, _, _ := runtime.Caller(0)
  141. if !strings.HasSuffix(filename, relFilePath) {
  142. panic("source code file path doesn't match expected: " + relFilePath)
  143. }
  144. prefix = strings.TrimSuffix(filename, relFilePath)
  145. log.RegisterEventWriter("test", newTestLoggerWriter)
  146. duration, err := time.ParseDuration(os.Getenv("GITEA_TEST_SLOW_RUN"))
  147. if err == nil && duration > 0 {
  148. TestSlowRun = duration
  149. }
  150. duration, err = time.ParseDuration(os.Getenv("GITEA_TEST_SLOW_FLUSH"))
  151. if err == nil && duration > 0 {
  152. TestSlowFlush = duration
  153. }
  154. }
  155. func Fatalf(format string, args ...any) {
  156. Printf(format+"\n", args...)
  157. os.Exit(1)
  158. }