diff --git a/.github/workflows/reusable_testing.yml b/.github/workflows/reusable_testing.yml index 54cd379c6..55fe9d862 100644 --- a/.github/workflows/reusable_testing.yml +++ b/.github/workflows/reusable_testing.yml @@ -38,36 +38,8 @@ jobs: with: go-version: 1.13 - - name: Run base tests - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - - - name: Run queue tests - working-directory: ./queue - run: | - mkdir snap xlog - tarantoolctl rocks install queue 1.1.0 - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID + - name: Install test dependencies + run: ./deps.sh - - name: Run uuid tests - working-directory: ./uuid - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - if: ${{ !startsWith(env.TNT_VERSION, 'Tarantool 1.10') }} - - - name: Run multi tests - working-directory: ./multi - run: | - mkdir -p m1/{snap,xlog} m2/{snap,xlog} - TNT_PID_1=$(tarantool ./config_m1.lua > tarantool_m1.log 2>&1 & echo $!) - TNT_PID_2=$(tarantool ./config_m2.lua > tarantool_m2.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID_1 $TNT_PID_2 + - name: Run tests + run: go clean -testcache && go test ./... -v -p 1 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 68cf3d6b0..12b1f1e02 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -39,38 +39,9 @@ jobs: with: go-version: 1.13 - - name: Run base tests - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - - - name: Run queue tests + - name: Install test dependencies working-directory: ./queue - run: | - rm -rf snap - rm -rf xlog - mkdir snap xlog - tarantoolctl rocks install queue 1.1.0 - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - - - name: Run uuid tests - working-directory: ./uuid - run: | - mkdir snap xlog - TNT_PID=$(tarantool ./config.lua > tarantool.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID - if: ${{ matrix.tarantool != 1.10 }} + run: ./deps.sh - - name: Run multi tests - working-directory: ./multi - run: | - mkdir -p m1/{snap,xlog} m2/{snap,xlog} - TNT_PID_1=$(tarantool ./config_m1.lua > tarantool_m1.log 2>&1 & echo $!) - TNT_PID_2=$(tarantool ./config_m2.lua > tarantool_m2.log 2>&1 & echo $!) - go clean -testcache && go test -v - kill $TNT_PID_1 $TNT_PID_2 + - name: Run tests + run: go clean -testcache && go test ./... -v -p 1 diff --git a/.gitignore b/.gitignore index 8fcb790f1..8958050e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.DS_Store *.swp .idea/ -snap -xlog +work_dir* +.rocks diff --git a/config.lua b/config.lua index c2a9ae966..d06e05395 100644 --- a/config.lua +++ b/config.lua @@ -1,7 +1,7 @@ +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. box.cfg{ - listen = 3013, - wal_dir='xlog', - snap_dir='snap', + work_dir = os.getenv("TEST_TNT_WORK_DIR"), } box.once("init", function() @@ -56,7 +56,10 @@ function simple_incr(a) end box.space.test:truncate() -local console = require 'console' -console.listen '0.0.0.0:33015' --box.schema.user.revoke('guest', 'read,write,execute', 'universe') + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/deps.sh b/deps.sh new file mode 100755 index 000000000..557fbc70d --- /dev/null +++ b/deps.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Call this script to install test dependencies for all subpackages. + +set -eux + +# We should install rock dependencies to corresponding directory. +# If you run ./queue/deps.sh instead of cd ./queue; ./deps.sh, +# rock will be installed to current directory instead of ./queue. +cd ./queue; ./deps.sh; cd .. diff --git a/multi/config.lua b/multi/config.lua new file mode 100644 index 000000000..e511cfad9 --- /dev/null +++ b/multi/config.lua @@ -0,0 +1,19 @@ +local nodes_load = require("config_load_nodes") + +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. +box.cfg{ + work_dir = os.getenv("TEST_TNT_WORK_DIR"), +} + +get_cluster_nodes = nodes_load.get_cluster_nodes + +box.once("init", function() + box.schema.user.create('test', { password = 'test' }) + box.schema.user.grant('test', 'read,write,execute', 'universe') +end) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/multi/config_m1.lua b/multi/config_m1.lua deleted file mode 100644 index 5e364b5cc..000000000 --- a/multi/config_m1.lua +++ /dev/null @@ -1,14 +0,0 @@ -local nodes_load = require("config_load_nodes") - -box.cfg { - listen = 3013, - wal_dir = 'm1/xlog', - snap_dir = 'm1/snap', -} - -get_cluster_nodes = nodes_load.get_cluster_nodes - -box.once("init", function() - box.schema.user.create('test', { password = 'test' }) - box.schema.user.grant('test', 'read,write,execute', 'universe') -end) diff --git a/multi/config_m2.lua b/multi/config_m2.lua deleted file mode 100644 index cf73da319..000000000 --- a/multi/config_m2.lua +++ /dev/null @@ -1,14 +0,0 @@ -local nodes_load = require("config_load_nodes") - -box.cfg { - listen = 3014, - wal_dir = 'm2/xlog', - snap_dir = 'm2/snap', -} - -get_cluster_nodes = nodes_load.get_cluster_nodes - -box.once("init", function() - box.schema.user.create('test', { password = 'test' }) - box.schema.user.grant('test', 'read,write,execute', 'universe') -end) diff --git a/multi/multi_test.go b/multi/multi_test.go index 4515c6232..65ae439f8 100644 --- a/multi/multi_test.go +++ b/multi/multi_test.go @@ -1,10 +1,13 @@ package multi import ( + "log" + "os" "testing" "time" "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/test_helpers" ) var server1 = "127.0.0.1:3013" @@ -204,3 +207,54 @@ func TestRefresh(t *testing.T) { t.Error("Expect to get data after reconnect") } } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + initScript := "config.lua" + waitStart := 100 * time.Millisecond + var connectRetry uint = 3 + retryTimeout := 500 * time.Millisecond + + inst1, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: initScript, + Listen: server1, + WorkDir: "work_dir1", + User: connOpts.User, + Pass: connOpts.Pass, + WaitStart: waitStart, + ConnectRetry: connectRetry, + RetryTimeout: retryTimeout, + }) + defer test_helpers.StopTarantoolWithCleanup(inst1) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + inst2, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: initScript, + Listen: server2, + WorkDir: "work_dir2", + User: connOpts.User, + Pass: connOpts.Pass, + WaitStart: waitStart, + ConnectRetry: connectRetry, + RetryTimeout: retryTimeout, + }) + defer test_helpers.StopTarantoolWithCleanup(inst2) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/queue/config.lua b/queue/config.lua index 57aef4d20..767f259ad 100644 --- a/queue/config.lua +++ b/queue/config.lua @@ -1,9 +1,9 @@ -queue = require 'queue' +queue = require('queue') +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. box.cfg{ - listen = 3013, - wal_dir='xlog', - snap_dir='snap', + work_dir = os.getenv("TEST_TNT_WORK_DIR"), } box.once("init", function() @@ -45,3 +45,8 @@ if box.space._func_index ~= nil then box.schema.user.grant('test', 'read', 'space', '_func_index') end end) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/queue/deps.sh b/queue/deps.sh new file mode 100755 index 000000000..1a598bf65 --- /dev/null +++ b/queue/deps.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# Call this script to install test dependencies. + +set -eux + +tarantoolctl rocks install queue 1.1.0 diff --git a/queue/queue_test.go b/queue/queue_test.go index 4419e12eb..db531eb4c 100644 --- a/queue/queue_test.go +++ b/queue/queue_test.go @@ -2,12 +2,15 @@ package queue_test import ( "fmt" + "log" "math" + "os" "testing" "time" . "github.com/tarantool/go-tarantool" "github.com/tarantool/go-tarantool/queue" + "github.com/tarantool/go-tarantool/test_helpers" "gopkg.in/vmihailenco/msgpack.v2" ) @@ -818,3 +821,33 @@ func TestUtube_Put(t *testing.T) { t.Fatalf("Blocking time is less than expected: actual = %.2fs, expected = 1s", end.Sub(start).Seconds()) } } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "config.lua", + Listen: server, + WorkDir: "work_dir", + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 500 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/tarantool_test.go b/tarantool_test.go index ae725fdc7..41bdfe830 100644 --- a/tarantool_test.go +++ b/tarantool_test.go @@ -2,12 +2,15 @@ package tarantool_test import ( "fmt" + "log" + "os" "strings" "sync" "testing" "time" . "github.com/tarantool/go-tarantool" + "github.com/tarantool/go-tarantool/test_helpers" "gopkg.in/vmihailenco/msgpack.v2" ) @@ -1005,3 +1008,33 @@ func TestComplexStructs(t *testing.T) { return } } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "config.lua", + Listen: server, + WorkDir: "work_dir", + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 500 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +} diff --git a/test_helpers/main.go b/test_helpers/main.go new file mode 100644 index 000000000..e5c73bfe5 --- /dev/null +++ b/test_helpers/main.go @@ -0,0 +1,234 @@ +package test_helpers + +import ( + "errors" + "fmt" + "log" + "os" + "os/exec" + "regexp" + "strconv" + "time" + + "github.com/tarantool/go-tarantool" +) + +type StartOpts struct { + // InitScript is a Lua script for tarantool to run on start. + InitScript string + + // Listen is box.cfg listen parameter for tarantool. + // Use this address to connect to tarantool after configuration. + // https://www.tarantool.io/en/doc/latest/reference/configuration/#cfg-basic-listen + Listen string + + // WorkDir is box.cfg work_dir parameter for tarantool. + // Specify folder to store tarantool data files. + // Folder must be unique for each tarantool process used simultaneously. + // https://www.tarantool.io/en/doc/latest/reference/configuration/#confval-work_dir + WorkDir string + + // User is a username used to connect to tarantool. + // All required grants must be given in InitScript. + User string + + // Pass is a password for specified User. + Pass string + + // WaitStart is a time to wait before starting to ping tarantool. + WaitStart time.Duration + + // ConnectRetry is a count of attempts to ping tarantool. + ConnectRetry uint + + // RetryTimeout is a time between tarantool ping retries. + RetryTimeout time.Duration +} + +// TarantoolInstance is a data for instance graceful shutdown and cleanup. +type TarantoolInstance struct { + // Cmd is a Tarantool command. Used to kill Tarantool process. + Cmd *exec.Cmd + + // WorkDir is a directory with tarantool data. Cleaned up after run. + WorkDir string +} + +func isReady(server string, opts *tarantool.Opts) error { + var err error + var conn *tarantool.Connection + var resp *tarantool.Response + + conn, err = tarantool.Connect(server, *opts) + if err != nil { + return err + } + if conn == nil { + return errors.New("Conn is nil after connect") + } + defer conn.Close() + + resp, err = conn.Ping() + if err != nil { + return err + } + if resp == nil { + return errors.New("Response is nil after ping") + } + + return nil +} + +var ( + // Used to extract Tarantool version (major.minor.patch). + tarantoolVersionRegexp *regexp.Regexp +) + +func init() { + tarantoolVersionRegexp = regexp.MustCompile(`Tarantool (?:Enterprise )?(\d+)\.(\d+)\.(\d+).*`) +} + +// atoiUint64 parses string to uint64. +func atoiUint64(str string) (uint64, error) { + res, err := strconv.ParseUint(str, 10, 64) + if err != nil { + return 0, fmt.Errorf("cast to number error (%s)", err) + } + return res, nil +} + +// IsTarantoolVersionLess checks if tarantool version is less +// than passed . Returns error if failed +// to extract version. +func IsTarantoolVersionLess(majorMin uint64, minorMin uint64, patchMin uint64) (bool, error) { + var major, minor, patch uint64 + + out, err := exec.Command("tarantool", "--version").Output() + + if err != nil { + return true, err + } + + parsed := tarantoolVersionRegexp.FindStringSubmatch(string(out)) + + if parsed == nil { + return true, errors.New("regexp parse failed") + } + + if major, err = atoiUint64(parsed[1]); err != nil { + return true, fmt.Errorf("failed to parse major: %s", err) + } + + if minor, err = atoiUint64(parsed[2]); err != nil { + return true, fmt.Errorf("failed to parse minor: %s", err) + } + + if patch, err = atoiUint64(parsed[3]); err != nil { + return true, fmt.Errorf("failed to parse patch: %s", err) + } + + if major != majorMin { + return major < majorMin, nil + } else if minor != minorMin { + return minor < minorMin, nil + } else { + return patch < patchMin, nil + } + + return false, nil +} + +// StartTarantool starts a tarantool instance for tests +// with specifies parameters (refer to StartOpts). +// Process must be stopped with StopTarantool. +func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) { + // Prepare tarantool command. + var inst TarantoolInstance + inst.Cmd = exec.Command("tarantool", startOpts.InitScript) + + inst.Cmd.Env = append( + os.Environ(), + fmt.Sprintf("TEST_TNT_WORK_DIR=%s", startOpts.WorkDir), + fmt.Sprintf("TEST_TNT_LISTEN=%s", startOpts.Listen), + ) + + // Clean up existing work_dir. + err := os.RemoveAll(startOpts.WorkDir) + if err != nil { + return inst, err + } + + // Create work_dir. + err = os.Mkdir(startOpts.WorkDir, 0755) + if err != nil { + return inst, err + } + + inst.WorkDir = startOpts.WorkDir + + // Start tarantool. + err = inst.Cmd.Start() + if err != nil { + return inst, err + } + + // Try to connect and ping tarantool. + // Using reconnect opts do not help on Connect, + // see https://github.com/tarantool/go-tarantool/issues/136 + time.Sleep(startOpts.WaitStart) + + opts := tarantool.Opts{ + Timeout: 500 * time.Millisecond, + User: startOpts.User, + Pass: startOpts.Pass, + SkipSchema: true, + } + + var i uint + for i = 0; i <= startOpts.ConnectRetry; i++ { + err = isReady(startOpts.Listen, &opts) + + // Both connect and ping is ok. + if err == nil { + break + } + + if i != startOpts.ConnectRetry { + time.Sleep(startOpts.RetryTimeout) + } + } + + return inst, err +} + +// StopTarantool stops a tarantool instance started +// with StartTarantool. Waits until any resources +// associated with the process is released. If something went wrong, fails. +func StopTarantool(inst TarantoolInstance) { + if inst.Cmd != nil && inst.Cmd.Process != nil { + if err := inst.Cmd.Process.Kill(); err != nil { + log.Fatalf("Failed to kill tarantool (pid %d), got %s", inst.Cmd.Process.Pid, err) + } + + // Wait releases any resources associated with the Process. + if _, err := inst.Cmd.Process.Wait(); err != nil { + log.Fatalf("Failed to wait for Tarantool process to exit, got %s", err) + } + + inst.Cmd = nil + } +} + +// StopTarantoolWithCleanup stops a tarantool instance started +// with StartTarantool. Waits until any resources +// associated with the process is released. +// Cleans work directory after stop. If something went wrong, fails. +func StopTarantoolWithCleanup(inst TarantoolInstance) { + StopTarantool(inst) + + if inst.WorkDir != "" { + if err := os.RemoveAll(inst.WorkDir); err != nil { + log.Fatalf("Failed to clean work directory, got %s", err) + } + } +} diff --git a/uuid/config.lua b/uuid/config.lua index 34b5d26c4..b8fe1fe08 100644 --- a/uuid/config.lua +++ b/uuid/config.lua @@ -1,10 +1,10 @@ local uuid = require('uuid') local msgpack = require('msgpack') +-- Do not set listen for now so connector won't be +-- able to send requests until everything is configured. box.cfg{ - listen = 3013, - wal_dir = 'xlog', - snap_dir = 'snap', + work_dir = os.getenv("TEST_TNT_WORK_DIR"), } box.schema.user.create('test', { password = 'test' , if_not_exists = true }) @@ -29,3 +29,8 @@ s:truncate() box.schema.user.grant('test', 'read,write', 'space', 'testUUID', { if_not_exists = true }) s:insert({ uuid.fromstr("c8f0fa1f-da29-438c-a040-393f1126ad39") }) + +-- Set listen only when every other thing is configured. +box.cfg{ + listen = os.getenv("TEST_TNT_LISTEN"), +} diff --git a/uuid/uuid_test.go b/uuid/uuid_test.go index 61eb632d2..b8987b0bc 100644 --- a/uuid/uuid_test.go +++ b/uuid/uuid_test.go @@ -2,15 +2,23 @@ package uuid_test import ( "fmt" + "log" + "os" "testing" "time" + "github.com/google/uuid" . "github.com/tarantool/go-tarantool" - _ "github.com/tarantool/go-tarantool/uuid" + "github.com/tarantool/go-tarantool/test_helpers" + _ "github.com/tarantool/go-tarantool/uuid" "gopkg.in/vmihailenco/msgpack.v2" - "github.com/google/uuid" ) +// There is no way to skip tests in testing.M, +// so we use this variable to pass info +// to each testing.T that it should skip. +var isUUIDSupported = false + var server = "127.0.0.1:3013" var opts = Opts{ Timeout: 500 * time.Millisecond, @@ -53,23 +61,6 @@ func connectWithValidation(t *testing.T) *Connection { return conn } -func skipIfUUIDUnsupported(t *testing.T, conn *Connection) { - resp, err := conn.Eval("return pcall(require('msgpack').encode, require('uuid').new())", []interface{}{}) - if err != nil { - t.Errorf("Failed to Eval: %s", err.Error()) - } - if resp == nil { - t.Errorf("Response is nil after Eval") - } - if len(resp.Data) < 1 { - t.Errorf("Response.Data is empty after Eval") - } - val := resp.Data[0].(bool) - if val != true { - t.Skip("Skipping test for Tarantool without UUID support in msgpack") - } -} - func tupleValueIsId(t *testing.T, tuples []interface{}, id uuid.UUID) { if len(tuples) != 1 { t.Errorf("Response Data len != 1") @@ -88,17 +79,19 @@ func tupleValueIsId(t *testing.T, tuples []interface{}, id uuid.UUID) { } func TestSelect(t *testing.T) { + if isUUIDSupported == false { + t.Skip("Skipping test for Tarantool without UUID support in msgpack") + } + conn := connectWithValidation(t) defer conn.Close() - skipIfUUIDUnsupported(t, conn) - id, uuidErr := uuid.Parse("c8f0fa1f-da29-438c-a040-393f1126ad39") if uuidErr != nil { t.Errorf("Failed to prepare test uuid: %s", uuidErr) } - resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{ id }) + resp, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{id}) if errSel != nil { t.Errorf("UUID select failed: %s", errSel.Error()) } @@ -108,7 +101,7 @@ func TestSelect(t *testing.T) { tupleValueIsId(t, resp.Data, id) var tuples []TupleUUID - errTyp := conn.SelectTyped(space, index, 0, 1, IterEq, []interface{}{ id }, &tuples) + errTyp := conn.SelectTyped(space, index, 0, 1, IterEq, []interface{}{id}, &tuples) if errTyp != nil { t.Errorf("Failed to SelectTyped: %s", errTyp.Error()) } @@ -121,17 +114,19 @@ func TestSelect(t *testing.T) { } func TestReplace(t *testing.T) { + if isUUIDSupported == false { + t.Skip("Skipping test for Tarantool without UUID support in msgpack") + } + conn := connectWithValidation(t) defer conn.Close() - skipIfUUIDUnsupported(t, conn) - id, uuidErr := uuid.Parse("64d22e4d-ac92-4a23-899a-e59f34af5479") if uuidErr != nil { t.Errorf("Failed to prepare test uuid: %s", uuidErr) } - respRep, errRep := conn.Replace(space, []interface{}{ id }) + respRep, errRep := conn.Replace(space, []interface{}{id}) if errRep != nil { t.Errorf("UUID replace failed: %s", errRep) } @@ -140,7 +135,7 @@ func TestReplace(t *testing.T) { } tupleValueIsId(t, respRep.Data, id) - respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{ id }) + respSel, errSel := conn.Select(space, index, 0, 1, IterEq, []interface{}{id}) if errSel != nil { t.Errorf("UUID select failed: %s", errSel) } @@ -149,3 +144,46 @@ func TestReplace(t *testing.T) { } tupleValueIsId(t, respSel.Data, id) } + +// runTestMain is a body of TestMain function +// (see https://pkg.go.dev/testing#hdr-Main). +// Using defer + os.Exit is not works so TestMain body +// is a separate function, see +// https://stackoverflow.com/questions/27629380/how-to-exit-a-go-program-honoring-deferred-calls +func runTestMain(m *testing.M) int { + isLess, err := test_helpers.IsTarantoolVersionLess(2, 4, 1) + if err != nil { + log.Fatalf("Failed to extract tarantool version: %s", err) + } + + if isLess { + log.Println("Skipping UUID tests...") + isUUIDSupported = false + return m.Run() + } else { + isUUIDSupported = true + } + + inst, err := test_helpers.StartTarantool(test_helpers.StartOpts{ + InitScript: "config.lua", + Listen: server, + WorkDir: "work_dir", + User: opts.User, + Pass: opts.Pass, + WaitStart: 100 * time.Millisecond, + ConnectRetry: 3, + RetryTimeout: 500 * time.Millisecond, + }) + defer test_helpers.StopTarantoolWithCleanup(inst) + + if err != nil { + log.Fatalf("Failed to prepare test tarantool: %s", err) + } + + return m.Run() +} + +func TestMain(m *testing.M) { + code := runTestMain(m) + os.Exit(code) +}