parse_test.go 9.41 KB
Newer Older
1 2 3
package cli

import (
4 5 6
	"io"
	"io/ioutil"
	"os"
rht's avatar
rht committed
7 8
	"strings"
	"testing"
Matt Bell's avatar
Matt Bell committed
9

10
	"github.com/ipfs/go-ipfs/commands"
11 12
)

13 14 15 16
type kvs map[string]interface{}
type words []string

func sameWords(a words, b words) bool {
17 18 19
	if len(a) != len(b) {
		return false
	}
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
	for i, w := range a {
		if w != b[i] {
			return false
		}
	}
	return true
}

func sameKVs(a kvs, b kvs) bool {
	if len(a) != len(b) {
		return false
	}
	for k, v := range a {
		if v != b[k] {
			return false
		}
	}
	return true
}

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
func TestSameWords(t *testing.T) {
	a := []string{"v1", "v2"}
	b := []string{"v1", "v2", "v3"}
	c := []string{"v2", "v3"}
	d := []string{"v2"}
	e := []string{"v2", "v3"}
	f := []string{"v2", "v1"}

	test := func(a words, b words, v bool) {
		if sameWords(a, b) != v {
			t.Errorf("sameWords('%v', '%v') != %v", a, b, v)
		}
	}

	test(a, b, false)
	test(a, a, true)
	test(a, c, false)
	test(b, c, false)
	test(c, d, false)
	test(c, e, true)
	test(b, e, false)
	test(a, b, false)
	test(a, f, false)
	test(e, f, false)
	test(f, f, true)
}

67
func TestOptionParsing(t *testing.T) {
Matt Bell's avatar
Matt Bell committed
68
	subCmd := &commands.Command{}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70
	cmd := &commands.Command{
		Options: []commands.Option{
Etienne Laurin's avatar
Etienne Laurin committed
71 72
			commands.StringOption("string", "s", "a string"),
			commands.BoolOption("bool", "b", "a bool"),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
73
		},
Matt Bell's avatar
Matt Bell committed
74
		Subcommands: map[string]*commands.Command{
Matt Bell's avatar
Matt Bell committed
75
			"test": subCmd,
Matt Bell's avatar
Matt Bell committed
76
		},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77
	}
78

Etienne Laurin's avatar
Etienne Laurin committed
79 80 81 82 83 84 85 86 87 88 89 90
	testHelper := func(args string, expectedOpts kvs, expectedWords words, expectErr bool) {
		_, opts, input, _, err := parseOpts(strings.Split(args, " "), cmd)
		if expectErr {
			if err == nil {
				t.Errorf("Command line '%v' parsing should have failed", args)
			}
		} else if err != nil {
			t.Errorf("Command line '%v' failed to parse: %v", args, err)
		} else if !sameWords(input, expectedWords) || !sameKVs(opts, expectedOpts) {
			t.Errorf("Command line '%v':\n  parsed as  %v %v\n  instead of %v %v",
				args, opts, input, expectedOpts, expectedWords)
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
91
	}
Etienne Laurin's avatar
Etienne Laurin committed
92 93 94

	testFail := func(args string) {
		testHelper(args, kvs{}, words{}, true)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95
	}
Etienne Laurin's avatar
Etienne Laurin committed
96 97 98

	test := func(args string, expectedOpts kvs, expectedWords words) {
		testHelper(args, expectedOpts, expectedWords, false)
Matt Bell's avatar
Matt Bell committed
99
	}
Etienne Laurin's avatar
Etienne Laurin committed
100 101 102 103 104 105 106 107 108

	test("-", kvs{}, words{"-"})
	testFail("-b -b")
	test("beep boop", kvs{}, words{"beep", "boop"})
	test("test beep boop", kvs{}, words{"beep", "boop"})
	testFail("-s")
	test("-s foo", kvs{"s": "foo"}, words{})
	test("-sfoo", kvs{"s": "foo"}, words{})
	test("-s=foo", kvs{"s": "foo"}, words{})
109 110
	test("-b", kvs{"b": true}, words{})
	test("-bs foo", kvs{"b": true, "s": "foo"}, words{})
Etienne Laurin's avatar
Etienne Laurin committed
111
	test("-sb", kvs{"s": "b"}, words{})
112 113
	test("-b foo", kvs{"b": true}, words{"foo"})
	test("--bool foo", kvs{"bool": true}, words{"foo"})
Etienne Laurin's avatar
Etienne Laurin committed
114 115 116 117 118
	testFail("--bool=foo")
	testFail("--string")
	test("--string foo", kvs{"string": "foo"}, words{})
	test("--string=foo", kvs{"string": "foo"}, words{})
	test("-- -b", kvs{}, words{"-b"})
119 120 121 122 123 124 125 126 127
	test("foo -b", kvs{"b": true}, words{"foo"})
	test("-b=false", kvs{"b": false}, words{})
	test("-b=true", kvs{"b": true}, words{})
	test("-b=false foo", kvs{"b": false}, words{"foo"})
	test("-b=true foo", kvs{"b": true}, words{"foo"})
	test("--bool=true foo", kvs{"bool": true}, words{"foo"})
	test("--bool=false foo", kvs{"bool": false}, words{"foo"})
	test("-b=FaLsE foo", kvs{"b": false}, words{"foo"})
	test("-b=TrUe foo", kvs{"b": true}, words{"foo"})
128
}
129 130 131 132

func TestArgumentParsing(t *testing.T) {
	rootCmd := &commands.Command{
		Subcommands: map[string]*commands.Command{
rht's avatar
rht committed
133 134
			"noarg": {},
			"onearg": {
135 136 137 138
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
				},
			},
rht's avatar
rht committed
139
			"twoargs": {
140 141 142 143 144
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, false, "another arg"),
				},
			},
rht's avatar
rht committed
145
			"variadic": {
146 147 148 149
				Arguments: []commands.Argument{
					commands.StringArg("a", true, true, "some arg"),
				},
			},
rht's avatar
rht committed
150
			"optional": {
151 152 153 154
				Arguments: []commands.Argument{
					commands.StringArg("b", false, true, "another arg"),
				},
			},
155 156 157 158 159 160
			"optionalsecond": {
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", false, false, "another arg"),
				},
			},
