Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ ignore:
- shell/mock/
- lock/test/
- priority/check/
- util/test_executable/
- term/remote.go
- deprecation.go
- logger.go
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sonar.projectName=resticprofile
sonar.projectVersion=0.32.0

sonar.sources=.
sonar.exclusions=**/*_test.go,/docs/**
sonar.exclusions=**/*_test.go,/docs/**,**/mocks/*.go,shell/mock/**,lock/test/**,priority/check/**,util/test_executable/**

sonar.tests=.
sonar.test.inclusions=**/*_test.go
Expand Down
26 changes: 20 additions & 6 deletions util/executable_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,38 @@ package util
import (
"errors"
"os"
"os/exec"
"path/filepath"
)

// Executable returns the path name for the executable that started the current process.
// On non-Linux systems, it behaves like os.Executable.
// On Linux, it returns the path to the executable as specified in the command line arguments.
func Executable() (string, error) {
executable := os.Args[0]
return resolveExecutable(os.Args[0])
}

func resolveExecutable(executable string) (string, error) {
if len(executable) == 0 {
return "", errors.New("executable path is empty")
}
if executable[0] != '/' {
wd, err := os.Getwd()
if err != nil {
return "", err
// If the path is relative, we need to resolve it to an absolute path
if executable[0] == '.' {
wd, err := os.Getwd()
if err != nil {
return "", err
}
// If the executable path is relative, prepend the current working directory to form an absolute path.
executable = filepath.Join(wd, executable)
} else {
// If the path is not absolute, we assume it is in the PATH and resolve it.
found, err := exec.LookPath(executable)
if err != nil {
return "", err
}
executable = filepath.Clean(found)
}
// If the executable path is relative, prepend the current working directory to form an absolute path.
executable = filepath.Join(wd, executable)
}
return executable, nil
}
53 changes: 53 additions & 0 deletions util/executable_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//go:build linux

package util

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestResolveExecutable(t *testing.T) {
t.Run("empty path", func(t *testing.T) {
path, err := resolveExecutable("")
assert.Equal(t, "", path)
assert.Error(t, err)
assert.Equal(t, "executable path is empty", err.Error())
})

t.Run("absolute path", func(t *testing.T) {
path, err := resolveExecutable("/usr/bin/ls")
assert.NoError(t, err)
assert.Equal(t, "/usr/bin/ls", path)
})

t.Run("relative path with dot", func(t *testing.T) {
wd, err := os.Getwd()
require.NoError(t, err)

path, err := resolveExecutable("./test")
assert.NoError(t, err)
expected := filepath.Join(wd, "test")
assert.Equal(t, expected, path)
})

t.Run("command in PATH", func(t *testing.T) {
// Testing with "ls" which should be available on most Linux systems
path, err := resolveExecutable("ls")
assert.NoError(t, err)
assert.NotEmpty(t, path)
t.Log(path)
// The exact path can vary by system, but it should be an absolute path
assert.True(t, filepath.IsAbs(path), "Path should be absolute")
})

t.Run("command not in PATH", func(t *testing.T) {
path, err := resolveExecutable("this_command_should_not_exist_anywhere")
assert.Equal(t, "", path)
assert.Error(t, err)
})
}
110 changes: 110 additions & 0 deletions util/executable_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package util

import (
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/creativeprojects/resticprofile/platform"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -15,3 +18,110 @@ func TestExecutableIsAbsolute(t *testing.T) {

assert.True(t, filepath.IsAbs(executable))
}

func TestExecutable(t *testing.T) {
if platform.IsWindows() {
t.Skip("Executable test is not applicable on Windows")
}

tempDir, err := os.MkdirTemp("", "resticprofile-executable")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}

t.Cleanup(func() {
if err := os.RemoveAll(tempDir); err != nil {
t.Errorf("failed to remove temp dir: %v", err)
}
})

helperBinary := filepath.Join(tempDir, "executable_test_helper")
assert.True(t, filepath.IsAbs(helperBinary), "Helper binary path should be absolute")

cmd := exec.Command("go", "build", "-buildvcs=false", "-o", helperBinary, "./test_executable")
if err := cmd.Run(); err != nil {
t.Fatalf("Error building helper binary: %s\n", err)
}

symlinkBinary := filepath.Join(tempDir, "executable_test_symlink")
err = os.Symlink(helperBinary, symlinkBinary)
require.NoError(t, err, "Failed to create symlink for helper binary")

t.Run("absolute", func(t *testing.T) {
cmd = exec.Command(helperBinary)
output, err := cmd.Output()
if err != nil {
t.Fatalf("Error executing helper binary: %s\n", err)
}
t.Log(string(output))
assert.Equal(t, string(output), "\""+helperBinary+"\"\n", "Output should match the helper binary path")
})

t.Run("absolute symlink", func(t *testing.T) {
cmd = exec.Command(symlinkBinary)
output, err := cmd.Output()
if err != nil {
t.Fatalf("Error executing helper binary: %s\n", err)
}
t.Log(string(output))
assert.Equal(t, string(output), "\""+symlinkBinary+"\"\n", "Output should match the helper binary path")
})

t.Run("relative", func(t *testing.T) {
cmd = exec.Command("./" + filepath.Base(helperBinary))
cmd.Dir = tempDir // Set the working directory to the temp directory
output, err := cmd.Output()
if err != nil {
t.Fatalf("Error executing helper binary: %s\n", err)
}
t.Log(string(output))
assert.Equal(t, string(output), "\""+helperBinary+"\"\n", "Output should match the helper binary path")
})

t.Run("relative symlink", func(t *testing.T) {
cmd = exec.Command("./" + filepath.Base(symlinkBinary))
cmd.Dir = tempDir // Set the working directory to the temp directory
output, err := cmd.Output()
if err != nil {
t.Fatalf("Error executing helper binary: %s\n", err)
}
t.Log(string(output))
assert.Equal(t, string(output), "\""+symlinkBinary+"\"\n", "Output should match the helper binary path")
})

t.Run("from PATH", func(t *testing.T) {
path := os.Getenv("PATH")
t.Cleanup(func() {
os.Setenv("PATH", path) // Restore original PATH after test
})
os.Setenv("PATH", tempDir+string(os.PathListSeparator)+path) // Add tempDir to PATH for this test
t.Logf("Using PATH: %s", os.Getenv("PATH"))

cmd = exec.Command(filepath.Base(helperBinary))
cmd.Dir = tempDir // Set the working directory to the temp directory
output, err := cmd.Output()
if err != nil {
t.Fatalf("Error executing helper binary: %s\n", err)
}
t.Log(string(output))
assert.Equal(t, string(output), "\""+helperBinary+"\"\n", "Output should match the helper binary path")
})

t.Run("symlink from PATH", func(t *testing.T) {
path := os.Getenv("PATH")
t.Cleanup(func() {
os.Setenv("PATH", path) // Restore original PATH after test
})
os.Setenv("PATH", tempDir+string(os.PathListSeparator)+path) // Add tempDir to PATH for this test
t.Logf("Using PATH: %s", os.Getenv("PATH"))

cmd = exec.Command(filepath.Base(symlinkBinary))
cmd.Dir = tempDir // Set the working directory to the temp directory
output, err := cmd.Output()
if err != nil {
t.Fatalf("Error executing helper binary: %s\n", err)
}
t.Log(string(output))
assert.Equal(t, string(output), "\""+symlinkBinary+"\"\n", "Output should match the helper binary path")
})
}
17 changes: 17 additions & 0 deletions util/test_executable/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"fmt"
"os"

"github.com/creativeprojects/resticprofile/util"
)

func main() {
executable, err := util.Executable()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting executable: %s\n", err)
os.Exit(1)
}
fmt.Printf("%q\n", executable)
}
Loading