forked from rclone/rclone
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmdtest_test.go
217 lines (189 loc) · 6.73 KB
/
cmdtest_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// cmdtest_test creates a testable interface to rclone main
//
// The interface is used to perform end-to-end test of
// commands, flags, environment variables etc.
package cmdtest
import (
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestMain is initially called by go test to initiate the testing.
// TestMain is also called during the tests to start rclone main in a fresh context (using exec.Command).
// The context is determined by setting/finding the environment variable RCLONE_TEST_MAIN
func TestMain(m *testing.M) {
_, found := os.LookupEnv(rcloneTestMain)
if !found {
// started by Go test => execute tests
err := os.Setenv(rcloneTestMain, "true")
if err != nil {
log.Fatalf("Unable to set %s: %s", rcloneTestMain, err.Error())
}
os.Exit(m.Run())
} else {
// started by func rcloneExecMain => call rclone main in cmdtest.go
err := os.Unsetenv(rcloneTestMain)
if err != nil {
log.Fatalf("Unable to unset %s: %s", rcloneTestMain, err.Error())
}
main()
}
}
const rcloneTestMain = "RCLONE_TEST_MAIN"
// rcloneExecMain calls rclone with the given environment and arguments.
// The environment variables are in a single string separated by ;
// The terminal output is returned as a string.
func rcloneExecMain(env string, args ...string) (string, error) {
_, found := os.LookupEnv(rcloneTestMain)
if !found {
log.Fatalf("Unexpected execution path: %s is missing.", rcloneTestMain)
}
// make a call to self to execute rclone main in a predefined environment (enters TestMain above)
command := exec.Command(os.Args[0], args...)
command.Env = getEnvInitial()
if env != "" {
command.Env = append(command.Env, strings.Split(env, ";")...)
}
out, err := command.CombinedOutput()
return string(out), err
}
// rcloneEnv calls rclone with the given environment and arguments.
// The environment variables are in a single string separated by ;
// The test config file is automatically configured in RCLONE_CONFIG.
// The terminal output is returned as a string.
func rcloneEnv(env string, args ...string) (string, error) {
envConfig := env
if testConfig != "" {
if envConfig != "" {
envConfig += ";"
}
envConfig += "RCLONE_CONFIG=" + testConfig
}
return rcloneExecMain(envConfig, args...)
}
// rclone calls rclone with the given arguments, E.g. "version","--help".
// The test config file is automatically configured in RCLONE_CONFIG.
// The terminal output is returned as a string.
func rclone(args ...string) (string, error) {
return rcloneEnv("", args...)
}
// getEnvInitial returns the os environment variables cleaned for RCLONE_ vars (except RCLONE_TEST_MAIN).
func getEnvInitial() []string {
if envInitial == nil {
// Set initial environment variables
osEnv := os.Environ()
for i := range osEnv {
if !strings.HasPrefix(osEnv[i], "RCLONE_") || strings.HasPrefix(osEnv[i], rcloneTestMain) {
envInitial = append(envInitial, osEnv[i])
}
}
}
return envInitial
}
var envInitial []string
// createTestEnvironment creates a temporary testFolder and
// sets testConfig to testFolder/rclone.config.
func createTestEnvironment(t *testing.T) {
//Set temporary folder for config and test data
tempFolder := t.TempDir()
testFolder = filepath.ToSlash(tempFolder)
// Set path to temporary config file
testConfig = testFolder + "/rclone.config"
}
var testFolder string
var testConfig string
// createTestFile creates the file testFolder/name
func createTestFile(name string, t *testing.T) string {
err := os.WriteFile(testFolder+"/"+name, []byte("content_of_"+name), 0666)
require.NoError(t, err)
return testFolder + "/" + name
}
// createTestFolder creates the folder testFolder/name
func createTestFolder(name string, t *testing.T) string {
err := os.Mkdir(testFolder+"/"+name, 0777)
require.NoError(t, err)
return testFolder + "/" + name
}
// createSimpleTestData creates simple test data in testFolder/subFolder
func createSimpleTestData(t *testing.T) string {
createTestFolder("testdata", t)
createTestFile("testdata/file1.txt", t)
createTestFile("testdata/file2.txt", t)
createTestFolder("testdata/folderA", t)
createTestFile("testdata/folderA/fileA1.txt", t)
createTestFile("testdata/folderA/fileA2.txt", t)
createTestFolder("testdata/folderA/folderAA", t)
createTestFile("testdata/folderA/folderAA/fileAA1.txt", t)
createTestFile("testdata/folderA/folderAA/fileAA2.txt", t)
createTestFolder("testdata/folderB", t)
createTestFile("testdata/folderB/fileB1.txt", t)
createTestFile("testdata/folderB/fileB2.txt", t)
t.Cleanup(func() {
err := os.RemoveAll(testFolder + "/testdata")
require.NoError(t, err)
})
return testFolder + "/testdata"
}
// TestCmdTest demonstrates and verifies the test functions for end-to-end testing of rclone
func TestCmdTest(t *testing.T) {
createTestEnvironment(t)
// Test simple call and output from rclone
out, err := rclone("version")
t.Logf("rclone version\n" + out)
if assert.NoError(t, err) {
assert.Contains(t, out, "rclone v")
assert.Contains(t, out, "version: ")
assert.NotContains(t, out, "Error:")
assert.NotContains(t, out, "--help")
assert.NotContains(t, out, " DEBUG : ")
assert.Regexp(t, "rclone\\s+v\\d+\\.\\d+", out) // rclone v_.__
}
// Test multiple arguments and DEBUG output
out, err = rclone("version", "-vv")
if assert.NoError(t, err) {
assert.Contains(t, out, "rclone v")
assert.Contains(t, out, " DEBUG : ")
}
// Test error and error output
out, err = rclone("version", "--provoke-an-error")
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "exit status 1")
assert.Contains(t, out, "Error: unknown flag")
}
// Test effect of environment variable
env := "RCLONE_LOG_LEVEL=DEBUG"
out, err = rcloneEnv(env, "version")
if assert.NoError(t, err) {
assert.Contains(t, out, "rclone v")
assert.Contains(t, out, " DEBUG : ")
}
// Test effect of multiple environment variables, including one with ,
env = "RCLONE_LOG_LEVEL=DEBUG;RCLONE_LOG_FORMAT=date,shortfile;RCLONE_STATS=173ms"
out, err = rcloneEnv(env, "version")
if assert.NoError(t, err) {
assert.Contains(t, out, "rclone v")
assert.Contains(t, out, " DEBUG : ")
assert.Regexp(t, "[^\\s]+\\.go:\\d+:", out) // ___.go:__:
assert.Contains(t, out, "173ms")
}
// Test setup of config file
out, err = rclone("config", "create", "myLocal", "local")
if assert.NoError(t, err) {
assert.Contains(t, out, "[myLocal]")
assert.Contains(t, out, "type = local")
}
// Test creation of simple test data
createSimpleTestData(t)
// Test access to config file and simple test data
out, err = rclone("lsl", "myLocal:"+testFolder)
t.Logf("rclone lsl myLocal:testFolder\n" + out)
if assert.NoError(t, err) {
assert.Contains(t, out, "rclone.config")
assert.Contains(t, out, "testdata/folderA/fileA1.txt")
}
}