rht's avatar
rht committed
161
			"reversedoptional": {
162 163 164 165 166
				Arguments: []commands.Argument{
					commands.StringArg("a", false, false, "some arg"),
					commands.StringArg("b", true, false, "another arg"),
				},
			},
rht's avatar
rht committed
167
			"stdinenabled": {
168 169 170 171
				Arguments: []commands.Argument{
					commands.StringArg("a", true, true, "some arg").EnableStdin(),
				},
			},
172 173 174 175 176 177
			"stdinenabled2args": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, true, "another arg").EnableStdin(),
				},
			},
178 179 180 181 182 183 184 185 186 187 188
			"stdinenablednotvariadic": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg").EnableStdin(),
				},
			},
			"stdinenablednotvariadic2args": &commands.Command{
				Arguments: []commands.Argument{
					commands.StringArg("a", true, false, "some arg"),
					commands.StringArg("b", true, false, "another arg").EnableStdin(),
				},
			},
189 190 191
		},
	}

192 193 194 195 196 197 198 199
	test := func(cmd words, f *os.File, res words) {
		if f != nil {
			if _, err := f.Seek(0, os.SEEK_SET); err != nil {
				t.Fatal(err)
			}
		}
		req, _, _, err := Parse(cmd, f, rootCmd)
		if err != nil {
200
			t.Errorf("Command '%v' should have passed parsing: %v", cmd, err)
201 202
		}
		if !sameWords(req.Arguments(), res) {
203
			t.Errorf("Arguments parsed from '%v' are '%v' instead of '%v'", cmd, req.Arguments(), res)
204
		}
205
	}
206

207 208 209 210 211
	testFail := func(cmd words, msg string) {
		_, _, _, err := Parse(cmd, nil, rootCmd)
		if err == nil {
			t.Errorf("Should have failed: %v", msg)
		}
212 213
	}

214 215
	test([]string{"noarg"}, nil, []string{})
	testFail([]string{"noarg", "value!"}, "provided an arg, but command didn't define any")
216

217 218
	test([]string{"onearg", "value!"}, nil, []string{"value!"})
	testFail([]string{"onearg"}, "didn't provide any args, arg is required")
219

