fbc7f2e61b
This replaces built-in os.MkdirAll with a patched version that stops the recursion when reaching the volume part of the path. The original version would continue recursion, and for extended length paths end up with \\? as the top-level directory, and the error message would then be something like: mkdir \\?: The filename, directory name, or volume label syntax is incorrect.
202 lines
5.5 KiB
Go
202 lines
5.5 KiB
Go
//go:build !noselfupdate
|
|
// +build !noselfupdate
|
|
|
|
package selfupdate
|
|
|
|
import (
|
|
"context"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fstest/testy"
|
|
"github.com/rclone/rclone/lib/file"
|
|
"github.com/rclone/rclone/lib/random"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestGetVersion(t *testing.T) {
|
|
testy.SkipUnreliable(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
// a beta version can only have "v" prepended
|
|
resultVer, _, err := GetVersion(ctx, true, "1.2.3.4")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "v1.2.3.4", resultVer)
|
|
|
|
// but a stable version syntax should be checked
|
|
_, _, err = GetVersion(ctx, false, "1")
|
|
assert.Error(t, err)
|
|
_, _, err = GetVersion(ctx, false, "1.")
|
|
assert.Error(t, err)
|
|
_, _, err = GetVersion(ctx, false, "1.2.")
|
|
assert.Error(t, err)
|
|
_, _, err = GetVersion(ctx, false, "1.2.3.4")
|
|
assert.Error(t, err)
|
|
|
|
// incomplete stable version should have micro release added
|
|
resultVer, _, err = GetVersion(ctx, false, "1.52")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "v1.52.3", resultVer)
|
|
}
|
|
|
|
func makeTestDir() (testDir string, err error) {
|
|
const maxAttempts = 5
|
|
testDirBase := filepath.Join(os.TempDir(), "rclone-test-selfupdate.")
|
|
|
|
for attempt := 0; attempt < maxAttempts; attempt++ {
|
|
testDir = testDirBase + random.String(4)
|
|
err = file.MkdirAll(testDir, os.ModePerm)
|
|
if err == nil {
|
|
break
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func TestInstallOnLinux(t *testing.T) {
|
|
testy.SkipUnreliable(t)
|
|
if runtime.GOOS != "linux" {
|
|
t.Skip("this is a Linux only test")
|
|
}
|
|
|
|
// Prepare for test
|
|
ctx := context.Background()
|
|
testDir, err := makeTestDir()
|
|
assert.NoError(t, err)
|
|
path := filepath.Join(testDir, "rclone")
|
|
defer func() {
|
|
_ = os.Chmod(path, 0644)
|
|
_ = os.RemoveAll(testDir)
|
|
}()
|
|
|
|
regexVer := regexp.MustCompile(`v[0-9]\S+`)
|
|
|
|
betaVer, _, err := GetVersion(ctx, true, "")
|
|
assert.NoError(t, err)
|
|
|
|
// Must do nothing if version isn't changing
|
|
assert.NoError(t, InstallUpdate(ctx, &Options{Beta: true, Output: path, Version: fs.Version}))
|
|
|
|
// Must fail on non-writable file
|
|
assert.NoError(t, ioutil.WriteFile(path, []byte("test"), 0644))
|
|
assert.NoError(t, os.Chmod(path, 0000))
|
|
err = (InstallUpdate(ctx, &Options{Beta: true, Output: path}))
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "run self-update as root")
|
|
|
|
// Must keep non-standard permissions
|
|
assert.NoError(t, os.Chmod(path, 0644))
|
|
assert.NoError(t, InstallUpdate(ctx, &Options{Beta: true, Output: path}))
|
|
|
|
info, err := os.Stat(path)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, os.FileMode(0644), info.Mode().Perm())
|
|
|
|
// Must remove temporary files
|
|
files, err := ioutil.ReadDir(testDir)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, len(files))
|
|
|
|
// Must contain valid executable
|
|
assert.NoError(t, os.Chmod(path, 0755))
|
|
cmd := exec.Command(path, "version")
|
|
output, err := cmd.CombinedOutput()
|
|
assert.NoError(t, err)
|
|
assert.True(t, cmd.ProcessState.Success())
|
|
assert.Equal(t, betaVer, regexVer.FindString(string(output)))
|
|
}
|
|
|
|
func TestRenameOnWindows(t *testing.T) {
|
|
testy.SkipUnreliable(t)
|
|
if runtime.GOOS != "windows" {
|
|
t.Skip("this is a Windows only test")
|
|
}
|
|
|
|
// Prepare for test
|
|
ctx := context.Background()
|
|
|
|
testDir, err := makeTestDir()
|
|
assert.NoError(t, err)
|
|
defer func() {
|
|
_ = os.RemoveAll(testDir)
|
|
}()
|
|
|
|
path := filepath.Join(testDir, "rclone.exe")
|
|
regexVer := regexp.MustCompile(`v[0-9]\S+`)
|
|
|
|
stableVer, _, err := GetVersion(ctx, false, "")
|
|
assert.NoError(t, err)
|
|
|
|
betaVer, _, err := GetVersion(ctx, true, "")
|
|
assert.NoError(t, err)
|
|
|
|
// Must not create temporary files when target doesn't exist
|
|
assert.NoError(t, InstallUpdate(ctx, &Options{Beta: true, Output: path}))
|
|
|
|
files, err := ioutil.ReadDir(testDir)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, len(files))
|
|
|
|
// Must save running executable as the "old" file
|
|
cmdWait := exec.Command(path, "config")
|
|
stdinWait, err := cmdWait.StdinPipe() // Make it run waiting for input
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, cmdWait.Start())
|
|
|
|
assert.NoError(t, InstallUpdate(ctx, &Options{Beta: false, Output: path}))
|
|
files, err = ioutil.ReadDir(testDir)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, len(files))
|
|
|
|
pathOld := filepath.Join(testDir, "rclone.old.exe")
|
|
_, err = os.Stat(pathOld)
|
|
assert.NoError(t, err)
|
|
|
|
cmd := exec.Command(path, "version")
|
|
output, err := cmd.CombinedOutput()
|
|
assert.NoError(t, err)
|
|
assert.True(t, cmd.ProcessState.Success())
|
|
assert.Equal(t, stableVer, regexVer.FindString(string(output)))
|
|
|
|
cmdOld := exec.Command(pathOld, "version")
|
|
output, err = cmdOld.CombinedOutput()
|
|
assert.NoError(t, err)
|
|
assert.True(t, cmdOld.ProcessState.Success())
|
|
assert.Equal(t, betaVer, regexVer.FindString(string(output)))
|
|
|
|
// Stop previous waiting executable, run new and saved executables
|
|
_ = stdinWait.Close()
|
|
_ = cmdWait.Wait()
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
cmdWait = exec.Command(path, "config")
|
|
stdinWait, err = cmdWait.StdinPipe()
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, cmdWait.Start())
|
|
|
|
cmdWaitOld := exec.Command(pathOld, "config")
|
|
stdinWaitOld, err := cmdWaitOld.StdinPipe()
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, cmdWaitOld.Start())
|
|
|
|
// Updating when the "old" executable is running must produce a random "old" file
|
|
assert.NoError(t, InstallUpdate(ctx, &Options{Beta: true, Output: path}))
|
|
files, err = ioutil.ReadDir(testDir)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 3, len(files))
|
|
|
|
// Stop all waiting executables
|
|
_ = stdinWait.Close()
|
|
_ = cmdWait.Wait()
|
|
_ = stdinWaitOld.Close()
|
|
_ = cmdWaitOld.Wait()
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|