Improve testing and try to fix MySQL hanging (#32515)

By some CI fine tunes (`run tests`), SQLite & MSSQL could complete
in about 12~13 minutes (before > 14), MySQL could complete in 18 minutes
(before: about 23 or even > 30)

Major changes:

1. use tmpfs for MySQL storage
1. run `make test-mysql` instead of `make integration-test-coverage`
because the code coverage is not really used at the moment.
1. refactor testlogger to make it more reliable and be able to report
stuck stacktrace
1. do not requeue failed items when a queue is being flushed (failed
items would keep failing and make flush uncompleted)
1. reduce the file sizes for testing
1. use math ChaCha20 random data instead of crypot/rand (for testing
purpose only)
1. no need to `DeleteRepository` in `TestLinguist`
1. other related refactoring to make code easier to maintain
This commit is contained in:
wxiaoguang 2024-11-15 23:45:07 +08:00 committed by GitHub
parent a0c0cb3a2c
commit ecbb03dc6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 201 additions and 246 deletions

View File

@ -154,12 +154,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
services: services:
mysql: mysql:
image: mysql:8.0 # the bitnami mysql image has more options than the official one, it's easier to customize
image: bitnami/mysql:8.0
env: env:
MYSQL_ALLOW_EMPTY_PASSWORD: true ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: testgitea MYSQL_DATABASE: testgitea
ports: ports:
- "3306:3306" - "3306:3306"
options: >-
--mount type=tmpfs,destination=/bitnami/mysql/data
elasticsearch: elasticsearch:
image: elasticsearch:7.5.0 image: elasticsearch:7.5.0
env: env:
@ -188,7 +191,8 @@ jobs:
- name: run migration tests - name: run migration tests
run: make test-mysql-migration run: make test-mysql-migration
- name: run tests - name: run tests
run: make integration-test-coverage # run: make integration-test-coverage (at the moment, no coverage is really handled)
run: make test-mysql
env: env:
TAGS: bindata TAGS: bindata
RACE_ENABLED: true RACE_ENABLED: true

View File

@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/testlogger"
@ -91,12 +90,11 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
} }
func MainTest(m *testing.M) { func MainTest(m *testing.M) {
log.RegisterEventWriter("test", testlogger.NewTestLoggerWriter) testlogger.Init()
giteaRoot := base.SetupGiteaRoot() giteaRoot := base.SetupGiteaRoot()
if giteaRoot == "" { if giteaRoot == "" {
fmt.Println("Environment variable $GITEA_ROOT not set") testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
os.Exit(1)
} }
giteaBinary := "gitea" giteaBinary := "gitea"
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
@ -104,8 +102,7 @@ func MainTest(m *testing.M) {
} }
setting.AppPath = filepath.Join(giteaRoot, giteaBinary) setting.AppPath = filepath.Join(giteaRoot, giteaBinary)
if _, err := os.Stat(setting.AppPath); err != nil { if _, err := os.Stat(setting.AppPath); err != nil {
fmt.Printf("Could not find gitea binary at %s\n", setting.AppPath) testlogger.Fatalf("Could not find gitea binary at %s\n", setting.AppPath)
os.Exit(1)
} }
giteaConf := os.Getenv("GITEA_CONF") giteaConf := os.Getenv("GITEA_CONF")
@ -122,8 +119,7 @@ func MainTest(m *testing.M) {
tmpDataPath, err := os.MkdirTemp("", "data") tmpDataPath, err := os.MkdirTemp("", "data")
if err != nil { if err != nil {
fmt.Printf("Unable to create temporary data path %v\n", err) testlogger.Fatalf("Unable to create temporary data path %v\n", err)
os.Exit(1)
} }
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom") setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
@ -131,8 +127,7 @@ func MainTest(m *testing.M) {
unittest.InitSettings() unittest.InitSettings()
if err = git.InitFull(context.Background()); err != nil { if err = git.InitFull(context.Background()); err != nil {
fmt.Printf("Unable to InitFull: %v\n", err) testlogger.Fatalf("Unable to InitFull: %v\n", err)
os.Exit(1)
} }
setting.LoadDBSetting() setting.LoadDBSetting()
setting.InitLoggersForTest() setting.InitLoggersForTest()

View File

@ -86,6 +86,8 @@ type ColoredValue struct {
colors []ColorAttribute colors []ColorAttribute
} }
var _ fmt.Formatter = (*ColoredValue)(nil)
func (c *ColoredValue) Format(f fmt.State, verb rune) { func (c *ColoredValue) Format(f fmt.State, verb rune) {
_, _ = f.Write(ColorBytes(c.colors...)) _, _ = f.Write(ColorBytes(c.colors...))
s := fmt.Sprintf(fmt.FormatString(f, verb), c.v) s := fmt.Sprintf(fmt.FormatString(f, verb), c.v)
@ -93,6 +95,10 @@ func (c *ColoredValue) Format(f fmt.State, verb rune) {
_, _ = f.Write(resetBytes) _, _ = f.Write(resetBytes)
} }
func (c *ColoredValue) Value() any {
return c.v
}
func NewColoredValue(v any, color ...ColorAttribute) *ColoredValue { func NewColoredValue(v any, color ...ColorAttribute) *ColoredValue {
return &ColoredValue{v: v, colors: color} return &ColoredValue{v: v, colors: color}
} }

View File

@ -23,7 +23,7 @@ var (
) )
func init() { func init() {
unhandledItemRequeueDuration.Store(int64(5 * time.Second)) unhandledItemRequeueDuration.Store(int64(time.Second))
} }
// workerGroup is a group of workers to work with a WorkerPoolQueue // workerGroup is a group of workers to work with a WorkerPoolQueue
@ -104,7 +104,12 @@ func (q *WorkerPoolQueue[T]) doWorkerHandle(batch []T) {
// if none of the items were handled, it should back-off for a few seconds // if none of the items were handled, it should back-off for a few seconds
// in this case the handler (eg: document indexer) may have encountered some errors/failures // in this case the handler (eg: document indexer) may have encountered some errors/failures
if len(unhandled) == len(batch) && unhandledItemRequeueDuration.Load() != 0 { if len(unhandled) == len(batch) && unhandledItemRequeueDuration.Load() != 0 {
if q.isFlushing.Load() {
return // do not requeue items when flushing, since all items failed, requeue them will continue failing.
}
log.Error("Queue %q failed to handle batch of %d items, backoff for a few seconds", q.GetName(), len(batch)) log.Error("Queue %q failed to handle batch of %d items, backoff for a few seconds", q.GetName(), len(batch))
// TODO: ideally it shouldn't "sleep" here (blocks the worker, then blocks flush).
// It could debounce the requeue operation, and try to requeue the items in the future.
select { select {
case <-q.ctxRun.Done(): case <-q.ctxRun.Done():
case <-time.After(time.Duration(unhandledItemRequeueDuration.Load())): case <-time.After(time.Duration(unhandledItemRequeueDuration.Load())):
@ -193,6 +198,9 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
// doFlush flushes the queue: it tries to read all items from the queue and handles them. // doFlush flushes the queue: it tries to read all items from the queue and handles them.
// It is for testing purpose only. It's not designed to work for a cluster. // It is for testing purpose only. It's not designed to work for a cluster.
func (q *WorkerPoolQueue[T]) doFlush(wg *workerGroup[T], flush flushType) { func (q *WorkerPoolQueue[T]) doFlush(wg *workerGroup[T], flush flushType) {
q.isFlushing.Store(true)
defer q.isFlushing.Store(false)
log.Debug("Queue %q starts flushing", q.GetName()) log.Debug("Queue %q starts flushing", q.GetName())
defer log.Debug("Queue %q finishes flushing", q.GetName()) defer log.Debug("Queue %q finishes flushing", q.GetName())
@ -236,6 +244,9 @@ loop:
emptyCounter := 0 emptyCounter := 0
for { for {
select { select {
case <-q.ctxRun.Done():
log.Debug("Queue %q is shutting down", q.GetName())
return
case data, dataOk := <-wg.popItemChan: case data, dataOk := <-wg.popItemChan:
if !dataOk { if !dataOk {
return return
@ -251,9 +262,6 @@ loop:
log.Error("Failed to pop item from queue %q (doFlush): %v", q.GetName(), err) log.Error("Failed to pop item from queue %q (doFlush): %v", q.GetName(), err)
} }
return return
case <-q.ctxRun.Done():
log.Debug("Queue %q is shutting down", q.GetName())
return
case <-time.After(20 * time.Millisecond): case <-time.After(20 * time.Millisecond):
// There is no reliable way to make sure all queue items are consumed by the Flush, there always might be some items stored in some buffers/temp variables. // There is no reliable way to make sure all queue items are consumed by the Flush, there always might be some items stored in some buffers/temp variables.
// If we run Gitea in a cluster, we can even not guarantee all items are consumed in a deterministic instance. // If we run Gitea in a cluster, we can even not guarantee all items are consumed in a deterministic instance.
@ -331,6 +339,15 @@ func (q *WorkerPoolQueue[T]) doRun() {
var batchDispatchC <-chan time.Time = infiniteTimerC var batchDispatchC <-chan time.Time = infiniteTimerC
for { for {
select { select {
case flush := <-q.flushChan:
// before flushing, it needs to try to dispatch the batch to worker first, in case there is no worker running
// after the flushing, there is at least one worker running, so "doFlush" could wait for workers to finish
// since we are already in a "flush" operation, so the dispatching function shouldn't read the flush chan.
q.doDispatchBatchToWorker(wg, skipFlushChan)
q.doFlush(wg, flush)
case <-q.ctxRun.Done():
log.Debug("Queue %q is shutting down", q.GetName())
return
case data, dataOk := <-wg.popItemChan: case data, dataOk := <-wg.popItemChan:
if !dataOk { if !dataOk {
return return
@ -349,20 +366,11 @@ func (q *WorkerPoolQueue[T]) doRun() {
case <-batchDispatchC: case <-batchDispatchC:
batchDispatchC = infiniteTimerC batchDispatchC = infiniteTimerC
q.doDispatchBatchToWorker(wg, q.flushChan) q.doDispatchBatchToWorker(wg, q.flushChan)
case flush := <-q.flushChan:
// before flushing, it needs to try to dispatch the batch to worker first, in case there is no worker running
// after the flushing, there is at least one worker running, so "doFlush" could wait for workers to finish
// since we are already in a "flush" operation, so the dispatching function shouldn't read the flush chan.
q.doDispatchBatchToWorker(wg, skipFlushChan)
q.doFlush(wg, flush)
case err := <-wg.popItemErr: case err := <-wg.popItemErr:
if !q.isCtxRunCanceled() { if !q.isCtxRunCanceled() {
log.Error("Failed to pop item from queue %q (doRun): %v", q.GetName(), err) log.Error("Failed to pop item from queue %q (doRun): %v", q.GetName(), err)
} }
return return
case <-q.ctxRun.Done():
log.Debug("Queue %q is shutting down", q.GetName())
return
} }
} }
} }

View File

@ -32,8 +32,9 @@ type WorkerPoolQueue[T any] struct {
baseConfig *BaseConfig baseConfig *BaseConfig
baseQueue baseQueue baseQueue baseQueue
batchChan chan []T batchChan chan []T
flushChan chan flushType flushChan chan flushType
isFlushing atomic.Bool
batchLength int batchLength int
workerNum int workerNum int

View File

@ -19,9 +19,10 @@ import (
) )
var ( var (
prefix string prefix string
SlowTest = 10 * time.Second TestTimeout = 10 * time.Minute
SlowFlush = 5 * time.Second TestSlowRun = 10 * time.Second
TestSlowFlush = 1 * time.Second
) )
var WriterCloser = &testLoggerWriterCloser{} var WriterCloser = &testLoggerWriterCloser{}
@ -89,79 +90,97 @@ func (w *testLoggerWriterCloser) Reset() {
w.Unlock() w.Unlock()
} }
// Printf takes a format and args and prints the string to os.Stdout
func Printf(format string, args ...any) {
if !log.CanColorStdout {
for i := 0; i < len(args); i++ {
if c, ok := args[i].(*log.ColoredValue); ok {
args[i] = c.Value()
}
}
}
_, _ = fmt.Fprintf(os.Stdout, format, args...)
}
// PrintCurrentTest prints the current test to os.Stdout // PrintCurrentTest prints the current test to os.Stdout
func PrintCurrentTest(t testing.TB, skip ...int) func() { func PrintCurrentTest(t testing.TB, skip ...int) func() {
t.Helper() t.Helper()
start := time.Now() runStart := time.Now()
actualSkip := util.OptionalArg(skip) + 1 actualSkip := util.OptionalArg(skip) + 1
_, filename, line, _ := runtime.Caller(actualSkip) _, filename, line, _ := runtime.Caller(actualSkip)
if log.CanColorStdout { Printf("=== %s (%s:%d)\n", log.NewColoredValue(t.Name()), strings.TrimPrefix(filename, prefix), line)
_, _ = fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", fmt.Formatter(log.NewColoredValue(t.Name())), strings.TrimPrefix(filename, prefix), line)
} else {
_, _ = fmt.Fprintf(os.Stdout, "=== %s (%s:%d)\n", t.Name(), strings.TrimPrefix(filename, prefix), line)
}
WriterCloser.pushT(t) WriterCloser.pushT(t)
return func() { timeoutChecker := time.AfterFunc(TestTimeout, func() {
took := time.Since(start) l := 128 * 1024
if took > SlowTest { var stack []byte
if log.CanColorStdout { for {
_, _ = fmt.Fprintf(os.Stdout, "+++ %s is a slow test (took %v)\n", fmt.Formatter(log.NewColoredValue(t.Name(), log.Bold, log.FgYellow)), fmt.Formatter(log.NewColoredValue(took, log.Bold, log.FgYellow))) stack = make([]byte, l)
} else { n := runtime.Stack(stack, true)
_, _ = fmt.Fprintf(os.Stdout, "+++ %s is a slow test (took %v)\n", t.Name(), took) if n <= l {
stack = stack[:n]
break
} }
l = n
} }
timer := time.AfterFunc(SlowFlush, func() { Printf("!!! %s ... timeout: %v ... stacktrace:\n%s\n\n", log.NewColoredValue(t.Name(), log.Bold, log.FgRed), TestTimeout, string(stack))
if log.CanColorStdout { })
_, _ = fmt.Fprintf(os.Stdout, "+++ %s ... still flushing after %v ...\n", fmt.Formatter(log.NewColoredValue(t.Name(), log.Bold, log.FgRed)), SlowFlush) return func() {
} else { flushStart := time.Now()
_, _ = fmt.Fprintf(os.Stdout, "+++ %s ... still flushing after %v ...\n", t.Name(), SlowFlush) slowFlushChecker := time.AfterFunc(TestSlowFlush, func() {
} Printf("+++ %s ... still flushing after %v ...\n", log.NewColoredValue(t.Name(), log.Bold, log.FgRed), TestSlowFlush)
}) })
if err := queue.GetManager().FlushAll(context.Background(), -1); err != nil { if err := queue.GetManager().FlushAll(context.Background(), -1); err != nil {
t.Errorf("Flushing queues failed with error %v", err) t.Errorf("Flushing queues failed with error %v", err)
} }
timer.Stop() slowFlushChecker.Stop()
flushTook := time.Since(start) - took timeoutChecker.Stop()
if flushTook > SlowFlush {
if log.CanColorStdout { runDuration := time.Since(runStart)
_, _ = fmt.Fprintf(os.Stdout, "+++ %s had a slow clean-up flush (took %v)\n", fmt.Formatter(log.NewColoredValue(t.Name(), log.Bold, log.FgRed)), fmt.Formatter(log.NewColoredValue(flushTook, log.Bold, log.FgRed))) flushDuration := time.Since(flushStart)
} else { if runDuration > TestSlowRun {
_, _ = fmt.Fprintf(os.Stdout, "+++ %s had a slow clean-up flush (took %v)\n", t.Name(), flushTook) Printf("+++ %s is a slow test (run: %v, flush: %v)\n", log.NewColoredValue(t.Name(), log.Bold, log.FgYellow), runDuration, flushDuration)
}
} }
WriterCloser.popT() WriterCloser.popT()
} }
} }
// Printf takes a format and args and prints the string to os.Stdout
func Printf(format string, args ...any) {
if log.CanColorStdout {
for i := 0; i < len(args); i++ {
args[i] = log.NewColoredValue(args[i])
}
}
_, _ = fmt.Fprintf(os.Stdout, "\t"+format, args...)
}
// TestLogEventWriter is a logger which will write to the testing log // TestLogEventWriter is a logger which will write to the testing log
type TestLogEventWriter struct { type TestLogEventWriter struct {
*log.EventWriterBaseImpl *log.EventWriterBaseImpl
} }
// NewTestLoggerWriter creates a TestLogEventWriter as a log.LoggerProvider // newTestLoggerWriter creates a TestLogEventWriter as a log.LoggerProvider
func NewTestLoggerWriter(name string, mode log.WriterMode) log.EventWriter { func newTestLoggerWriter(name string, mode log.WriterMode) log.EventWriter {
w := &TestLogEventWriter{} w := &TestLogEventWriter{}
w.EventWriterBaseImpl = log.NewEventWriterBase(name, "test-log-writer", mode) w.EventWriterBaseImpl = log.NewEventWriterBase(name, "test-log-writer", mode)
w.OutputWriteCloser = WriterCloser w.OutputWriteCloser = WriterCloser
return w return w
} }
func init() { func Init() {
const relFilePath = "modules/testlogger/testlogger.go" const relFilePath = "modules/testlogger/testlogger.go"
_, filename, _, _ := runtime.Caller(0) _, filename, _, _ := runtime.Caller(0)
if !strings.HasSuffix(filename, relFilePath) { if !strings.HasSuffix(filename, relFilePath) {
panic("source code file path doesn't match expected: " + relFilePath) panic("source code file path doesn't match expected: " + relFilePath)
} }
prefix = strings.TrimSuffix(filename, relFilePath) prefix = strings.TrimSuffix(filename, relFilePath)
log.RegisterEventWriter("test", newTestLoggerWriter)
duration, err := time.ParseDuration(os.Getenv("GITEA_TEST_SLOW_RUN"))
if err == nil && duration > 0 {
TestSlowRun = duration
}
duration, err = time.ParseDuration(os.Getenv("GITEA_TEST_SLOW_FLUSH"))
if err == nil && duration > 0 {
TestSlowFlush = duration
}
}
func Fatalf(format string, args ...any) {
Printf(format+"\n", args...)
os.Exit(1)
} }

View File

@ -99,18 +99,8 @@ We appreciate that some testing machines may not be very powerful and
the default timeouts for declaring a slow test or a slow clean-up flush the default timeouts for declaring a slow test or a slow clean-up flush
may not be appropriate. may not be appropriate.
You can either: You can set the following environment variables:
* Within the test ini file set the following section:
```ini
[integration-tests]
SLOW_TEST = 10s ; 10s is the default value
SLOW_FLUSH = 5S ; 5s is the default value
```
* Set the following environment variables:
```bash ```bash
GITEA_SLOW_TEST_TIME="10s" GITEA_SLOW_FLUSH_TIME="5s" make test-sqlite GITEA_TEST_SLOW_RUN="10s" GITEA_TEST_SLOW_FLUSH="1s" make test-sqlite
``` ```

View File

@ -39,11 +39,11 @@ func TestAPIGetRawFileOrLFS(t *testing.T) {
t.Run("Partial Clone", doPartialGitClone(dstPath2, u)) t.Run("Partial Clone", doPartialGitClone(dstPath2, u))
lfs := lfsCommitAndPushTest(t, dstPath, littleSize)[0] lfs := lfsCommitAndPushTest(t, dstPath, testFileSizeSmall)[0]
reqLFS := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/media/"+lfs) reqLFS := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/media/"+lfs)
respLFS := MakeRequestNilResponseRecorder(t, reqLFS, http.StatusOK) respLFS := MakeRequestNilResponseRecorder(t, reqLFS, http.StatusOK)
assert.Equal(t, littleSize, respLFS.Length) assert.Equal(t, testFileSizeSmall, respLFS.Length)
doAPIDeleteRepository(httpContext) doAPIDeleteRepository(httpContext)
}) })

View File

@ -4,9 +4,10 @@
package integration package integration
import ( import (
"crypto/rand"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io"
mathRand "math/rand/v2"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -34,8 +35,8 @@ import (
) )
const ( const (
littleSize = 1024 // 1K testFileSizeSmall = 10
bigSize = 128 * 1024 * 1024 // 128M testFileSizeLarge = 10 * 1024 * 1024 // 10M
) )
func TestGitGeneral(t *testing.T) { func TestGitGeneral(t *testing.T) {
@ -73,8 +74,8 @@ func testGitGeneral(t *testing.T, u *url.URL) {
t.Run("Partial Clone", doPartialGitClone(dstPath2, u)) t.Run("Partial Clone", doPartialGitClone(dstPath2, u))
pushedFilesStandard := standardCommitAndPushTest(t, dstPath, littleSize, bigSize) pushedFilesStandard := standardCommitAndPushTest(t, dstPath, testFileSizeSmall, testFileSizeLarge)
pushedFilesLFS := lfsCommitAndPushTest(t, dstPath, littleSize, bigSize) pushedFilesLFS := lfsCommitAndPushTest(t, dstPath, testFileSizeSmall, testFileSizeLarge)
rawTest(t, &httpContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1]) rawTest(t, &httpContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1])
mediaTest(t, &httpContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1]) mediaTest(t, &httpContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1])
@ -114,8 +115,8 @@ func testGitGeneral(t *testing.T, u *url.URL) {
t.Run("Clone", doGitClone(dstPath, sshURL)) t.Run("Clone", doGitClone(dstPath, sshURL))
pushedFilesStandard := standardCommitAndPushTest(t, dstPath, littleSize, bigSize) pushedFilesStandard := standardCommitAndPushTest(t, dstPath, testFileSizeSmall, testFileSizeLarge)
pushedFilesLFS := lfsCommitAndPushTest(t, dstPath, littleSize, bigSize) pushedFilesLFS := lfsCommitAndPushTest(t, dstPath, testFileSizeSmall, testFileSizeLarge)
rawTest(t, &sshContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1]) rawTest(t, &sshContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1])
mediaTest(t, &sshContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1]) mediaTest(t, &sshContext, pushedFilesStandard[0], pushedFilesStandard[1], pushedFilesLFS[0], pushedFilesLFS[1])
@ -202,14 +203,14 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
// Request raw paths // Request raw paths
req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little)) req := NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", little))
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, littleSize, resp.Length) assert.Equal(t, testFileSizeSmall, resp.Length)
if setting.LFS.StartServer { if setting.LFS.StartServer {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
assert.NotEqual(t, littleSize, resp.Body.Len()) assert.NotEqual(t, testFileSizeSmall, resp.Body.Len())
assert.LessOrEqual(t, resp.Body.Len(), 1024) assert.LessOrEqual(t, resp.Body.Len(), 1024)
if resp.Body.Len() != littleSize && resp.Body.Len() <= 1024 { if resp.Body.Len() != testFileSizeSmall && resp.Body.Len() <= 1024 {
assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier) assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier)
} }
} }
@ -217,13 +218,13 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
if !testing.Short() { if !testing.Short() {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, bigSize, resp.Length) assert.Equal(t, testFileSizeLarge, resp.Length)
if setting.LFS.StartServer { if setting.LFS.StartServer {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
assert.NotEqual(t, bigSize, resp.Body.Len()) assert.NotEqual(t, testFileSizeLarge, resp.Body.Len())
if resp.Body.Len() != bigSize && resp.Body.Len() <= 1024 { if resp.Body.Len() != testFileSizeLarge && resp.Body.Len() <= 1024 {
assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier) assert.Contains(t, resp.Body.String(), lfs.MetaFileIdentifier)
} }
} }
@ -243,21 +244,21 @@ func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS
// Request media paths // Request media paths
req := NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", little)) req := NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", little))
resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, littleSize, resp.Length) assert.Equal(t, testFileSizeSmall, resp.Length)
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, littleSize, resp.Length) assert.Equal(t, testFileSizeSmall, resp.Length)
if !testing.Short() { if !testing.Short() {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, bigSize, resp.Length) assert.Equal(t, testFileSizeLarge, resp.Length)
if setting.LFS.StartServer { if setting.LFS.StartServer {
req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS)) req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK) resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
assert.Equal(t, bigSize, resp.Length) assert.Equal(t, testFileSizeLarge, resp.Length)
} }
} }
}) })
@ -287,35 +288,19 @@ func doCommitAndPush(t *testing.T, size int, repoPath, prefix string) string {
} }
func generateCommitWithNewData(size int, repoPath, email, fullName, prefix string) (string, error) { func generateCommitWithNewData(size int, repoPath, email, fullName, prefix string) (string, error) {
// Generate random file
bufSize := 4 * 1024
if bufSize > size {
bufSize = size
}
buffer := make([]byte, bufSize)
tmpFile, err := os.CreateTemp(repoPath, prefix) tmpFile, err := os.CreateTemp(repoPath, prefix)
if err != nil { if err != nil {
return "", err return "", err
} }
defer tmpFile.Close() defer tmpFile.Close()
written := 0
for written < size { var seed [32]byte
n := size - written rander := mathRand.NewChaCha8(seed) // for testing only, no need to seed
if n > bufSize { _, err = io.CopyN(tmpFile, rander, int64(size))
n = bufSize if err != nil {
} return "", err
_, err := rand.Read(buffer[:n])
if err != nil {
return "", err
}
n, err = tmpFile.Write(buffer[:n])
if err != nil {
return "", err
}
written += n
} }
_ = tmpFile.Close()
// Commit // Commit
// Now here we should explicitly allow lfs filters to run // Now here we should explicitly allow lfs filters to run
@ -355,7 +340,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
// Try to push without permissions, which should fail // Try to push without permissions, which should fail
t.Run("TryPushWithoutPermissions", func(t *testing.T) { t.Run("TryPushWithoutPermissions", func(t *testing.T) {
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err) assert.NoError(t, err)
doGitPushTestRepositoryFail(dstPath, "origin", "protected") doGitPushTestRepositoryFail(dstPath, "origin", "protected")
}) })
@ -367,7 +352,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
// Normal push should work // Normal push should work
t.Run("NormalPushWithPermissions", func(t *testing.T) { t.Run("NormalPushWithPermissions", func(t *testing.T) {
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err) assert.NoError(t, err)
doGitPushTestRepository(dstPath, "origin", "protected") doGitPushTestRepository(dstPath, "origin", "protected")
}) })
@ -376,7 +361,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
t.Run("ForcePushWithoutForcePermissions", func(t *testing.T) { t.Run("ForcePushWithoutForcePermissions", func(t *testing.T) {
t.Run("CreateDivergentHistory", func(t *testing.T) { t.Run("CreateDivergentHistory", func(t *testing.T) {
git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath}) git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-new") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-new")
assert.NoError(t, err) assert.NoError(t, err)
}) })
doGitPushTestRepositoryFail(dstPath, "-f", "origin", "protected") doGitPushTestRepositoryFail(dstPath, "-f", "origin", "protected")
@ -411,7 +396,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("GenerateCommit", func(t *testing.T) { t.Run("GenerateCommit", func(t *testing.T) {
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected-2")) t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected-2"))
@ -426,7 +411,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
t.Run("ProtectProtectedBranchUnprotectedFilePaths", doProtectBranch(ctx, "protected", "", "", "unprotected-file-*")) t.Run("ProtectProtectedBranchUnprotectedFilePaths", doProtectBranch(ctx, "protected", "", "", "unprotected-file-*"))
t.Run("GenerateCommit", func(t *testing.T) { t.Run("GenerateCommit", func(t *testing.T) {
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "unprotected-file-") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "unprotected-file-")
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("PushUnprotectedFilesToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected")) t.Run("PushUnprotectedFilesToProtectedBranch", doGitPushTestRepository(dstPath, "origin", "protected"))
@ -436,7 +421,7 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes
t.Run("CheckoutMaster", doGitCheckoutBranch(dstPath, "master")) t.Run("CheckoutMaster", doGitCheckoutBranch(dstPath, "master"))
t.Run("CreateBranchForced", doGitCreateBranch(dstPath, "toforce")) t.Run("CreateBranchForced", doGitCreateBranch(dstPath, "toforce"))
t.Run("GenerateCommit", func(t *testing.T) { t.Run("GenerateCommit", func(t *testing.T) {
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("FailToForcePushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected")) t.Run("FailToForcePushToProtectedBranch", doGitPushTestRepositoryFail(dstPath, "-f", "origin", "toforce:protected"))
@ -649,7 +634,7 @@ func doAutoPRMerge(baseCtx *APITestContext, dstPath string) func(t *testing.T) {
t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected")) t.Run("CheckoutProtected", doGitCheckoutBranch(dstPath, "protected"))
t.Run("PullProtected", doGitPull(dstPath, "origin", "protected")) t.Run("PullProtected", doGitPull(dstPath, "origin", "protected"))
t.Run("GenerateCommit", func(t *testing.T) { t.Run("GenerateCommit", func(t *testing.T) {
_, err := generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") _, err := generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err) assert.NoError(t, err)
}) })
t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected3")) t.Run("PushToUnprotectedBranch", doGitPushTestRepository(dstPath, "origin", "protected:unprotected3"))

View File

@ -98,7 +98,7 @@ func TestAgitPullPush(t *testing.T) {
doGitCreateBranch(dstPath, "test-agit-push") doGitCreateBranch(dstPath, "test-agit-push")
// commit 1 // commit 1
_, err = generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-") _, err = generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-")
assert.NoError(t, err) assert.NoError(t, err)
// push to create an agit pull request // push to create an agit pull request
@ -115,7 +115,7 @@ func TestAgitPullPush(t *testing.T) {
assert.Equal(t, "test-description", pr.Issue.Content) assert.Equal(t, "test-description", pr.Issue.Content)
// commit 2 // commit 2
_, err = generateCommitWithNewData(littleSize, dstPath, "user2@example.com", "User Two", "branch-data-file-2-") _, err = generateCommitWithNewData(testFileSizeSmall, dstPath, "user2@example.com", "User Two", "branch-data-file-2-")
assert.NoError(t, err) assert.NoError(t, err)
// push 2 // push 2

View File

@ -20,7 +20,6 @@ import (
"strings" "strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
@ -28,7 +27,6 @@ import (
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/testlogger"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers"
@ -90,27 +88,6 @@ func TestMain(m *testing.M) {
tests.InitTest(true) tests.InitTest(true)
testWebRoutes = routers.NormalRoutes() testWebRoutes = routers.NormalRoutes()
// integration test settings...
if setting.CfgProvider != nil {
testingCfg := setting.CfgProvider.Section("integration-tests")
testlogger.SlowTest = testingCfg.Key("SLOW_TEST").MustDuration(testlogger.SlowTest)
testlogger.SlowFlush = testingCfg.Key("SLOW_FLUSH").MustDuration(testlogger.SlowFlush)
}
if os.Getenv("GITEA_SLOW_TEST_TIME") != "" {
duration, err := time.ParseDuration(os.Getenv("GITEA_SLOW_TEST_TIME"))
if err == nil {
testlogger.SlowTest = duration
}
}
if os.Getenv("GITEA_SLOW_FLUSH_TIME") != "" {
duration, err := time.ParseDuration(os.Getenv("GITEA_SLOW_FLUSH_TIME"))
if err == nil {
testlogger.SlowFlush = duration
}
}
os.Unsetenv("GIT_AUTHOR_NAME") os.Unsetenv("GIT_AUTHOR_NAME")
os.Unsetenv("GIT_AUTHOR_EMAIL") os.Unsetenv("GIT_AUTHOR_EMAIL")
os.Unsetenv("GIT_AUTHOR_DATE") os.Unsetenv("GIT_AUTHOR_DATE")
@ -132,8 +109,6 @@ func TestMain(m *testing.M) {
// Instead, "No tests were found", last nonsense log is "According to the configuration, subsequent logs will not be printed to the console" // Instead, "No tests were found", last nonsense log is "According to the configuration, subsequent logs will not be printed to the console"
exitCode := m.Run() exitCode := m.Run()
testlogger.WriterCloser.Reset()
if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil { if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil {
fmt.Printf("util.RemoveAll: %v\n", err) fmt.Printf("util.RemoveAll: %v\n", err)
os.Exit(1) os.Exit(1)

View File

@ -6,6 +6,7 @@ package integration
import ( import (
"context" "context"
"net/url" "net/url"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -19,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/queue"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
files_service "code.gitea.io/gitea/services/repository/files" files_service "code.gitea.io/gitea/services/repository/files"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -218,42 +220,43 @@ func TestLinguist(t *testing.T) {
} }
for i, c := range cases { for i, c := range cases {
repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{ t.Run("Case-"+strconv.Itoa(i), func(t *testing.T) {
Name: "linguist-test", defer tests.PrintCurrentTest(t)()
repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
Name: "linguist-test-" + strconv.Itoa(i),
})
assert.NoError(t, err)
files := []*files_service.ChangeRepoFile{
{
TreePath: ".gitattributes",
ContentReader: strings.NewReader(c.GitAttributesContent),
},
}
files = append(files, c.FilesToAdd...)
for _, f := range files {
f.Operation = "create"
}
_, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user, &files_service.ChangeRepoFilesOptions{
Files: files,
OldBranch: repo.DefaultBranch,
NewBranch: repo.DefaultBranch,
})
assert.NoError(t, err)
assert.NoError(t, stats.UpdateRepoIndexer(repo))
assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 10*time.Second))
stats, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, len(c.FilesToAdd))
assert.NoError(t, err)
languages := make([]string, 0, len(stats))
for _, s := range stats {
languages = append(languages, s.Language)
}
assert.Equal(t, c.ExpectedLanguageOrder, languages, "case %d: unexpected language stats", i)
}) })
assert.NoError(t, err)
files := []*files_service.ChangeRepoFile{
{
TreePath: ".gitattributes",
ContentReader: strings.NewReader(c.GitAttributesContent),
},
}
files = append(files, c.FilesToAdd...)
for _, f := range files {
f.Operation = "create"
}
_, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user, &files_service.ChangeRepoFilesOptions{
Files: files,
OldBranch: repo.DefaultBranch,
NewBranch: repo.DefaultBranch,
})
assert.NoError(t, err)
assert.NoError(t, stats.UpdateRepoIndexer(repo))
assert.NoError(t, queue.GetManager().FlushAll(context.Background(), 10*time.Second))
stats, err := repo_model.GetTopLanguageStats(db.DefaultContext, repo, len(c.FilesToAdd))
assert.NoError(t, err)
languages := make([]string, 0, len(stats))
for _, s := range stats {
languages = append(languages, s.Language)
}
assert.Equal(t, c.ExpectedLanguageOrder, languages, "case %d: unexpected language stats", i)
assert.NoError(t, repo_service.DeleteRepository(db.DefaultContext, user, repo, false))
} }
}) })
} }

View File

@ -28,33 +28,29 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/testlogger"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/xorm" "xorm.io/xorm"
) )
var currentEngine *xorm.Engine var currentEngine *xorm.Engine
func initMigrationTest(t *testing.T) func() { func initMigrationTest(t *testing.T) func() {
log.RegisterEventWriter("test", testlogger.NewTestLoggerWriter) testlogger.Init()
deferFn := tests.PrintCurrentTest(t, 2) deferFn := testlogger.PrintCurrentTest(t, 2)
giteaRoot := base.SetupGiteaRoot() giteaRoot := base.SetupGiteaRoot()
if giteaRoot == "" { if giteaRoot == "" {
tests.Printf("Environment variable $GITEA_ROOT not set\n") testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
os.Exit(1)
} }
setting.AppPath = path.Join(giteaRoot, "gitea") setting.AppPath = path.Join(giteaRoot, "gitea")
if _, err := os.Stat(setting.AppPath); err != nil { if _, err := os.Stat(setting.AppPath); err != nil {
tests.Printf("Could not find gitea binary at %s\n", setting.AppPath) testlogger.Fatalf(fmt.Sprintf("Could not find gitea binary at %s\n", setting.AppPath))
os.Exit(1)
} }
giteaConf := os.Getenv("GITEA_CONF") giteaConf := os.Getenv("GITEA_CONF")
if giteaConf == "" { if giteaConf == "" {
tests.Printf("Environment variable $GITEA_CONF not set\n") testlogger.Fatalf("Environment variable $GITEA_CONF not set\n")
os.Exit(1)
} else if !path.IsAbs(giteaConf) { } else if !path.IsAbs(giteaConf) {
setting.CustomConf = path.Join(giteaRoot, giteaConf) setting.CustomConf = path.Join(giteaRoot, giteaConf)
} else { } else {
@ -123,13 +119,10 @@ func readSQLFromFile(version string) (string, error) {
return string(charset.MaybeRemoveBOM(bytes, charset.ConvertOpts{})), nil return string(charset.MaybeRemoveBOM(bytes, charset.ConvertOpts{})), nil
} }
func restoreOldDB(t *testing.T, version string) bool { func restoreOldDB(t *testing.T, version string) {
data, err := readSQLFromFile(version) data, err := readSQLFromFile(version)
assert.NoError(t, err) require.NoError(t, err)
if len(data) == 0 { require.NotEmpty(t, data, "No data found for %s version: %s", setting.Database.Type, version)
tests.Printf("No db found to restore for %s version: %s\n", setting.Database.Type, version)
return false
}
switch { switch {
case setting.Database.Type.IsSQLite3(): case setting.Database.Type.IsSQLite3():
@ -197,15 +190,12 @@ func restoreOldDB(t *testing.T, version string) bool {
db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s",
setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode)) setting.Database.User, setting.Database.Passwd, setting.Database.Host, setting.Database.Name, setting.Database.SSLMode))
} }
if !assert.NoError(t, err) { require.NoError(t, err)
return false
}
defer db.Close() defer db.Close()
schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema))
if !assert.NoError(t, err) || !assert.NotEmpty(t, schrows) { require.NoError(t, err)
return false require.NotEmpty(t, schrows)
}
if !schrows.Next() { if !schrows.Next() {
// Create and setup a DB schema // Create and setup a DB schema
@ -260,7 +250,6 @@ func restoreOldDB(t *testing.T, version string) bool {
} }
db.Close() db.Close()
} }
return true
} }
func wrappedMigrate(x *xorm.Engine) error { func wrappedMigrate(x *xorm.Engine) error {
@ -269,11 +258,8 @@ func wrappedMigrate(x *xorm.Engine) error {
} }
func doMigrationTest(t *testing.T, version string) { func doMigrationTest(t *testing.T, version string) {
defer tests.PrintCurrentTest(t)() defer testlogger.PrintCurrentTest(t)()
tests.Printf("Performing migration test for %s version: %s\n", setting.Database.Type, version) restoreOldDB(t, version)
if !restoreOldDB(t, version) {
return
}
setting.InitSQLLoggersForCli(log.INFO) setting.InitSQLLoggersForCli(log.INFO)
@ -305,14 +291,9 @@ func TestMigrations(t *testing.T) {
dialect := setting.Database.Type dialect := setting.Database.Type
versions, err := availableVersions() versions, err := availableVersions()
assert.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, versions, "No old database versions available to migration test for %s", dialect)
if len(versions) == 0 {
tests.Printf("No old database versions available to migration test for %s\n", dialect)
return
}
tests.Printf("Preparing to test %d migrations for %s\n", len(versions), dialect)
for _, version := range versions { for _, version := range versions {
t.Run(fmt.Sprintf("Migrate-%s-%s", dialect, version), func(t *testing.T) { t.Run(fmt.Sprintf("Migrate-%s-%s", dialect, version), func(t *testing.T) {
doMigrationTest(t, version) doMigrationTest(t, version)

View File

@ -209,8 +209,6 @@ func checkRecentlyPushedNewBranches(t *testing.T, session *TestSession, repoPath
} }
func TestRecentlyPushedNewBranches(t *testing.T) { func TestRecentlyPushedNewBranches(t *testing.T) {
defer tests.PrepareTestEnv(t)()
onGiteaRun(t, func(t *testing.T, u *url.URL) { onGiteaRun(t, func(t *testing.T, u *url.URL) {
user1Session := loginUser(t, "user1") user1Session := loginUser(t, "user1")
user2Session := loginUser(t, "user2") user2Session := loginUser(t, "user2")

View File

@ -29,17 +29,12 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func exitf(format string, args ...any) {
fmt.Printf(format+"\n", args...)
os.Exit(1)
}
func InitTest(requireGitea bool) { func InitTest(requireGitea bool) {
log.RegisterEventWriter("test", testlogger.NewTestLoggerWriter) testlogger.Init()
giteaRoot := base.SetupGiteaRoot() giteaRoot := base.SetupGiteaRoot()
if giteaRoot == "" { if giteaRoot == "" {
exitf("Environment variable $GITEA_ROOT not set") testlogger.Fatalf("Environment variable $GITEA_ROOT not set\n")
} }
// TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure. // TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure.
@ -54,7 +49,7 @@ func InitTest(requireGitea bool) {
} }
setting.AppPath = filepath.Join(giteaRoot, giteaBinary) setting.AppPath = filepath.Join(giteaRoot, giteaBinary)
if _, err := os.Stat(setting.AppPath); err != nil { if _, err := os.Stat(setting.AppPath); err != nil {
exitf("Could not find gitea binary at %s", setting.AppPath) testlogger.Fatalf("Could not find gitea binary at %s\n", setting.AppPath)
} }
} }
giteaConf := os.Getenv("GITEA_CONF") giteaConf := os.Getenv("GITEA_CONF")
@ -66,7 +61,7 @@ func InitTest(requireGitea bool) {
_ = os.Setenv("GITEA_CONF", giteaConf) _ = os.Setenv("GITEA_CONF", giteaConf)
fmt.Printf("Environment variable $GITEA_CONF not set, use default: %s\n", giteaConf) fmt.Printf("Environment variable $GITEA_CONF not set, use default: %s\n", giteaConf)
if !setting.EnableSQLite3 { if !setting.EnableSQLite3 {
exitf(`sqlite3 requires: import _ "github.com/mattn/go-sqlite3" or -tags sqlite,sqlite_unlock_notify`) testlogger.Fatalf(`sqlite3 requires: import _ "github.com/mattn/go-sqlite3" or -tags sqlite,sqlite_unlock_notify` + "\n")
} }
} }
if !filepath.IsAbs(giteaConf) { if !filepath.IsAbs(giteaConf) {
@ -85,7 +80,7 @@ func InitTest(requireGitea bool) {
setting.LoadDBSetting() setting.LoadDBSetting()
if err := storage.Init(); err != nil { if err := storage.Init(); err != nil {
exitf("Init storage failed: %v", err) testlogger.Fatalf("Init storage failed: %v\n", err)
} }
switch { switch {
@ -258,8 +253,3 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() {
t.Helper() t.Helper()
return testlogger.PrintCurrentTest(t, util.OptionalArg(skip)+1) return testlogger.PrintCurrentTest(t, util.OptionalArg(skip)+1)
} }
// Printf takes a format and args and prints the string to os.Stdout
func Printf(format string, args ...any) {
testlogger.Printf(format, args...)
}