11package trellis
22
33import (
4+ "bytes"
5+ "io"
6+ "os"
7+ "reflect"
8+ "sort"
49 "strings"
510 "testing"
611
12+ "github.com/mitchellh/cli"
713 "github.com/posener/complete"
814)
915
10- func TestPredictEnvironment (t * testing.T ) {
16+ // Tests based on
17+ // https://github.com/mitchellh/cli/blob/5454ffe87bc5c6d8b6b21c825617755e18a07828/cli_test.go#L1125-L1225
18+
19+ // envComplete is the env var that the complete library sets to specify
20+ // it should be calculating an auto-completion.
21+ const envComplete = "COMP_LINE"
22+
23+ func TestCompletionFunctions (t * testing.T ) {
1124 project := & Project {}
1225 trellis := NewTrellis (project )
1326
@@ -18,93 +31,114 @@ func TestPredictEnvironment(t *testing.T) {
1831 }
1932
2033 cases := []struct {
21- name string
22- completed []string
23- want []string
34+ Predictor complete.Predictor
35+ Completed []string
36+ Last string
37+ Expected []string
2438 }{
25- {
26- name : "No args completed" ,
27- completed : []string {},
28- want : []string {},
29- },
30- {
31- name : "Command supplied" ,
32- completed : []string {"command" },
33- want : []string {"development" , "production" , "valet-link" },
34- },
35- {
36- name : "Command and env supplied" ,
37- completed : []string {"command" , "development" },
38- want : []string {},
39- },
39+ {trellis .AutocompleteEnvironment (), []string {"deploy" }, "" , []string {"development" , "valet-link" , "production" }},
40+ {trellis .AutocompleteEnvironment (), []string {"deploy" }, "d" , []string {"development" }},
41+ {trellis .AutocompleteEnvironment (), []string {"deploy" , "production" }, "" , nil },
42+ {trellis .AutocompleteSite (), []string {"deploy" }, "" , []string {"development" , "valet-link" , "production" }},
43+ {trellis .AutocompleteSite (), []string {"deploy" }, "d" , []string {"development" }},
44+ {trellis .AutocompleteSite (), []string {"deploy" , "production" }, "" , []string {"example.com" }},
4045 }
4146
4247 for _ , tc := range cases {
43- matches := trellis .PredictEnvironment ().Predict (
44- complete.Args {Completed : tc .completed },
45- )
46-
47- got := strings .Join (matches , "," )
48- want := strings .Join (tc .want , "," )
49-
50- if got != want {
51- t .Errorf ("failed %s\n got = %s\n want: %s" , t .Name (), got , want )
52- }
48+ t .Run (tc .Last , func (t * testing.T ) {
49+ command := new (cli.MockCommandAutocomplete )
50+ command .AutocompleteArgsValue = tc .Predictor
51+
52+ cli := & cli.CLI {
53+ Commands : map [string ]cli.CommandFactory {
54+ "deploy" : func () (cli.Command , error ) { return command , nil },
55+ },
56+ Autocomplete : true ,
57+ }
58+
59+ // Setup the autocomplete line
60+ var input bytes.Buffer
61+ input .WriteString ("cli " )
62+ if len (tc .Completed ) > 0 {
63+ input .WriteString (strings .Join (tc .Completed , " " ))
64+ input .WriteString (" " )
65+ }
66+ input .WriteString (tc .Last )
67+ defer testAutocomplete (t , input .String ())()
68+
69+ // Setup the output so that we can read it. We don't need to
70+ // reset os.Stdout because testAutocomplete will do that for us.
71+ r , w , err := os .Pipe ()
72+ if err != nil {
73+ t .Fatalf ("err: %s" , err )
74+ }
75+ defer r .Close () // Only defer reader since writer is closed below
76+ os .Stdout = w
77+
78+ // Run
79+ exitCode , err := cli .Run ()
80+ w .Close ()
81+ if err != nil {
82+ t .Fatalf ("err: %s" , err )
83+ }
84+
85+ if exitCode != 0 {
86+ t .Fatalf ("bad: %d" , exitCode )
87+ }
88+
89+ // Copy the output and get the autocompletions. We trim the last
90+ // element if we have one since we usually output a final newline
91+ // which results in a blank.
92+ var outBuf bytes.Buffer
93+ io .Copy (& outBuf , r )
94+ actual := strings .Split (outBuf .String (), "\n " )
95+ if len (actual ) > 0 {
96+ actual = actual [:len (actual )- 1 ]
97+ }
98+ if len (actual ) == 0 {
99+ // If we have no elements left, make the value nil since
100+ // this is what we use in tests.
101+ actual = nil
102+ }
103+
104+ sort .Strings (actual )
105+ sort .Strings (tc .Expected )
106+
107+ if ! reflect .DeepEqual (actual , tc .Expected ) {
108+ t .Fatalf ("bad:\n \n %#v\n \n %#v" , actual , tc .Expected )
109+ }
110+ })
53111 }
54112}
55113
56- func TestPredictSite (t * testing.T ) {
57- project := & Project {}
58- trellis := NewTrellis (project )
114+ // testAutocomplete sets up the environment to behave like a <tab> was
115+ // pressed in a shell to autocomplete a command.
116+ func testAutocomplete (t * testing.T , input string ) func () {
117+ // This env var is used to trigger autocomplete
118+ os .Setenv (envComplete , input )
59119
60- defer TestChdir (t , "testdata/trellis" )()
120+ // Change stdout/stderr since the autocompleter writes directly to them.
121+ oldStdout := os .Stdout
122+ oldStderr := os .Stderr
61123
62- if err := trellis .LoadProject (); err != nil {
63- t .Fatalf (err .Error ())
124+ r , w , err := os .Pipe ()
125+ if err != nil {
126+ t .Fatalf ("err: %s" , err )
64127 }
65128
66- cases := []struct {
67- name string
68- completed []string
69- lastCompleted string
70- want []string
71- }{
72- {
73- name : "No args completed" ,
74- completed : []string {},
75- lastCompleted : "" ,
76- want : []string {},
77- },
78- {
79- name : "Command supplied" ,
80- completed : []string {"command" },
81- lastCompleted : "command" ,
82- want : []string {"development" , "production" , "valet-link" },
83- },
84- {
85- name : "Command and env supplied" ,
86- completed : []string {"command" , "development" },
87- lastCompleted : "development" ,
88- want : []string {"example.com" },
89- },
90- {
91- name : "Command, env, and site supplied" ,
92- completed : []string {"command" , "development" , "example.com" },
93- lastCompleted : "example.com" ,
94- want : []string {},
95- },
96- }
129+ os .Stdout = w
130+ os .Stderr = w
97131
98- for _ , tc := range cases {
99- matches := trellis .PredictSite ().Predict (
100- complete.Args {Completed : tc .completed , LastCompleted : tc .lastCompleted },
101- )
132+ return func () {
133+ // Reset our env
134+ os .Unsetenv (envComplete )
102135
103- got := strings .Join (matches , "," )
104- want := strings .Join (tc .want , "," )
136+ // Reset stdout, stderr
137+ os .Stdout = oldStdout
138+ os .Stderr = oldStderr
105139
106- if got != want {
107- t . Errorf ( "failed %s \n got = %s \n want: %s" , t . Name (), got , want )
108- }
140+ // Close our pipe
141+ r . Close ( )
142+ w . Close ()
109143 }
110144}
0 commit comments