simplify conf based test parsing
this also fixes a problem with parsing comments in the conf file
diff --git a/docopt_test.go b/docopt_test.go
index 209c524..beaf0da 100644
--- a/docopt_test.go
+++ b/docopt_test.go
@@ -8,11 +8,10 @@
package docopt
import (
- "bufio"
"bytes"
"encoding/json"
"fmt"
- "io"
+ "io/ioutil"
"os"
"reflect"
"regexp"
@@ -1399,53 +1398,28 @@
// conf file based test cases
func TestFileTestcases(t *testing.T) {
- filename := "testcases.docopt"
- file, err := os.Open(filename)
- if err != nil {
- t.Fatal(err)
- }
- defer file.Close()
-
- for c := range parseTest(file) {
- if c.err != nil {
+ filenames := []string{"testcases.docopt", "test_golang.docopt"}
+ for _, filename := range filenames {
+ raw, err := ioutil.ReadFile(filename)
+ if err != nil {
t.Fatal(err)
- break
}
- result, _, err := parse(c.doc, c.argv, true, "", false)
- if _, ok := err.(*UserError); c.userError && !ok {
- // expected a user-error
- t.Error("testcase:", c.id, "result:", result)
- } else if _, ok := err.(*UserError); !c.userError && ok {
- // unexpected user-error
- t.Error("testcase:", c.id, "error:", err, "result:", result)
- } else if reflect.DeepEqual(c.expect, result) != true {
- t.Error("testcase:", c.id, "result:", result)
- }
- }
-}
-func TestFileTestcasesGo(t *testing.T) {
- filename := "test_golang.docopt"
- file, err := os.Open(filename)
- if err != nil {
- t.Fatal(err)
- }
- defer file.Close()
-
- for c := range parseTest(file) {
- if c.err != nil {
+ tests, err := parseTest(raw)
+ if err != nil {
t.Fatal(err)
- break
}
- result, _, err := parse(c.doc, c.argv, true, "", false)
- if _, ok := err.(*UserError); c.userError && !ok {
- // expected a user-error
- t.Error("testcase:", c.id, "result:", result)
- } else if _, ok := err.(*UserError); !c.userError && ok {
- // unexpected user-error
- t.Error("testcase:", c.id, "error:", err, "result:", result)
- } else if reflect.DeepEqual(c.expect, result) != true {
- t.Error("testcase:", c.id, "result:", result)
+ for _, c := range tests {
+ result, _, err := parse(c.doc, c.argv, true, "", false)
+ if _, ok := err.(*UserError); c.userError && !ok {
+ // expected a user-error
+ t.Error("testcase:", c.id, "result:", result)
+ } else if _, ok := err.(*UserError); !c.userError && ok {
+ // unexpected user-error
+ t.Error("testcase:", c.id, "error:", err, "result:", result)
+ } else if reflect.DeepEqual(c.expect, result) != true {
+ t.Error("testcase:", c.id, "result:", result, "expect:", c.expect)
+ }
}
}
}
@@ -1457,96 +1431,60 @@
argv []string
expect map[string]interface{}
userError bool
- err error
}
-func parseTest(raw io.Reader) <-chan testcase {
- c := make(chan testcase)
+func parseTest(raw []byte) ([]testcase, error) {
+ var res []testcase
+ commentPattern := regexp.MustCompile("#.*")
+ raw = commentPattern.ReplaceAll(raw, []byte(""))
+ raw = bytes.TrimSpace(raw)
+ if bytes.HasPrefix(raw, []byte(`"""`)) {
+ raw = raw[3:]
+ }
- go func() {
- scanner := bufio.NewScanner(raw)
- commentPattern := regexp.MustCompile("#.*($|\n)")
- scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
- // look for matching `r"""` or `r"""`+EOF
- sep := []byte(`r"""`)
- count := bytes.Count(data, sep)
- start := bytes.Index(data, sep)
- end := len(data)
-
- if atEOF && count == 0 {
- // no more matches, so consume everything
- return end, nil, nil
+ id := 0
+ for _, fixture := range bytes.Split(raw, []byte(`r"""`)) {
+ doc, _, body := stringPartition(string(fixture), `"""`)
+ for _, cas := range strings.Split(body, "$")[1:] {
+ argvString, _, expectString := stringPartition(strings.TrimSpace(cas), "\n")
+ prog, _, argvString := stringPartition(strings.TrimSpace(argvString), " ")
+ argv := []string{}
+ if len(argvString) > 0 {
+ argv = strings.Fields(argvString)
}
- if atEOF && count == 1 {
- // already matched up to end
- } else if count >= 2 {
- end = start + len(sep) + bytes.Index(data[start+len(sep):], sep)
- } else {
- // haven't found a match yet so ask for more data
- return 0, nil, nil
+ var expectUntyped interface{}
+ err := json.Unmarshal([]byte(expectString), &expectUntyped)
+ if err != nil {
+ return nil, err
}
- token = data[start:end]
- token = commentPattern.ReplaceAllLiteral(token, []byte(""))
- return end, token, nil
- })
-
- for id := 0; scanner.Scan(); {
- data := scanner.Bytes()
-
- offset := 4
- end := offset + bytes.Index(data[offset:], []byte(`"""`))
- doc := string(data[offset:end])
- offset = end + 3
-
- for offset < len(data) {
- offset = offset + bytes.Index(data[offset:], []byte(`$ `)) + 2
- end = offset + bytes.Index(data[offset:], []byte("\n"))
- prog, _, args := stringPartition(string(data[offset:end]), " ")
- argv := strings.Fields(args)
- offset = end + 1
-
- if bytes.Contains(data[offset:], []byte("$ ")) {
- end = offset + bytes.Index(data[offset:], []byte("$ "))
- } else {
- end = len(data)
- }
- var expectUntyped interface{}
- err := json.Unmarshal(data[offset:end], &expectUntyped)
- offset = end
-
- if err != nil {
- c <- testcase{id, doc, prog, argv, nil, false, err}
- break
- }
-
- switch expect := expectUntyped.(type) {
- case string: // user-error
- c <- testcase{id, doc, prog, argv, nil, true, nil}
- case map[string]interface{}:
- // convert []interface{} values to []string
- // convert float64 values to int
- for k, vUntyped := range expect {
- switch v := vUntyped.(type) {
- case []interface{}:
- itemList := make([]string, len(v))
- for i, itemUntyped := range v {
- if item, ok := itemUntyped.(string); ok {
- itemList[i] = item
- }
+ switch expect := expectUntyped.(type) {
+ case string: // user-error
+ res = append(res, testcase{id, doc, prog, argv, nil, true})
+ case map[string]interface{}:
+ // convert []interface{} values to []string
+ // convert float64 values to int
+ for k, vUntyped := range expect {
+ switch v := vUntyped.(type) {
+ case []interface{}:
+ itemList := make([]string, len(v))
+ for i, itemUntyped := range v {
+ if item, ok := itemUntyped.(string); ok {
+ itemList[i] = item
}
- expect[k] = itemList
- case float64:
- expect[k] = int(v)
}
+ expect[k] = itemList
+ case float64:
+ expect[k] = int(v)
}
- c <- testcase{id, doc, prog, argv, expect, false, nil}
}
- id++
+ res = append(res, testcase{id, doc, prog, argv, expect, false})
+ default:
+ return nil, fmt.Errorf("unhandled json data type")
}
+ id++
}
- close(c)
- }()
- return c
+ }
+ return res, nil
}
var debugEnabled = false