19
19
package cli
20
20
21
21
import (
22
- "encoding/json"
23
- "flag"
24
22
"fmt"
25
23
"io/ioutil"
26
24
"os"
27
25
"path/filepath"
28
- "runtime"
29
26
"testing"
30
27
31
- "github.com/spf13/viper"
32
-
33
- "github.com/arduino/arduino-cli/cli/errorcodes"
34
- "github.com/arduino/arduino-cli/cli/feedback"
35
-
36
- "bou.ke/monkey"
37
28
"github.com/stretchr/testify/require"
38
- semver "go.bug.st/relaxed-semver"
39
- )
40
-
41
- var (
42
- // Redirecting stdOut so we can analyze output line by
43
- // line and check with what we want.
44
- stdOut = os .Stdout
45
- stdErr = os .Stderr
46
-
47
- currDownloadDir string
48
- currDataDir string
49
- currUserDir string
50
29
)
51
30
52
- type outputRedirect struct {
53
- tempFile * os.File
54
- }
55
-
56
- func (grabber * outputRedirect ) Open () {
57
- tempFile , err := ioutil .TempFile (os .TempDir (), "test" )
58
- if err != nil {
59
- panic ("Opening temp output file" )
60
- }
61
- os .Stdout = tempFile
62
- os .Stderr = tempFile
63
- grabber .tempFile = tempFile
64
- }
65
-
66
- func (grabber * outputRedirect ) GetOutput () []byte {
67
- _ , err := grabber .tempFile .Seek (0 , 0 )
68
- if err != nil {
69
- panic ("Rewinding temp output file" )
70
- }
71
-
72
- output , err := ioutil .ReadAll (grabber .tempFile )
73
- if err != nil {
74
- panic ("Reading temp output file" )
75
- }
76
-
77
- return output
78
- }
79
-
80
- func (grabber * outputRedirect ) Close () {
81
- grabber .tempFile .Close ()
82
- err := os .Remove (grabber .tempFile .Name ())
83
- if err != nil {
84
- panic ("Removing temp output file" )
85
- }
86
- os .Stdout = stdOut
87
- os .Stderr = stdErr
88
- }
89
-
90
- func TestMain (m * testing.M ) {
91
- // all these tests perform actual operations, don't run in short mode
92
- flag .Parse ()
93
- if testing .Short () {
94
- fmt .Println ("skip integration tests" )
95
- os .Exit (0 )
96
- }
97
-
98
- // SetUp
99
- currDataDir = tmpDirOrDie ()
100
- os .MkdirAll (filepath .Join (currDataDir , "packages" ), 0755 )
101
- os .Setenv ("ARDUINO_DIRECTORIES_DATA" , currDataDir )
102
- currDownloadDir = tmpDirOrDie ()
103
- os .Setenv ("ARDUINO_DIRECTORIES_DOWNLOADS" , currDownloadDir )
104
- currUserDir = filepath .Join ("testdata" , "custom_hardware" )
105
- // use ARDUINO_SKETCHBOOK_DIR instead of ARDUINO_DIRECTORIES_USER to
106
- // ensure the backward compat code is working
107
- os .Setenv ("ARDUINO_SKETCHBOOK_DIR" , currUserDir )
108
-
109
- // Run
110
- res := m .Run ()
111
-
112
- // TearDown
113
- os .RemoveAll (currDataDir )
114
- os .Unsetenv ("ARDUINO_DIRECTORIES_DATA" )
115
- currDataDir = ""
116
- os .RemoveAll (currDownloadDir )
117
- os .Unsetenv ("ARDUINO_DIRECTORIES_DOWNLOADS" )
118
- currDownloadDir = ""
119
- os .Unsetenv ("ARDUINO_SKETCHBOOK_DIR" )
120
-
121
- os .Exit (res )
122
- }
123
-
124
31
func tmpDirOrDie () string {
125
32
dir , err := ioutil .TempDir (os .TempDir (), "cli_test" )
126
33
if err != nil {
@@ -129,168 +36,6 @@ func tmpDirOrDie() string {
129
36
return dir
130
37
}
131
38
132
- // executeWithArgs executes the Cobra Command with the given arguments
133
- // and intercepts any errors (even `os.Exit()` ones), returning the exit code
134
- func executeWithArgs (args ... string ) (int , []byte ) {
135
- var output []byte
136
- var exitCode int
137
- fmt .Printf ("RUNNING: %s\n " , args )
138
- viper .Reset ()
139
-
140
- // This closure is here because we won't that the defer are executed after the end of the "executeWithArgs" method
141
- func () {
142
- redirect := & outputRedirect {}
143
- redirect .Open ()
144
- // re-init feedback so it'll write to our grabber
145
- feedback .SetDefaultFeedback (feedback .New (os .Stdout , os .Stdout , feedback .Text ))
146
- defer func () {
147
- output = redirect .GetOutput ()
148
- redirect .Close ()
149
- fmt .Print (string (output ))
150
- fmt .Println ()
151
- }()
152
-
153
- // Mock the os.Exit function, so that we can use the
154
- // error result for the test and prevent the test from exiting
155
- fakeExitFired := false
156
- fakeExit := func (code int ) {
157
- exitCode = code
158
- fakeExitFired = true
159
-
160
- // use panic to exit and jump to deferred recover
161
- panic (fmt .Errorf ("os.Exit(%d)" , code ))
162
- }
163
- patch := monkey .Patch (os .Exit , fakeExit )
164
- defer patch .Unpatch ()
165
- defer func () {
166
- if fakeExitFired {
167
- recover ()
168
- }
169
- }()
170
-
171
- // Execute the CLI command, start fresh every time
172
- ArduinoCli .ResetCommands ()
173
- ArduinoCli .ResetFlags ()
174
- createCliCommandTree (ArduinoCli )
175
- ArduinoCli .SetArgs (args )
176
- if err := ArduinoCli .Execute (); err != nil {
177
- exitCode = errorcodes .ErrGeneric
178
- }
179
- }()
180
-
181
- return exitCode , output
182
- }
183
-
184
- func detectLatestAVRCore (t * testing.T ) string {
185
- jsonFile := filepath .Join (currDataDir , "package_index.json" )
186
- type index struct {
187
- Packages []struct {
188
- Name string
189
- Platforms []struct {
190
- Architecture string
191
- Version string
192
- }
193
- }
194
- }
195
- var jsonIndex index
196
- jsonData , err := ioutil .ReadFile (jsonFile )
197
- require .NoError (t , err , "reading package_index.json" )
198
- err = json .Unmarshal (jsonData , & jsonIndex )
199
- require .NoError (t , err , "parsing package_index.json" )
200
- latest := semver .MustParse ("0.0.1" )
201
- for _ , p := range jsonIndex .Packages {
202
- if p .Name == "arduino" {
203
- for _ , pl := range p .Platforms {
204
- ver , err := semver .Parse (pl .Version )
205
- require .NoError (t , err , "version parsing" )
206
- if pl .Architecture == "avr" && ver .GreaterThan (latest ) {
207
- latest = ver
208
- }
209
- }
210
- break
211
- }
212
- }
213
- require .NotEmpty (t , latest , "latest avr core version" )
214
- return latest .String ()
215
- }
216
-
217
- // END -- Utility functions
218
-
219
- func TestUploadIntegration (t * testing.T ) {
220
- if runtime .GOOS == "windows" {
221
- t .Skip ("This test runs only on Linux" )
222
- }
223
-
224
- exitCode , _ := executeWithArgs ("core" , "update-index" )
225
- require .Zero (t , exitCode )
226
-
227
- exitCode , _ = executeWithArgs ("core" , "install" , "arduino:avr" )
228
- require .Zero (t , exitCode )
229
-
230
- // -i flag
231
- exitCode , d := executeWithArgs ("upload" , "-i" , filepath .Join (currUserDir , "test.hex" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
232
- require .Zero (t , exitCode )
233
- require .Contains (t , string (d ), "QUIET" )
234
- require .Contains (t , string (d ), "NOVERIFY" )
235
- require .Contains (t , string (d ), "testdata/custom_hardware/test.hex" )
236
-
237
- // -i flag with implicit extension
238
- exitCode , d = executeWithArgs ("upload" , "-i" , filepath .Join (currUserDir , "test" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
239
- require .Zero (t , exitCode )
240
- require .Contains (t , string (d ), "QUIET" )
241
- require .Contains (t , string (d ), "NOVERIFY" )
242
- require .Contains (t , string (d ), "testdata/custom_hardware/test.hex" )
243
-
244
- // -i with absolute path
245
- fullPath , err := filepath .Abs (filepath .Join (currUserDir , "test.hex" ))
246
- require .NoError (t , err )
247
- exitCode , d = executeWithArgs ("upload" , "-i" , fullPath , "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
248
- require .Zero (t , exitCode )
249
- require .Contains (t , string (d ), "QUIET" )
250
- require .Contains (t , string (d ), "NOVERIFY" )
251
- require .Contains (t , string (d ), "testdata/custom_hardware/test.hex" )
252
-
253
- // -v verbose
254
- exitCode , d = executeWithArgs ("upload" , "-v" , "-t" , "-i" , filepath .Join (currUserDir , "test.hex" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
255
- require .Zero (t , exitCode )
256
- require .Contains (t , string (d ), "VERBOSE" )
257
- require .Contains (t , string (d ), "VERIFY" )
258
- require .Contains (t , string (d ), "testdata/custom_hardware/test.hex" )
259
-
260
- // -t verify
261
- exitCode , d = executeWithArgs ("upload" , "-i" , filepath .Join (currUserDir , "test.hex" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
262
- require .Zero (t , exitCode )
263
- require .Contains (t , string (d ), "QUIET" )
264
- require .Contains (t , string (d ), "NOVERIFY" )
265
- require .Contains (t , string (d ), "testdata/custom_hardware/test.hex" )
266
-
267
- // -v -t verbose verify
268
- exitCode , d = executeWithArgs ("upload" , "-v" , "-t" , "-i" , filepath .Join (currUserDir , "test.hex" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
269
- require .Zero (t , exitCode )
270
- require .Contains (t , string (d ), "VERBOSE" )
271
- require .Contains (t , string (d ), "VERIFY" )
272
- require .Contains (t , string (d ), "testdata/custom_hardware/test.hex" )
273
-
274
- // non-existent file
275
- exitCode , _ = executeWithArgs ("upload" , "-i" , filepath .Join (currUserDir , "test123.hex" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
276
- require .NotZero (t , exitCode )
277
-
278
- // sketch
279
- exitCode , d = executeWithArgs ("upload" , filepath .Join (currUserDir , "TestSketch" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
280
- require .Zero (t , exitCode )
281
- require .Contains (t , string (d ), "QUIET" )
282
- require .Contains (t , string (d ), "NOVERIFY" )
283
- require .Contains (t , string (d ), "testdata/custom_hardware/TestSketch/TestSketch.test.avr.testboard.hex" )
284
-
285
- // sketch without build
286
- exitCode , _ = executeWithArgs ("upload" , filepath .Join (currUserDir , "TestSketch2" ), "-b" , "test:avr:testboard" , "-p" , "/dev/ttyACM0" )
287
- require .NotZero (t , exitCode )
288
-
289
- // platform without 'recipe.output.tmp_file' property
290
- exitCode , _ = executeWithArgs ("upload" , "-i" , filepath .Join (currUserDir , "test.hex" ), "-b" , "test2:avr:testboard" , "-p" , "/dev/ttyACM0" )
291
- require .NotZero (t , exitCode )
292
- }
293
-
294
39
func TestSearchConfigTreeNotFound (t * testing.T ) {
295
40
tmp := tmpDirOrDie ()
296
41
require .Empty (t , searchConfigTree (tmp ))
0 commit comments