200 lines · 5.4 KB · Go
RawDownload
// +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/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 = os.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)}