603 lines
13 KiB
Go
603 lines
13 KiB
Go
|
package gluaflag
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"io"
|
||
|
"os"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/yuin/gopher-lua"
|
||
|
)
|
||
|
|
||
|
func captureStdout() func() string {
|
||
|
old := os.Stdout // keep backup of the real stdout
|
||
|
r, w, _ := os.Pipe()
|
||
|
os.Stdout = w
|
||
|
|
||
|
outC := make(chan string)
|
||
|
// copy the output in a separate goroutine so printing can't block indefinitely
|
||
|
go func() {
|
||
|
var buf bytes.Buffer
|
||
|
io.Copy(&buf, r)
|
||
|
outC <- buf.String()
|
||
|
}()
|
||
|
|
||
|
return func() string {
|
||
|
w.Close()
|
||
|
os.Stdout = old // restoring the real stdout
|
||
|
return <-outC
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func captureStderr() func() string {
|
||
|
old := os.Stderr // keep backup of the real Stderr
|
||
|
r, w, _ := os.Pipe()
|
||
|
os.Stderr = w
|
||
|
|
||
|
outC := make(chan string)
|
||
|
// copy the output in a separate goroutine so printing can't block indefinitely
|
||
|
go func() {
|
||
|
var buf bytes.Buffer
|
||
|
io.Copy(&buf, r)
|
||
|
outC <- buf.String()
|
||
|
}()
|
||
|
|
||
|
return func() string {
|
||
|
w.Close()
|
||
|
os.Stderr = old // restoring the real stdout
|
||
|
return <-outC
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func doString(src string, t *testing.T) (stdout, stderr string) {
|
||
|
L := lua.NewState()
|
||
|
defer L.Close()
|
||
|
L.PreloadModule("flag", Loader)
|
||
|
|
||
|
restoreStdout := captureStdout()
|
||
|
restoreStderr := captureStderr()
|
||
|
err := L.DoString(src)
|
||
|
stdout = restoreStdout()
|
||
|
stderr = restoreStderr()
|
||
|
if err != nil {
|
||
|
t.Errorf("runtime error: %v", err)
|
||
|
}
|
||
|
|
||
|
if len(stdout) > 0 {
|
||
|
stdout = stdout[0 : len(stdout)-1]
|
||
|
}
|
||
|
|
||
|
if len(stderr) > 0 {
|
||
|
stderr = stderr[0 : len(stderr)-1]
|
||
|
}
|
||
|
|
||
|
return stdout, stderr
|
||
|
}
|
||
|
|
||
|
func TestUsage(t *testing.T) {
|
||
|
// TODO: Fix output
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-foo"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new("subcommand")
|
||
|
fs:number("times", 1, "Number help string")
|
||
|
function fail()
|
||
|
flags = fs:parse(arg)
|
||
|
end
|
||
|
ok, err = pcall(fail)
|
||
|
|
||
|
print(err)
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"usage: subcommand [options]",
|
||
|
" -times float",
|
||
|
" Number help string (default 1)",
|
||
|
"<string>:8: flag provided but not defined: -foo",
|
||
|
}, "\n")
|
||
|
expectedStderr := strings.Join([]string{
|
||
|
"flag provided but not defined: -foo",
|
||
|
}, "\n")
|
||
|
got, stderr := doString(src, t)
|
||
|
|
||
|
if got != expected || stderr != expectedStderr {
|
||
|
t.Errorf("expected stdout: `%v`\ngot: `%v`\nexpected stderr: `%v`\ngot: `%v`\nsrc: `%v`", expected, got, expectedStderr, stderr, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNumberFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times", "2"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:number("times", 1, "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(flags.times)
|
||
|
print(type(flags.times))
|
||
|
`
|
||
|
|
||
|
expected := "2\nnumber"
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIntFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times", "2"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:int("times", 1, "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(flags.times)
|
||
|
print(type(flags.times))
|
||
|
`
|
||
|
|
||
|
expected := "2\nnumber"
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIntSliceFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times", "2", "-times", "4"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:ints("times", "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(type(flags.times))
|
||
|
print(table.concat(flags.times, ","))
|
||
|
`
|
||
|
|
||
|
expected := "table\n2,4"
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNumberFlagCompgen(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
local function compgen()
|
||
|
return "1 2 3"
|
||
|
end
|
||
|
fs:number("times", 1, "Number help string", compgen)
|
||
|
flags = fs:compgen(2, arg)
|
||
|
|
||
|
print(table.concat(flags, " "))
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"1",
|
||
|
"2",
|
||
|
"3",
|
||
|
}, " ")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNumberFlagAndArgs(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times", "2", "foo"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:number("times", 1, "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(flags.times)
|
||
|
print(type(flags.times))
|
||
|
for i, v in ipairs(flags) do
|
||
|
print(i .. "=" .. v)
|
||
|
end
|
||
|
print(flags[1])
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"2",
|
||
|
"number",
|
||
|
"1=foo",
|
||
|
"foo",
|
||
|
}, "\n")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNumberSliceFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times", "2.4", "-times", "4.3"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:numbers("times", "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(type(flags.times))
|
||
|
print(table.concat(flags.times, ","))
|
||
|
`
|
||
|
|
||
|
expected := "table\n2.4,4.3"
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "bar"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(flags.name)
|
||
|
print(type(flags.name))
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"bar",
|
||
|
"string",
|
||
|
}, "\n")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringFlagCompgen(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
local arg = {"-name"}
|
||
|
arg[0] = "subcommand"
|
||
|
local fs = flag.new()
|
||
|
local function compgen()
|
||
|
return {"fii", "foo", "fum"}
|
||
|
end
|
||
|
fs:string("name", "foo", "String help string", compgen)
|
||
|
flags = fs:compgen(2, arg)
|
||
|
|
||
|
print(table.concat(flags, "\n"))
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"fii",
|
||
|
"foo",
|
||
|
"fum",
|
||
|
}, "\n")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringSliceFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-times", "foo", "-times", "bar"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:strings("times", "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(type(flags.times))
|
||
|
print(table.concat(flags.times, ","))
|
||
|
`
|
||
|
|
||
|
expected := "table\nfoo,bar"
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringSliceNoValues(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:strings("times", "Number help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(type(flags.times))
|
||
|
print(table.concat(flags.times, ","))
|
||
|
`
|
||
|
|
||
|
expected := "table\n"
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestBoolFlag(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-q"}
|
||
|
arg[0] = "subcmd"
|
||
|
fs = flag.new()
|
||
|
fs:bool("q", false, "Bool help string")
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
print(flags.q)
|
||
|
print(type(flags.q))
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"true",
|
||
|
"boolean",
|
||
|
}, "\n")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringArg(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"test", "-name", "foo", "mr"}
|
||
|
fs = flag.new()
|
||
|
local function compgen()
|
||
|
return "fii foo fum"
|
||
|
end
|
||
|
fs:string("name", "foo", "String help string", compgen)
|
||
|
fs:stringArg("title", 1, "Title", function()
|
||
|
return "mr miss mrs"
|
||
|
end)
|
||
|
flags = fs:parse(arg)
|
||
|
|
||
|
function pairsByKeys(t)
|
||
|
local a = {}
|
||
|
for n in pairs(t) do table.insert(a, n) end
|
||
|
table.sort(a)
|
||
|
local i = 0 -- iterator variable
|
||
|
local iter = function () -- iterator function
|
||
|
i = i + 1
|
||
|
if a[i] == nil then return nil
|
||
|
else return a[i], t[a[i]]
|
||
|
end
|
||
|
end
|
||
|
return iter
|
||
|
end
|
||
|
|
||
|
for k, v in pairsByKeys(flags) do
|
||
|
print(k .. " " .. v)
|
||
|
end
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"name foo",
|
||
|
"title mr",
|
||
|
}, "\n")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestOptionalStringArg(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo", "bar"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
fs:stringArg("title", "?", "Title")
|
||
|
flags = fs:parse(arg)
|
||
|
assert(flags.title == "bar", "expected flags.title to be 'bar'")
|
||
|
`
|
||
|
doString(src, t)
|
||
|
}
|
||
|
|
||
|
func TestOptionalStringsArg(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo", "bar"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
fs:stringArg("title", "*", "Title")
|
||
|
flags = fs:parse(arg)
|
||
|
assert(type(flags.title) == "table", "expected flags.title to be a 'table'")
|
||
|
assert(#flags.title == 1, "expected flags.title to have length == 1")
|
||
|
assert(flags.title[1] == "bar", "expected flags.title[1] to be 'bar'")
|
||
|
`
|
||
|
doString(src, t)
|
||
|
}
|
||
|
|
||
|
func TestStringsArg(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo", "bar"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
fs:stringArg("title", "+", "Title")
|
||
|
flags = fs:parse(arg)
|
||
|
assert(type(flags.title) == "table", "expected flags.title to be a 'table'")
|
||
|
assert(#flags.title == 1, "expected flags.title to have length == 1")
|
||
|
assert(flags.title[1] == "bar", "expected flags.title[1] to be 'bar'")
|
||
|
`
|
||
|
doString(src, t)
|
||
|
}
|
||
|
|
||
|
func TestStringsArgToFew(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
fs:stringArg("title", "+", "Title")
|
||
|
ok, err = pcall(function() fs:parse(arg) end)
|
||
|
print(err)
|
||
|
`
|
||
|
expected := "<string>:8: argument title: expected at least one string"
|
||
|
stdout, _ := doString(src, t)
|
||
|
if stdout != expected {
|
||
|
t.Errorf("expected: `%v`\ngot: `%v`\nsrc: `%v`", expected, stdout, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestNStringsArgument(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo", "bar", "baz"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
fs:stringArg("title", 2, "Title")
|
||
|
flags = fs:parse(arg)
|
||
|
assert(type(flags.title) == "table", "expected flags.title to be a 'table'")
|
||
|
assert(#flags.title == 2, "expected flags.title to have length == 1")
|
||
|
assert(flags.title[1] == "bar", "expected flags.title[1] to be 'bar'")
|
||
|
assert(flags.title[2] == "baz", "expected flags.title[1] to be 'baz'")
|
||
|
`
|
||
|
doString(src, t)
|
||
|
}
|
||
|
|
||
|
func TestNStringsArgToFew(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo", "bar"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
fs:string("name", "foo", "String help string")
|
||
|
fs:stringArg("title", 2, "Title")
|
||
|
ok, err = pcall(function() fs:parse(arg) end)
|
||
|
print(err)
|
||
|
`
|
||
|
expected := "<string>:8: argument title: expected 2 strings"
|
||
|
stdout, _ := doString(src, t)
|
||
|
if stdout != expected {
|
||
|
t.Errorf("expected: `%v`\ngot: `%v`\nsrc: `%v`", expected, stdout, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringArgumentCompgen(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
|
||
|
fs = flag.new("subcommand")
|
||
|
local function compgen()
|
||
|
return "fii", "foo", "fum"
|
||
|
end
|
||
|
|
||
|
res = {}
|
||
|
fs:string("name", "foo", "String help string", compgen)
|
||
|
fs:stringArg("mr", 1, "Title", function(arg, flags, raw)
|
||
|
res.arg = arg
|
||
|
res.flags = flags
|
||
|
res.raw = raw
|
||
|
return "mr", "miss", "mrs"
|
||
|
end)
|
||
|
|
||
|
local arg = {"-name", "foo", "m"}
|
||
|
arg[0] = "subcommand"
|
||
|
flags = fs:compgen(3, arg)
|
||
|
assert(res.arg == "m", "expected arg to be 'm', got " .. res.arg)
|
||
|
assert(res.flags.name == "foo", "expected flag.names to be 'foo', got " .. res.flags.name)
|
||
|
assert(res.raw[0] == "subcommand", "expected raw[0] to be 'subcommand', got " .. res.raw[0])
|
||
|
assert(res.raw[1] == "-name", "expected raw[1] to be '-name', got " .. res.raw[1])
|
||
|
|
||
|
print(table.concat(flags, " "))
|
||
|
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"mrs",
|
||
|
"miss",
|
||
|
"mr",
|
||
|
}, " ")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TeststringArgumentCompgen2(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
arg = {"-name", "foo", "m"}
|
||
|
arg[0] = "subcommand"
|
||
|
fs = flag.new()
|
||
|
local function compgen()
|
||
|
return "fii foo fum"
|
||
|
end
|
||
|
fs:string("name", "foo", "String help string", compgen)
|
||
|
fs:stringArgument("mr", 1, "Title", function()
|
||
|
return "mr miss mrs"
|
||
|
end)
|
||
|
flags = fs:compgen(3, arg)
|
||
|
|
||
|
print(flags)
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"mr",
|
||
|
"miss",
|
||
|
"mrs",
|
||
|
}, " ")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected: `%v`, got: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestStringArgumentUsage(t *testing.T) {
|
||
|
src := `
|
||
|
local flag = require('flag')
|
||
|
fs = flag.new("subcommand")
|
||
|
fs:stringArg("title", 1, "Your title")
|
||
|
|
||
|
print(fs:usage())
|
||
|
`
|
||
|
|
||
|
expected := strings.Join([]string{
|
||
|
"usage: subcommand title ",
|
||
|
" title string",
|
||
|
" \tYour title\n",
|
||
|
}, "\n")
|
||
|
got, _ := doString(src, t)
|
||
|
|
||
|
if got != expected {
|
||
|
t.Errorf("expected stdout: `%v`\ngot: `%v`\nsrc: `%v`", expected, got, src)
|
||
|
}
|
||
|
}
|