220
	test([]string{"twoargs", "value1", "value2"}, nil, []string{"value1", "value2"})
221 222
	testFail([]string{"twoargs", "value!"}, "only provided 1 arg, needs 2")
	testFail([]string{"twoargs"}, "didn't provide any args, 2 required")
223

224 225
	test([]string{"variadic", "value!"}, nil, []string{"value!"})
	test([]string{"variadic", "value1", "value2", "value3"}, nil, []string{"value1", "value2", "value3"})
226
	testFail([]string{"variadic"}, "didn't provide any args, 1 required")
227

228 229
	test([]string{"optional", "value!"}, nil, []string{"value!"})
	test([]string{"optional"}, nil, []string{})
230 231 232 233 234 235
	test([]string{"optional", "value1", "value2"}, nil, []string{"value1", "value2"})

	test([]string{"optionalsecond", "value!"}, nil, []string{"value!"})
	test([]string{"optionalsecond", "value1", "value2"}, nil, []string{"value1", "value2"})
	testFail([]string{"optionalsecond"}, "didn't provide any args, 1 required")
	testFail([]string{"optionalsecond", "value1", "value2", "value3"}, "provided too many args, takes 2 maximum")
236 237 238

	test([]string{"reversedoptional", "value1", "value2"}, nil, []string{"value1", "value2"})
	test([]string{"reversedoptional", "value!"}, nil, []string{"value!"})
239

240 241
	testFail([]string{"reversedoptional"}, "didn't provide any args, 1 required")
	testFail([]string{"reversedoptional", "value1", "value2", "value3"}, "provided too many args, only takes 1")
242 243

	// Use a temp file to simulate stdin
rht's avatar
rht committed
244
	fileToSimulateStdin := func(t *testing.T, content string) *os.File {
245 246 247 248 249
		fstdin, err := ioutil.TempFile("", "")
		if err != nil {
			t.Fatal(err)
		}
		defer os.Remove(fstdin.Name())
250

251 252 253 254
		if _, err := io.WriteString(fstdin, content); err != nil {
			t.Fatal(err)
		}
		return fstdin
255 256
	}

257
	test([]string{"stdinenabled", "value1", "value2"}, nil, []string{"value1", "value2"})
258 259

	fstdin := fileToSimulateStdin(t, "stdin1")
260
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1"})
261 262
	test([]string{"stdinenabled", "value1"}, fstdin, []string{"value1"})
	test([]string{"stdinenabled", "value1", "value2"}, fstdin, []string{"value1", "value2"})
263 264 265 266 267 268

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1", "stdin2"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2\nstdin3")
	test([]string{"stdinenabled"}, fstdin, []string{"stdin1", "stdin2", "stdin3"})
269 270 271 272 273 274 275 276 277 278

	test([]string{"stdinenabled2args", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenabled2args", "value1"}, fstdin, []string{"value1", "stdin1"})
	test([]string{"stdinenabled2args", "value1", "value2"}, fstdin, []string{"value1", "value2"})
	test([]string{"stdinenabled2args", "value1", "value2", "value3"}, fstdin, []string{"value1", "value2", "value3"})

	fstdin = fileToSimulateStdin(t, "stdin1\nstdin2")
	test([]string{"stdinenabled2args", "value1"}, fstdin, []string{"value1", "stdin1", "stdin2"})
279 280 281 282 283 284 285 286 287 288 289 290

	test([]string{"stdinenablednotvariadic", "value1"}, nil, []string{"value1"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenablednotvariadic"}, fstdin, []string{"stdin1"})
	test([]string{"stdinenablednotvariadic", "value1"}, fstdin, []string{"value1"})

	test([]string{"stdinenablednotvariadic2args", "value1", "value2"}, nil, []string{"value1", "value2"})

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"stdinenablednotvariadic2args", "value1"}, fstdin, []string{"value1", "stdin1"})
	test([]string{"stdinenablednotvariadic2args", "value1", "value2"}, fstdin, []string{"value1", "value2"})
Lars Gierth's avatar
Lars Gierth committed
291 292 293

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"noarg"}, fstdin, []string{})
294 295 296

	fstdin = fileToSimulateStdin(t, "stdin1")
	test([]string{"optionalsecond", "value1", "value2"}, fstdin, []string{"value1", "value2"})
297
}