diff --git a/CHANGELOG.md b/CHANGELOG.md index d2b1d783e..f20fc9574 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed - `tt pack` now skips all `.git` files in packed environment, not only in main directory. +### Added + +- `tt install tarantool-dev`: ability to install tarantool from the local build directory. ## [1.1.2] - 2023-06-16 @@ -25,6 +28,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Fixed - Panic in tarantool 1.10.15 static build by `tt`. +- Removed `--use-docker` flag from `tt uninstall tarantool` since it was added by mistake. ## [1.1.1] - 2023-06-08 diff --git a/cli/cmd/install.go b/cli/cmd/install.go index 19b87c043..cec5a53ce 100644 --- a/cli/cmd/install.go +++ b/cli/cmd/install.go @@ -65,6 +65,31 @@ func newInstallTarantoolEeCmd() *cobra.Command { return tntCmd } +// newInstallTarantoolDevCmd creates a command to install tarantool +// from the local build directory. +func newInstallTarantoolDevCmd() *cobra.Command { + tntCmd := &cobra.Command{ + Use: "tarantool-dev ", + Short: "Install tarantool from the local build directory", + Example: "Assume, tarantool build directory is ~/src/tarantool/build\n" + + " Consider the following use case:\n\n" + + " make -j16 -C ~/src/tarantool/build\n" + + " tt install tarantool-dev ~/src/tarantool/build\n" + + " tt run # runs the binary compiled above", + Run: func(cmd *cobra.Command, args []string) { + cmdCtx.CommandName = cmd.Name() + installCtx.ProgramName = cmd.Name() + err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo, + internalInstallModule, args) + handleCmdErr(cmd, err) + }, + } + + tntCmd.Flags().StringVar(&installCtx.IncDir, "include-dir", "", + "tarantool headers directory") + return tntCmd +} + // NewInstallCmd creates install command. func NewInstallCmd() *cobra.Command { var installCmd = &cobra.Command{ @@ -91,6 +116,7 @@ func NewInstallCmd() *cobra.Command { newInstallTtCmd(), newInstallTarantoolCmd(), newInstallTarantoolEeCmd(), + newInstallTarantoolDevCmd(), ) return installCmd diff --git a/cli/cmd/uninstall.go b/cli/cmd/uninstall.go index 8fce9f476..dedf648db 100644 --- a/cli/cmd/uninstall.go +++ b/cli/cmd/uninstall.go @@ -44,9 +44,6 @@ func newUninstallTarantoolCmd() *cobra.Command { }, } - tntCmd.Flags().BoolVarP(&installCtx.BuildInDocker, "use-docker", "", false, - "build tarantool in Ubuntu 18.04 docker container") - return tntCmd } @@ -67,6 +64,23 @@ func newUninstallTarantoolEeCmd() *cobra.Command { return tntCmd } +// newUninstallTarantoolDevCmd creates a command to uninstall tarantool-dev. +func newUninstallTarantoolDevCmd() *cobra.Command { + tntCmd := &cobra.Command{ + Use: "tarantool-dev", + Short: "Uninstall tarantool-dev", + Run: func(cmd *cobra.Command, args []string) { + cmdCtx.CommandName = cmd.Name() + programName = cmd.Name() + err := modules.RunCmd(&cmdCtx, cmd.CommandPath(), &modulesInfo, + InternalUninstallModule, args) + handleCmdErr(cmd, err) + }, + } + + return tntCmd +} + // NewUninstallCmd creates uninstall command. func NewUninstallCmd() *cobra.Command { var uninstallCmd = &cobra.Command{ @@ -82,6 +96,7 @@ func NewUninstallCmd() *cobra.Command { newUninstallTtCmd(), newUninstallTarantoolCmd(), newUninstallTarantoolEeCmd(), + newUninstallTarantoolDevCmd(), ) return uninstallCmd diff --git a/cli/install/install.go b/cli/install/install.go index 8f54a7a88..b719a7b01 100644 --- a/cli/install/install.go +++ b/cli/install/install.go @@ -97,6 +97,11 @@ type InstallCtx struct { version string // Dynamic flag enables dynamic linking. Dynamic bool + // buildDir is the directory, where the tarantool executable is searched, + // in case of installation from the local build directory. + buildDir string + // IncDir is the directory, where the tarantool headers are located. + IncDir string } // Package is a struct containing sys and install name of the package. @@ -124,6 +129,18 @@ var ( VersionIDRe = regexp.MustCompile(`^VERSION_ID=(.*)$`) ) +// IsTarantoolDev returns true if tarantoolBinarySymlink is `tarantool-dev` version. +func IsTarantoolDev(tarantoolBinarySymlink, binDir string) (string, bool, error) { + bin, err := os.Readlink(tarantoolBinarySymlink) + if err != nil { + return "", false, err + } + if !filepath.IsAbs(bin) { + bin = filepath.Join(binDir, bin) + } + return bin, filepath.Dir(bin) != binDir, nil +} + // getDistroInfo collects info about linux distro. func getDistroInfo() (DistroInfo, error) { var distroInfo DistroInfo @@ -1114,6 +1131,89 @@ func dirIsWritable(dir string) bool { return unix.Access(dir, unix.W_OK) == nil } +// installTarantoolDev installs tarantool from the local build directory. +func installTarantoolDev(ttBinDir string, ttIncludeDir, buildDir, + includeDir string) error { + var err error + + // Validate build directory. + if buildDir, err = filepath.Abs(buildDir); err != nil { + return fmt.Errorf("failed to get absolute path: %v", err) + } + if !util.IsDir(buildDir) { + return fmt.Errorf("directory %v doesn't exist, or isn't directory", buildDir) + } + + defaultIncPath := filepath.Join(buildDir, "tarantool-prefix", "include", "tarantool") + if includeDir != "" { + // Validate headers directory. + if includeDir, err = filepath.Abs(includeDir); err != nil { + return fmt.Errorf("failed to get absolute path: %v", err) + } + if !util.IsDir(includeDir) { + return fmt.Errorf("directory %v doesn't exist, "+ + "or isn't directory", includeDir) + } + } else { + // Check for default. + if util.IsDir(defaultIncPath) { + log.Infof("tarantool headers directory set as %v", defaultIncPath) + includeDir = defaultIncPath + } + } + + if includeDir == "" { + log.Warn("Tarantool headers location was not specified and" + + fmt.Sprintf(" was not found in %v\n", defaultIncPath) + + "`tt build`, `tt rocks` may not work properly.\n" + + " To specify include files location use --include-dir option.") + } + + // Check that tt directories exist. + if err = util.CreateDirectory(ttBinDir, defaultDirPermissions); err != nil { + return err + } + if err = util.CreateDirectory(ttIncludeDir, defaultDirPermissions); err != nil { + return err + } + + checkedBinaryPaths := make([]string, 0) + + // Searching for tarantool binary. + for _, binaryRelPath := range [...]string{"src/tarantool", "tarantool/src/tarantool"} { + binaryPath := filepath.Join(buildDir, binaryRelPath) + + var isExecOwner bool + isExecOwner, err = util.IsExecOwner(binaryPath) + if err == nil && isExecOwner && !util.IsDir(binaryPath) { + log.Infof("Changing symlinks...") + err = util.CreateSymlink(binaryPath, filepath.Join(ttBinDir, "tarantool"), true) + if err != nil { + return err + } + tarantoolIncludeSymlink := filepath.Join(ttIncludeDir, "tarantool") + // Remove the old symlink to the tarantool headers. + // RemoveAll is used to perform deletion even if the file is not a symlink. + err = os.RemoveAll(tarantoolIncludeSymlink) + if err != nil { + return err + } + if includeDir != "" { + err = util.CreateSymlink(includeDir, tarantoolIncludeSymlink, true) + if err != nil { + return err + } + } + log.Infof("Done.") + return nil + } + checkedBinaryPaths = append(checkedBinaryPaths, binaryPath) + } + + return fmt.Errorf("tarantool binary was not found in the paths:\n%s", + strings.Join(checkedBinaryPaths, "\n")) +} + // subDirIsWritable checks if the passed dir doesn't exist but can be created. func subDirIsWritable(dir string) bool { var err error @@ -1153,6 +1253,9 @@ func Install(binDir string, includeDir string, installCtx InstallCtx, err = installTarantool(binDir, includeDir, installCtx, local) case search.ProgramEe: err = installTarantoolEE(binDir, includeDir, installCtx, local, cliOpts) + case search.ProgramDev: + err = installTarantoolDev(binDir, includeDir, installCtx.buildDir, + installCtx.IncDir) default: return fmt.Errorf("unknown application: %s", installCtx.ProgramName) } @@ -1163,6 +1266,14 @@ func Install(binDir string, includeDir string, installCtx InstallCtx, func FillCtx(cmdCtx *cmdcontext.CmdCtx, installCtx *InstallCtx, args []string) error { installCtx.verbose = cmdCtx.Cli.Verbose + if cmdCtx.CommandName == search.ProgramDev { + if len(args) != 1 { + return fmt.Errorf("exactly one build directory must be specified") + } + installCtx.buildDir = args[0] + return nil + } + if len(args) == 1 { installCtx.version = args[0] } else if len(args) > 1 { diff --git a/cli/install/install_test.go b/cli/install/install_test.go index 5b26562cc..df1f06cca 100644 --- a/cli/install/install_test.go +++ b/cli/install/install_test.go @@ -111,3 +111,133 @@ func Test_getLatestRelease(t *testing.T) { latestRelease = getLatestRelease(versions[1:6]) require.Equal(t, "", latestRelease) } + +func Test_installTarantoolDev(t *testing.T) { + ttBinDir := "binDir" + ttIncDir := "incDir" + + setupEnv := func() string { + // ├── ttBinDir + // ├── build_ce + // │ └── src + // │ └── tarantool + // ├── build_invalid + // │ └── tarantool + // │ └── src + // │ └── tarantool + // └── ttIncDir + + tempDir := os.TempDir() + tempsDir, _ := os.MkdirTemp(tempDir, "install_tarantool_dev_test") + + ttBinDir := filepath.Join(tempsDir, ttBinDir) + os.Mkdir(ttBinDir, os.ModePerm) + + ttIncDir := filepath.Join(tempsDir, ttIncDir) + os.Mkdir(ttIncDir, os.ModePerm) + + buildDir1 := filepath.Join(tempsDir, "build_ce") + os.MkdirAll(filepath.Join(buildDir1, "src"), os.ModePerm) + binaryPath1 := filepath.Join(buildDir1, "src/tarantool") + os.Create(binaryPath1) + os.Chmod(binaryPath1, 0700) + + buildDir2 := filepath.Join(tempsDir, "build_invalid") + os.MkdirAll(filepath.Join(buildDir2, "tarantool/src"), os.ModePerm) + binaryPath2 := filepath.Join(buildDir2, "tarantool/src/tarantool") + os.Create(binaryPath2) + os.Chmod(binaryPath2, 0700) + + return tempsDir + } + + t.Run("no include-dir", func(t *testing.T) { + tempDirectory := setupEnv() + defer os.RemoveAll(tempDirectory) + + ttBinPath := filepath.Join(tempDirectory, ttBinDir) + ttIncPath := filepath.Join(tempDirectory, ttIncDir) + + cases := []struct { + buildDir string + relExecPath string + }{ + {filepath.Join(tempDirectory, "build_ce"), "/src/tarantool"}, + {filepath.Join(tempDirectory, "build_invalid"), "/tarantool/src/tarantool"}, + } + + for _, tc := range cases { + err := installTarantoolDev(ttBinPath, ttIncPath, tc.buildDir, "") + assert.NoError(t, err) + link, err := os.Readlink(filepath.Join(ttBinPath, "tarantool")) + assert.NoError(t, err) + assert.Equal(t, filepath.Join(tc.buildDir, tc.relExecPath), link) + + // Check that old includeDir was removed. + _, err = os.Readlink(filepath.Join(ttIncPath, "tarantool")) + assert.Error(t, err) + } + }) + + t.Run("with include-dir", func(t *testing.T) { + tempDirectory := setupEnv() + defer os.RemoveAll(tempDirectory) + + ttBinPath := filepath.Join(tempDirectory, ttBinDir) + ttIncPath := filepath.Join(tempDirectory, ttIncDir) + + // Default include-dir. + os.MkdirAll(filepath.Join(tempDirectory, "build_ce", "tarantool-prefix", "include", + "tarantool"), os.ModePerm, + ) + + // Custom include-dir. + customIncDirectoryPath := filepath.Join(tempDirectory, "build_invalid", "custom_inc") + os.MkdirAll(customIncDirectoryPath, os.ModePerm) + cases := []struct { + buildDir string + incDir string + relExecPath string + expectedIncLink string + }{ + { + filepath.Join(tempDirectory, "build_ce"), + "", + "/src/tarantool", + filepath.Join(tempDirectory, "build_ce", "tarantool-prefix", "include", + "tarantool"), + }, + { + filepath.Join(tempDirectory, "build_invalid"), + customIncDirectoryPath, + "/tarantool/src/tarantool", + filepath.Join(tempDirectory, "build_invalid", "custom_inc"), + }, + } + + for _, tc := range cases { + err := installTarantoolDev(ttBinPath, ttIncPath, tc.buildDir, tc.incDir) + assert.NoError(t, err) + execLink, err := os.Readlink(filepath.Join(ttBinPath, "tarantool")) + assert.NoError(t, err) + assert.Equal(t, execLink, filepath.Join(tc.buildDir, tc.relExecPath)) + + incLink, err := os.Readlink(filepath.Join(ttIncPath, "tarantool")) + assert.NoError(t, err) + assert.Equal(t, tc.expectedIncLink, incLink) + } + }) + + t.Run("no executable", func(t *testing.T) { + tempDirectory := setupEnv() + defer os.RemoveAll(tempDirectory) + + ttBinPath := filepath.Join(tempDirectory, ttBinDir) + ttIncPath := filepath.Join(tempDirectory, ttIncDir) + + buildDir := filepath.Join(tempDirectory, "build_ee") + os.MkdirAll(buildDir, os.ModePerm) + err := installTarantoolDev(ttBinPath, ttIncPath, buildDir, "") + assert.Error(t, err) + }) +} diff --git a/cli/list/list.go b/cli/list/list.go index 0a656dc94..834564d97 100644 --- a/cli/list/list.go +++ b/cli/list/list.go @@ -3,6 +3,7 @@ package list import ( "errors" "fmt" + "github.com/tarantool/tt/cli/install" "io/fs" "math" "os" @@ -75,6 +76,22 @@ func printVersion(versionString string) { func parseBinaries(fileList []fs.DirEntry, programName string, binDir string) ([]version.Version, error) { var binaryVersions []version.Version + + if programName == search.ProgramDev { + binActive, isTarantoolBinary, err := install.IsTarantoolDev( + filepath.Join(binDir, "tarantool"), + binDir, + ) + if err != nil { + return binaryVersions, err + } + if isTarantoolBinary { + binaryVersions = append(binaryVersions, + version.Version{Str: programName + " -> " + binActive + " [active]"}) + } + return binaryVersions, nil + } + binActive, err := util.ResolveSymlink(filepath.Join(binDir, programName)) if err != nil && !os.IsNotExist(err) { return binaryVersions, err @@ -117,7 +134,7 @@ func ListBinaries(cmdCtx *cmdcontext.CmdCtx, cliOpts *config.CliOpts) (err error return fmt.Errorf("error reading directory %q: %s", binDir, err) } - programs := [...]string{search.ProgramTt, search.ProgramCe} + programs := [...]string{search.ProgramTt, search.ProgramCe, search.ProgramDev} fmt.Println("List of installed binaries:") for _, programName := range programs { binaryVersions, err := parseBinaries(binDirFilesList, programName, binDir) diff --git a/cli/list/list_test.go b/cli/list/list_test.go index bed787b21..69590d7bf 100644 --- a/cli/list/list_test.go +++ b/cli/list/list_test.go @@ -1,8 +1,10 @@ package list import ( + "fmt" "os" "sort" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -22,3 +24,19 @@ func TestParseBinaries(t *testing.T) { assert.Equal(t, expectedSortedVersions[i], versions[i].Str) } } + +func TestParseBinariesTarantoolDev(t *testing.T) { + for _, dir := range []string{"bin", "bin_symlink_broken"} { + t.Run(dir, func(t *testing.T) { + testDir := fmt.Sprintf("./testdata/tarantool_dev/%s", dir) + fileList, err := os.ReadDir(testDir) + assert.NoError(t, err) + versions, err := parseBinaries(fileList, "tarantool-dev", testDir) + assert.NoError(t, err) + assert.Equal(t, 1, len(versions)) + version := versions[0].Str + assert.True(t, strings.HasPrefix(version, "tarantool-dev")) + assert.True(t, strings.HasSuffix(version, "[active]")) + }) + } +} diff --git a/cli/list/testdata/tarantool_dev/bin/tarantool b/cli/list/testdata/tarantool_dev/bin/tarantool new file mode 120000 index 000000000..c344b86d1 --- /dev/null +++ b/cli/list/testdata/tarantool_dev/bin/tarantool @@ -0,0 +1 @@ +../tarantool \ No newline at end of file diff --git a/cli/list/testdata/tarantool_dev/bin/tarantool_1.10.0 b/cli/list/testdata/tarantool_dev/bin/tarantool_1.10.0 new file mode 100644 index 000000000..e69de29bb diff --git a/cli/list/testdata/tarantool_dev/bin/tarantool_2.10.7 b/cli/list/testdata/tarantool_dev/bin/tarantool_2.10.7 new file mode 100644 index 000000000..e69de29bb diff --git a/cli/list/testdata/tarantool_dev/bin_symlink_broken/tarantool b/cli/list/testdata/tarantool_dev/bin_symlink_broken/tarantool new file mode 120000 index 000000000..9d6f72e02 --- /dev/null +++ b/cli/list/testdata/tarantool_dev/bin_symlink_broken/tarantool @@ -0,0 +1 @@ +../kek.txt \ No newline at end of file diff --git a/cli/list/testdata/tarantool_dev/tarantool b/cli/list/testdata/tarantool_dev/tarantool new file mode 100644 index 000000000..e69de29bb diff --git a/cli/search/const.go b/cli/search/const.go index 29d4663b0..a948d814a 100644 --- a/cli/search/const.go +++ b/cli/search/const.go @@ -1,7 +1,8 @@ package search const ( - ProgramCe = "tarantool" - ProgramEe = "tarantool-ee" - ProgramTt = "tt" + ProgramCe = "tarantool" + ProgramEe = "tarantool-ee" + ProgramTt = "tt" + ProgramDev = "tarantool-dev" ) diff --git a/cli/uninstall/uninstall.go b/cli/uninstall/uninstall.go index f93885bb8..0dddaa33e 100644 --- a/cli/uninstall/uninstall.go +++ b/cli/uninstall/uninstall.go @@ -1,7 +1,9 @@ package uninstall import ( + "errors" "fmt" + "github.com/tarantool/tt/cli/install" "os" "path/filepath" "regexp" @@ -79,6 +81,27 @@ func UninstallProgram(program string, programVersion string, binDst string, head log.Infof("Removing binary...") var err error + if program == search.ProgramDev { + tarantoolBinarySymlink := filepath.Join(binDst, "tarantool") + _, isTarantoolDevInstalled, err := install.IsTarantoolDev(tarantoolBinarySymlink, binDst) + if err != nil { + return err + } + if !isTarantoolDevInstalled { + return fmt.Errorf("%s is not installed", program) + } + if err := os.Remove(tarantoolBinarySymlink); err != nil { + return err + } + headerDir := filepath.Join(headerDst, "tarantool") + log.Infof("Removing headers...") + // There can be no headers when `tarantool-dev` is installed. + if err := os.Remove(headerDir); err != nil && !errors.Is(err, os.ErrNotExist) { + return err + } + return nil + } + if programVersion == "" { if programVersion, err = getDefault(program, binDst); err != nil { return err diff --git a/test/integration/binaries/tarantool_dev/bin/tarantool b/test/integration/binaries/tarantool_dev/bin/tarantool new file mode 120000 index 000000000..c344b86d1 --- /dev/null +++ b/test/integration/binaries/tarantool_dev/bin/tarantool @@ -0,0 +1 @@ +../tarantool \ No newline at end of file diff --git a/test/integration/binaries/tarantool_dev/bin/tarantool_1.10.0 b/test/integration/binaries/tarantool_dev/bin/tarantool_1.10.0 new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/binaries/tarantool_dev/bin/tarantool_2.10.7 b/test/integration/binaries/tarantool_dev/bin/tarantool_2.10.7 new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/binaries/tarantool_dev/tarantool b/test/integration/binaries/tarantool_dev/tarantool new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/binaries/test_binaries.py b/test/integration/binaries/test_binaries.py index 956ab8bad..846737f0a 100644 --- a/test/integration/binaries/test_binaries.py +++ b/test/integration/binaries/test_binaries.py @@ -78,3 +78,31 @@ def test_binaries_empty_directory(tt_cmd, tmpdir): output = binaries_process.stdout.read() assert "there are no binaries installed in this environment of 'tt'" in output + + +def test_binaries_tarantool_dev(tt_cmd, tmpdir): + # Copy the test dir to the "run" directory. + test_app_path = os.path.join(os.path.dirname(__file__), "tarantool_dev") + shutil.copytree(test_app_path, tmpdir + "/tarantool_dev", True) + + config_path = os.path.join(tmpdir, config_name) + # Create test config. + with open(config_path, "w") as f: + f.write( + 'tt:\n app:\n bin_dir: "./tarantool_dev/bin"\n inc_dir:\n') + + binaries_cmd = [tt_cmd, "--cfg", config_path, "binaries"] + binaries_process = subprocess.Popen( + binaries_cmd, + cwd=tmpdir, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + text=True + ) + + binaries_process.wait() + output = binaries_process.stdout.read() + + assert "2.10.7" in output + assert "1.10.0" in output + assert "tarantool-dev" in output diff --git a/test/integration/install/test_install.py b/test/integration/install/test_install.py index 136f120e0..32f2540ce 100644 --- a/test/integration/install/test_install.py +++ b/test/integration/install/test_install.py @@ -1,18 +1,18 @@ import os import platform import re +import shutil import subprocess import tempfile import pytest import yaml -from utils import config_name +from utils import config_name, is_valid_tarantool_installed @pytest.mark.slow def test_install_tt(tt_cmd, tmpdir): - configPath = os.path.join(tmpdir, config_name) # Create test config with open(configPath, 'w') as f: @@ -46,7 +46,6 @@ def test_install_tt(tt_cmd, tmpdir): @pytest.mark.slow def test_install_tt_specific_version(tt_cmd, tmpdir): - configPath = os.path.join(tmpdir, config_name) # Create test config with open(configPath, 'w') as f: @@ -161,3 +160,142 @@ def test_install_tarantool_in_docker(tt_cmd, tmpdir): assert out == "GLIBC_2.27" assert os.path.exists(os.path.join(tmpdir, "my_inc", "include", "tarantool")) + + +def test_install_tarantool_dev_bin_invalid(tt_cmd, tmpdir): + # Copy test files. + testdata_path = os.path.join(os.path.dirname(__file__), "testdata") + shutil.copytree(testdata_path, os.path.join(tmpdir, "testdata"), True) + testdata_path = os.path.join(tmpdir, "testdata") + + tt_dir = "tt_basic" + for build_dir in ["build_invalid", "build_invalid2"]: + build_path = os.path.join(testdata_path, build_dir) + install_cmd = [ + tt_cmd, + "--cfg", os.path.join(testdata_path, tt_dir, config_name), + "install", "tarantool-dev", + build_path + ] + install_process = subprocess.Popen( + install_cmd, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + text=True + ) + install_process_rc = install_process.wait() + output = install_process.stdout.read() + assert "tarantool binary was not found" in output + assert install_process_rc != 0 + + assert is_valid_tarantool_installed( + os.path.join(testdata_path, tt_dir, "bin"), + os.path.join(testdata_path, tt_dir, "inc", "include"), + os.path.join(testdata_path, tt_dir, "bin", "tarantool_2.10.8"), + os.path.join(testdata_path, tt_dir, "inc", "include", + "tarantool_2.10.8") + ) + + +@pytest.mark.parametrize("tt_dir", [ + "tt_basic", + "tt_empty", + "tt_invalid" +]) +@pytest.mark.parametrize("build_dir, exec_rel_path, include_rel_path", [ + pytest.param( + "build_ce", + os.path.join("src", "tarantool"), + os.path.join("tarantool-prefix", "include", "tarantool") + ), + pytest.param( + "build_ee", + os.path.join("tarantool", "src", "tarantool"), + None + ) +]) +def test_install_tarantool_dev_no_include_option( + tt_cmd, + tmpdir, + build_dir, + exec_rel_path, + include_rel_path, + tt_dir +): + # Copy test files. + testdata_path = os.path.join(os.path.dirname(__file__), "testdata") + shutil.copytree(testdata_path, os.path.join(tmpdir, "testdata"), True) + testdata_path = os.path.join(tmpdir, "testdata") + + build_path = os.path.join(testdata_path, build_dir) + install_cmd = [ + tt_cmd, + "--cfg", os.path.join(testdata_path, tt_dir, config_name), + "install", "tarantool-dev", + build_path + ] + install_process = subprocess.Popen( + install_cmd, + stderr=subprocess.STDOUT, + stdout=subprocess.DEVNULL, + ) + + install_process_rc = install_process.wait() + assert install_process_rc == 0 + + expected_include_symlink = None + if include_rel_path is not None: + expected_include_symlink = os.path.join( + testdata_path, build_dir, include_rel_path + ) + + assert is_valid_tarantool_installed( + os.path.join(testdata_path, tt_dir, "bin"), + os.path.join(testdata_path, tt_dir, "inc", "include"), + os.path.join(testdata_path, build_dir, exec_rel_path), + expected_include_symlink, + ) + + +@pytest.mark.parametrize("tt_dir", [ + "tt_basic", + "tt_empty", + "tt_invalid" +]) +@pytest.mark.parametrize("rc, include_dir", [ + pytest.param(0, "custom_include/tarantool", id='dir exists'), + pytest.param(1, "include/tarantool", id='dir not exists') +]) +def test_install_tarantool_dev_include_option( + tt_cmd, tmpdir, rc, include_dir, tt_dir +): + # Copy test files. + testdata_path = os.path.join(os.path.dirname(__file__), "testdata") + shutil.copytree(testdata_path, os.path.join(tmpdir, "testdata"), True) + testdata_path = os.path.join(tmpdir, "testdata") + + build_dir = "build_ee" + build_path = os.path.join(testdata_path, build_dir) + install_cmd = [ + tt_cmd, + "--cfg", os.path.join(testdata_path, tt_dir, config_name), + "install", "tarantool-dev", + build_path, + "--include-dir", os.path.join(build_path, include_dir) + ] + + install_process = subprocess.Popen( + install_cmd, + stderr=subprocess.STDOUT, + stdout=subprocess.DEVNULL + ) + install_process_rc = install_process.wait() + assert install_process_rc == rc + + if rc == 0: + assert is_valid_tarantool_installed( + os.path.join(testdata_path, tt_dir, "bin"), + os.path.join(testdata_path, tt_dir, "inc", "include"), + os.path.join(build_path, "tarantool/src/tarantool"), + os.path.join(build_path, include_dir), + ) diff --git a/test/integration/install/testdata/build_ce/src/tarantool b/test/integration/install/testdata/build_ce/src/tarantool new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/build_ce/tarantool-prefix/include/tarantool/.gitkeep b/test/integration/install/testdata/build_ce/tarantool-prefix/include/tarantool/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/build_ee/custom_include/tarantool/.gitkeep b/test/integration/install/testdata/build_ee/custom_include/tarantool/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/build_ee/tarantool/src/tarantool b/test/integration/install/testdata/build_ee/tarantool/src/tarantool new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/build_invalid/.gitkeep b/test/integration/install/testdata/build_invalid/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/build_invalid2/src/tarantool/.gitkeep b/test/integration/install/testdata/build_invalid2/src/tarantool/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_basic/bin/tarantool b/test/integration/install/testdata/tt_basic/bin/tarantool new file mode 120000 index 000000000..c84307deb --- /dev/null +++ b/test/integration/install/testdata/tt_basic/bin/tarantool @@ -0,0 +1 @@ +tarantool_2.10.8 \ No newline at end of file diff --git a/test/integration/install/testdata/tt_basic/bin/tarantool_1.10.0 b/test/integration/install/testdata/tt_basic/bin/tarantool_1.10.0 new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_basic/bin/tarantool_2.10.8 b/test/integration/install/testdata/tt_basic/bin/tarantool_2.10.8 new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_basic/inc/include/tarantool b/test/integration/install/testdata/tt_basic/inc/include/tarantool new file mode 120000 index 000000000..1919efc44 --- /dev/null +++ b/test/integration/install/testdata/tt_basic/inc/include/tarantool @@ -0,0 +1 @@ +tarantool_2.10.8/ \ No newline at end of file diff --git a/test/integration/install/testdata/tt_basic/inc/include/tarantool_1.10.0/.gitkeep b/test/integration/install/testdata/tt_basic/inc/include/tarantool_1.10.0/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_basic/inc/include/tarantool_2.10.8/.gitkeep b/test/integration/install/testdata/tt_basic/inc/include/tarantool_2.10.8/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_basic/tt.yaml b/test/integration/install/testdata/tt_basic/tt.yaml new file mode 100644 index 000000000..82e874fb5 --- /dev/null +++ b/test/integration/install/testdata/tt_basic/tt.yaml @@ -0,0 +1,4 @@ +tt: + app: + bin_dir: "./bin" + inc_dir: "./inc" \ No newline at end of file diff --git a/test/integration/install/testdata/tt_empty/bin/.gitkeep b/test/integration/install/testdata/tt_empty/bin/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_empty/inc/include/.gitkeep b/test/integration/install/testdata/tt_empty/inc/include/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_empty/tt.yaml b/test/integration/install/testdata/tt_empty/tt.yaml new file mode 100644 index 000000000..82e874fb5 --- /dev/null +++ b/test/integration/install/testdata/tt_empty/tt.yaml @@ -0,0 +1,4 @@ +tt: + app: + bin_dir: "./bin" + inc_dir: "./inc" \ No newline at end of file diff --git a/test/integration/install/testdata/tt_invalid/bin/tarantool b/test/integration/install/testdata/tt_invalid/bin/tarantool new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_invalid/inc/include/tarantool/.gitkeep b/test/integration/install/testdata/tt_invalid/inc/include/tarantool/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/install/testdata/tt_invalid/tt.yaml b/test/integration/install/testdata/tt_invalid/tt.yaml new file mode 100644 index 000000000..82e874fb5 --- /dev/null +++ b/test/integration/install/testdata/tt_invalid/tt.yaml @@ -0,0 +1,4 @@ +tt: + app: + bin_dir: "./bin" + inc_dir: "./inc" \ No newline at end of file diff --git a/test/integration/uninstall/test_uninstall.py b/test/integration/uninstall/test_uninstall.py index 0bc0ed0d4..14b6c7364 100644 --- a/test/integration/uninstall/test_uninstall.py +++ b/test/integration/uninstall/test_uninstall.py @@ -1,8 +1,11 @@ import os import re +import shutil import subprocess -from utils import config_name +import pytest + +from utils import config_name, is_valid_tarantool_installed def test_uninstall_tt(tt_cmd, tmpdir): @@ -117,3 +120,73 @@ def test_uninstall_foreign_program(tt_cmd, tmpdir_with_cfg): uninstall_process.wait() uninstall_output = uninstall_process.stdout.readline() assert re.search(r"Uninstalls a program", uninstall_output) + + +@pytest.mark.parametrize("is_symlink_broken", [False, True]) +def test_uninstall_tarantool_dev_installed(tt_cmd, tmpdir, is_symlink_broken): + # Copy test files. + testdata_path = os.path.join(os.path.dirname(__file__), + "testdata/tarantool_dev") + shutil.copytree(testdata_path, os.path.join(tmpdir, "testdata"), True) + testdata_path = os.path.join(tmpdir, "testdata") + + tt_dir = "installed" + if is_symlink_broken: + os.remove(os.path.join(testdata_path, tt_dir, "tarantool")) + shutil.rmtree(os.path.join(testdata_path, tt_dir, "tarantool_inc")) + + uninstall_cmd = [ + tt_cmd, + "--cfg", os.path.join(testdata_path, tt_dir, config_name), + "uninstall", "tarantool-dev" + ] + uninstall_process = subprocess.Popen( + uninstall_cmd, + cwd=testdata_path, + stderr=subprocess.STDOUT, + stdout=subprocess.DEVNULL + ) + rc = uninstall_process.wait() + assert rc == 0 + assert is_valid_tarantool_installed( + os.path.join(testdata_path, tt_dir, "bin"), + os.path.join(testdata_path, tt_dir, "inc", "include"), + None, + None + ) + if not is_symlink_broken: + assert os.path.exists(os.path.join(testdata_path, tt_dir, "tarantool")) + assert os.path.exists(os.path.join(testdata_path, tt_dir, "tarantool_inc")) + + +def test_uninstall_tarantool_dev_not_installed(tt_cmd, tmpdir): + # Copy test files. + testdata_path = os.path.join(os.path.dirname(__file__), + "testdata/tarantool_dev") + shutil.copytree(testdata_path, os.path.join(tmpdir, "testdata"), True) + testdata_path = os.path.join(tmpdir, "testdata") + + tt_dir = "not_installed" + uninstall_cmd = [ + tt_cmd, + "--cfg", os.path.join(testdata_path, tt_dir, config_name), + "uninstall", "tarantool-dev" + ] + uninstall_process = subprocess.Popen( + uninstall_cmd, + cwd=testdata_path, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + text=True + ) + rc = uninstall_process.wait() + output = uninstall_process.stdout.read() + assert rc == 1 + assert "tarantool-dev is not installed" in output + assert is_valid_tarantool_installed( + os.path.join(testdata_path, tt_dir, "bin"), + os.path.join(testdata_path, tt_dir, "inc", "include"), + os.path.join(testdata_path, tt_dir, "bin", "tarantool_2.10.8"), + os.path.join(testdata_path, tt_dir, "inc", "include", + "tarantool_2.10.8") + ) diff --git a/test/integration/uninstall/testdata/tarantool_dev/installed/bin/tarantool b/test/integration/uninstall/testdata/tarantool_dev/installed/bin/tarantool new file mode 120000 index 000000000..c344b86d1 --- /dev/null +++ b/test/integration/uninstall/testdata/tarantool_dev/installed/bin/tarantool @@ -0,0 +1 @@ +../tarantool \ No newline at end of file diff --git a/test/integration/uninstall/testdata/tarantool_dev/installed/inc/include/tarantool b/test/integration/uninstall/testdata/tarantool_dev/installed/inc/include/tarantool new file mode 120000 index 000000000..e2b10de68 --- /dev/null +++ b/test/integration/uninstall/testdata/tarantool_dev/installed/inc/include/tarantool @@ -0,0 +1 @@ +../../tarantool_inc/ \ No newline at end of file diff --git a/test/integration/uninstall/testdata/tarantool_dev/installed/tarantool b/test/integration/uninstall/testdata/tarantool_dev/installed/tarantool new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/uninstall/testdata/tarantool_dev/installed/tarantool_inc/.gitkeep b/test/integration/uninstall/testdata/tarantool_dev/installed/tarantool_inc/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/uninstall/testdata/tarantool_dev/installed/tt.yaml b/test/integration/uninstall/testdata/tarantool_dev/installed/tt.yaml new file mode 100644 index 000000000..82e874fb5 --- /dev/null +++ b/test/integration/uninstall/testdata/tarantool_dev/installed/tt.yaml @@ -0,0 +1,4 @@ +tt: + app: + bin_dir: "./bin" + inc_dir: "./inc" \ No newline at end of file diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool b/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool new file mode 120000 index 000000000..c84307deb --- /dev/null +++ b/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool @@ -0,0 +1 @@ +tarantool_2.10.8 \ No newline at end of file diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool_1.10.0 b/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool_1.10.0 new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool_2.10.8 b/test/integration/uninstall/testdata/tarantool_dev/not_installed/bin/tarantool_2.10.8 new file mode 100755 index 000000000..e69de29bb diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool b/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool new file mode 120000 index 000000000..1919efc44 --- /dev/null +++ b/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool @@ -0,0 +1 @@ +tarantool_2.10.8/ \ No newline at end of file diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool_1.10.0/.gitkeep b/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool_1.10.0/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool_2.10.8/.gitkeep b/test/integration/uninstall/testdata/tarantool_dev/not_installed/inc/include/tarantool_2.10.8/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/uninstall/testdata/tarantool_dev/not_installed/tt.yaml b/test/integration/uninstall/testdata/tarantool_dev/not_installed/tt.yaml new file mode 100644 index 000000000..82e874fb5 --- /dev/null +++ b/test/integration/uninstall/testdata/tarantool_dev/not_installed/tt.yaml @@ -0,0 +1,4 @@ +tt: + app: + bin_dir: "./bin" + inc_dir: "./inc" \ No newline at end of file diff --git a/test/utils.py b/test/utils.py index ff04ad79f..01ba7e8f1 100644 --- a/test/utils.py +++ b/test/utils.py @@ -339,3 +339,41 @@ def extract_status(status_output): info["STATUS"] = " ".join(fields[1:]) result[instance] = info return result + + +def is_valid_tarantool_installed( + bin_path, + inc_path, + expected_bin=None, + expected_inc=None +): + tarantool_binary_symlink = os.path.join(bin_path, "tarantool") + tarantool_include_symlink = os.path.join(inc_path, "tarantool") + + if expected_bin is None: + if os.path.exists(tarantool_binary_symlink): + tarantool_bin = os.path.realpath( + os.path.join(bin_path, "tarantool")) + print(f"tarantool binary {tarantool_bin} is unexpected") + return False + else: + tarantool_bin = os.path.realpath(os.path.join(bin_path, "tarantool")) + if tarantool_bin != expected_bin: + print(f"tarantool binary {tarantool_bin} is unexpected," + f" expected: {expected_bin}") + return False + + if expected_inc is not None: + tarantool_inc = os.path.realpath(tarantool_include_symlink) + if tarantool_inc != expected_inc: + print(f"tarantool include {tarantool_inc} is unexpected," + f" expected: {expected_bin}") + return False + else: + if os.path.exists(tarantool_include_symlink): + tarantool_inc = os.path.realpath( + os.path.join(inc_path, "tarantool")) + print(f"tarantool include {tarantool_inc} is unexpected") + return False + + return True