diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index b822b2d74e5..c3183061928 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -91,7 +91,7 @@ func deleteDB() error { defer schrows.Close() if !schrows.Next() { - // Create and setup a DB schema + // Create and set up a DB schema _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema) if err != nil { return err @@ -134,7 +134,8 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu ourSkip := 2 ourSkip += skip deferFn := testlogger.PrintCurrentTest(t, ourSkip) - require.NoError(t, unittest.SyncDirs(filepath.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath)) + giteaRoot := setting.GetGiteaTestSourceRoot() + require.NoError(t, unittest.SyncDirs(filepath.Join(giteaRoot, "tests/gitea-repositories-meta"), setting.RepoRootPath)) if err := deleteDB(); err != nil { t.Fatalf("unable to reset database: %v", err) @@ -202,17 +203,18 @@ func LoadTableSchemasMap(t *testing.T, x *xorm.Engine) map[string]*schemas.Table func mainTest(m *testing.M) int { testlogger.Init() - setting.SetupGiteaTestEnv() - tmpDataPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-") + tempWorkPath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("migration-test-data-") if err != nil { - testlogger.Panicf("Unable to create temporary data path %v\n", err) + return testlogger.MainErrorf("Unable to create temporary dir for migration test: %v", err) } defer cleanup() - setting.AppDataPath = tmpDataPath + + setting.MockBuiltinPaths(tempWorkPath, "", "") + setting.SetupGiteaTestEnv() if err = git.InitFull(); err != nil { - testlogger.Panicf("Unable to InitFull: %v\n", err) + return testlogger.MainErrorf("Unable to InitFull: %v", err) } setting.LoadDBSetting() setting.InitLoggersForTest() diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 82c8d181ac2..bd832348e7c 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/setting/config" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/tempdir" + "code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -40,29 +41,21 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) { } func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { - reportError := func(msg string, a ...any) int { - _, _ = fmt.Fprintf(os.Stderr, msg+"\n", a...) - return 1 - } - testOpts := util.OptionalArg(testOptsArg, &TestOptions{}) tempWorkPath, tempCleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("unit-test-dir-") if err != nil { - return reportError("Failed to create temp dir for unit test: %v", err) + return testlogger.MainErrorf("Failed to create temp dir for unit test: %v", err) } defer tempCleanup() defer setting.MockBuiltinPaths(tempWorkPath, "", "")() setting.SetupGiteaTestEnv() - if setting.RepoRootPath == "" || setting.AppDataPath == "" { - return reportError("SetupGiteaTestEnv failed, paths are not initialized") - } giteaRoot := setting.GetGiteaTestSourceRoot() fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles} if err := CreateTestEngine(fixturesOpts); err != nil { - return reportError("Error creating test database engine: %v", err) + return testlogger.MainErrorf("Error creating test database engine: %v", err) } setting.AppURL = "https://try.gitea.io/" @@ -80,22 +73,22 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { config.SetDynGetter(system.NewDatabaseDynKeyGetter()) if err = cache.Init(); err != nil { - return reportError("cache.Init: %v", err) + return testlogger.MainErrorf("cache.Init: %v", err) } if err = storage.Init(); err != nil { - return reportError("storage.Init: %v", err) + return testlogger.MainErrorf("storage.Init: %v", err) } if err = SyncDirs(filepath.Join(giteaRoot, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil { - return reportError("util.SyncDirs: %v", err) + return testlogger.MainErrorf("util.SyncDirs: %v", err) } if err = git.InitFull(); err != nil { - return reportError("git.Init: %v", err) + return testlogger.MainErrorf("git.Init: %v", err) } if testOpts.SetUp != nil { if err := testOpts.SetUp(); err != nil { - return reportError("set up failed: %v", err) + return testlogger.MainErrorf("set up failed: %v", err) } } @@ -103,7 +96,7 @@ func mainTest(m *testing.M, testOptsArg ...*TestOptions) int { if testOpts.TearDown != nil { if err := testOpts.TearDown(); err != nil { - return reportError("tear down failed: %v", err) + return testlogger.MainErrorf("tear down failed: %v", err) } } return exitStatus diff --git a/modules/git/git.go b/modules/git/git.go index 2df83f9843a..d70d2446010 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -192,13 +192,13 @@ func RunGitTests(m interface{ Run() int }) { func runGitTests(m interface{ Run() int }) int { gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") if err != nil { - testlogger.Panicf("unable to create temp dir: %s", err.Error()) + return testlogger.MainErrorf("unable to create temp dir: %v", err) } defer cleanup() setting.Git.HomePath = gitHomePath if err = InitFull(); err != nil { - testlogger.Panicf("failed to call Init: %s", err.Error()) + return testlogger.MainErrorf("failed to call Init: %s", err) } return m.Run() } diff --git a/modules/git/gitcmd/command_test.go b/modules/git/gitcmd/command_test.go index 6e4214d9953..19ec02b8088 100644 --- a/modules/git/gitcmd/command_test.go +++ b/modules/git/gitcmd/command_test.go @@ -24,7 +24,7 @@ func testMain(m *testing.M) int { // "setting.Git.HomePath" is initialized in "git" package but really used in "gitcmd" package gitHomePath, cleanup, err := tempdir.OsTempDir("gitea-test").MkdirTempRandom("git-home") if err != nil { - testlogger.Panicf("failed to create temp dir: %v", err) + return testlogger.MainErrorf("failed to create temp dir: %v", err) } defer cleanup() diff --git a/modules/setting/testenv.go b/modules/setting/testenv.go index 1c06fbb6809..2e9d786b036 100644 --- a/modules/setting/testenv.go +++ b/modules/setting/testenv.go @@ -101,6 +101,10 @@ func SetupGiteaTestEnv() { cleanUpEnv() initWorkPathAndConfig() + if RepoRootPath == "" || AppDataPath == "" { + panic("SetupGiteaTestEnv failed, paths are not initialized") + } + // TODO: some git repo hooks (test fixtures) still use these env variables, need to be refactored in the future _ = os.Setenv("GITEA_ROOT", giteaRoot) _ = os.Setenv("GITEA_CONF", giteaConf) // test fixture git hooks use "$GITEA_ROOT/$GITEA_CONF" in their scripts diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index 217121f604b..2eeede878fa 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -173,7 +173,8 @@ func Init() { log.RegisterEventWriter("test", newTestLoggerWriter) } -func Panicf(format string, args ...any) { - // don't call os.Exit, otherwise the "defer" functions won't be executed - panic(fmt.Sprintf(format, args...)) +// MainErrorf is used to report an error from TestMain and return a non-zero value to indicate the failure +func MainErrorf(msg string, a ...any) int { + _, _ = fmt.Fprintf(os.Stderr, msg+"\n", a...) + return 1 } diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 33be7d89cb6..d0a33fdec8c 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -87,16 +87,19 @@ func testMain(m *testing.M) int { graceful.InitManager(managerCtx) defer cancel() - tests.InitTest() + err := tests.InitTest() + if err != nil { + return testlogger.MainErrorf("InitTest error: %v", err) + } testWebRoutes = routers.NormalRoutes() - err := unittest.InitFixtures( + err = unittest.InitFixtures( unittest.FixturesOptions{ Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"), }, ) if err != nil { - testlogger.Panicf("InitFixtures: %v", err) + return testlogger.MainErrorf("InitFixtures: %v", err) } // FIXME: the console logger is deleted by mistake, so if there is any `log.Fatal`, developers won't see any error message. diff --git a/tests/test_utils.go b/tests/test_utils.go index b724a30610a..7ff7dd7b8ab 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -8,6 +8,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "testing" "code.gitea.io/gitea/models/db" @@ -15,7 +16,6 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/testlogger" @@ -25,8 +25,9 @@ import ( "github.com/stretchr/testify/assert" ) -func InitTest() { +func InitTest() error { testlogger.Init() + if os.Getenv("GITEA_TEST_CONF") == "" { // By default, use sqlite.ini for testing, then IDE like GoLand can start the test process with debugger. // It's easier for developers to debug bugs step by step with a debugger. @@ -39,101 +40,102 @@ func InitTest() { setting.Repository.DefaultBranch = "master" // many test code still assume that default branch is called "master" if err := git.InitFull(); err != nil { - log.Fatal("git.InitOnceWithSync: %v", err) + return err } setting.LoadDBSetting() if err := storage.Init(); err != nil { - testlogger.Panicf("Init storage failed: %v\n", err) + return err } switch { case setting.Database.Type.IsMySQL(): - connType := "tcp" - if len(setting.Database.Host) > 0 && setting.Database.Host[0] == '/' { // looks like a unix socket - connType = "unix" - } - - db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/", - setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host)) - defer db.Close() - if err != nil { - log.Fatal("sql.Open: %v", err) - } - if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil { - log.Fatal("db.Exec: %v", err) - } - case setting.Database.Type.IsPostgreSQL(): - var db *sql.DB - var err error - if setting.Database.Host[0] == '/' { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s", - setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host)) - } else { - 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)) - } - - defer db.Close() - if err != nil { - log.Fatal("sql.Open: %v", err) - } - dbrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name)) - if err != nil { - log.Fatal("db.Query: %v", err) - } - defer dbrows.Close() - - if !dbrows.Next() { - if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil { - log.Fatal("db.Exec: CREATE DATABASE: %v", err) + { + connType := util.Iif(strings.HasPrefix(setting.Database.Host, "/"), "unix", "tcp") + db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s)/", + setting.Database.User, setting.Database.Passwd, connType, setting.Database.Host)) + if err != nil { + return err + } + defer db.Close() + if _, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + setting.Database.Name); err != nil { + return err } } - // Check if we need to setup a specific schema - if len(setting.Database.Schema) == 0 { - break - } - db.Close() - - if setting.Database.Host[0] == '/' { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s", - setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host)) - } else { - db, err = sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", + case setting.Database.Type.IsPostgreSQL(): + openPostgreSQL := func() (*sql.DB, error) { + if strings.HasPrefix(setting.Database.Host, "/") { + return sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@/%s?sslmode=%s&host=%s", + setting.Database.User, setting.Database.Passwd, setting.Database.Name, setting.Database.SSLMode, setting.Database.Host)) + } + return 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)) } - // This is a different db object; requires a different Close() - defer db.Close() - if err != nil { - log.Fatal("sql.Open: %v", err) - } - schrows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) - if err != nil { - log.Fatal("db.Query: %v", err) - } - defer schrows.Close() - if !schrows.Next() { - // Create and setup a DB schema - if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil { - log.Fatal("db.Exec: CREATE SCHEMA: %v", err) + // create database + { + db, err := openPostgreSQL() + if err != nil { + return err + } + defer db.Close() + dbRows, err := db.Query(fmt.Sprintf("SELECT 1 FROM pg_database WHERE datname = '%s'", setting.Database.Name)) + if err != nil { + return err + } + defer dbRows.Close() + + if !dbRows.Next() { + if _, err = db.Exec("CREATE DATABASE " + setting.Database.Name); err != nil { + return err + } + } + // Check if we need to set up a specific schema + if setting.Database.Schema == "" { + break + } + db.Close() + } + + // create schema + { + db, err := openPostgreSQL() + if err != nil { + return err + } + defer db.Close() + + schemaRows, err := db.Query(fmt.Sprintf("SELECT 1 FROM information_schema.schemata WHERE schema_name = '%s'", setting.Database.Schema)) + if err != nil { + return err + } + defer schemaRows.Close() + + if !schemaRows.Next() { + // Create and set up a DB schema + if _, err = db.Exec("CREATE SCHEMA " + setting.Database.Schema); err != nil { + return err + } } } case setting.Database.Type.IsMSSQL(): - host, port := setting.ParseMSSQLHostPort(setting.Database.Host) - db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", - host, port, "master", setting.Database.User, setting.Database.Passwd)) - if err != nil { - log.Fatal("sql.Open: %v", err) + { + host, port := setting.ParseMSSQLHostPort(setting.Database.Host) + db, err := sql.Open("mssql", fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", + host, port, "master", setting.Database.User, setting.Database.Passwd)) + if err != nil { + return err + } + defer db.Close() + if _, err = db.Exec(fmt.Sprintf("If(db_id(N'%s') IS NULL) BEGIN CREATE DATABASE %s; END;", setting.Database.Name, setting.Database.Name)); err != nil { + return err + } } - if _, err := db.Exec(fmt.Sprintf("If(db_id(N'%s') IS NULL) BEGIN CREATE DATABASE %s; END;", setting.Database.Name, setting.Database.Name)); err != nil { - log.Fatal("db.Exec: %v", err) - } - defer db.Close() } routers.InitWebInstalled(graceful.GetManager().HammerContext()) + return nil } func PrepareAttachmentsStorage(t testing.TB) {