From bbafa19bff5428c4622018e1b7595278202f2d66 Mon Sep 17 00:00:00 2001
From: Neil Pankey <npankey@gmail.com>
Date: Sat, 21 Mar 2020 18:38:32 -0700
Subject: [PATCH] test: Table-driven tests

---
 main.go      |  41 +++++------
 main_test.go | 188 ++++++++++++++++++++++-----------------------------
 2 files changed, 99 insertions(+), 130 deletions(-)

diff --git a/main.go b/main.go
index a21d14a..beae150 100644
--- a/main.go
+++ b/main.go
@@ -6,6 +6,7 @@ import (
 	"bufio"
 	"flag"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"log"
 	"os"
@@ -37,13 +38,13 @@ func init() {
 
 func main() {
 	log.SetFlags(0)
-	os.Exit(realMain(os.Args[1:]))
+	os.Exit(realMain(os.Args[1:], os.Stdout))
 }
 
-func realMain(args []string) int {
+func realMain(args []string, w io.Writer) int {
 	flag.CommandLine.Parse(args)
 	if *versionFlag {
-		fmt.Println(version)
+		fmt.Fprintln(w, version)
 		return 0
 	}
 	if *schemaFlag == "" {
@@ -70,7 +71,6 @@ func realMain(args []string) int {
 			if !filepath.IsAbs(pattern) {
 				pattern = filepath.Join(dir, pattern)
 			}
-
 			docs = append(docs, glob(pattern)...)
 		}
 		if err := scanner.Err(); err != nil {
@@ -89,9 +89,9 @@ func realMain(args []string) int {
 	}
 	for _, ref := range refFlags {
 		for _, p := range glob(ref) {
-			absPath, absPathErr := filepath.Abs(p)
-			if absPathErr != nil {
-				log.Fatalf("%s: unable to convert to absolute path: %s\n", absPath, absPathErr)
+			absPath, err := filepath.Abs(p)
+			if err != nil {
+				log.Fatalf("%s: unable to convert to absolute path: %s\n", absPath, err)
 			}
 
 			if absPath == schemaPath {
@@ -125,7 +125,6 @@ func realMain(args []string) int {
 	failures := make([]string, 0)
 	errors := make([]string, 0)
 	for _, p := range docs {
-		//fmt.Println(p)
 		wg.Add(1)
 		go func(path string) {
 			defer wg.Done()
@@ -135,8 +134,8 @@ func realMain(args []string) int {
 
 			loader, err := jsonLoader(path)
 			if err != nil {
-				msg := fmt.Sprintf("%s: error: load doc %s\n", path, err)
-				fmt.Println(msg)
+				msg := fmt.Sprintf("%s: error: load doc: %s", path, err)
+				fmt.Fprintln(w, msg)
 				errors = append(errors, msg)
 				return
 			}
@@ -144,7 +143,7 @@ func realMain(args []string) int {
 			switch {
 			case err != nil:
 				msg := fmt.Sprintf("%s: error: validate: %s", path, err)
-				fmt.Println(msg)
+				fmt.Fprintln(w, msg)
 				errors = append(errors, msg)
 
 			case !result.Valid():
@@ -153,11 +152,11 @@ func realMain(args []string) int {
 					lines[i] = fmt.Sprintf("%s: fail: %s", path, desc)
 				}
 				msg := strings.Join(lines, "\n")
-				fmt.Println(msg)
+				fmt.Fprintln(w, msg)
 				failures = append(failures, msg)
 
 			case !*quietFlag:
-				fmt.Printf("%s: pass\n", path)
+				fmt.Fprintf(w, "%s: pass\n", path)
 			}
 		}(p)
 	}
@@ -166,12 +165,12 @@ func realMain(args []string) int {
 	// Summarize results (e.g. errors)
 	if !*quietFlag {
 		if len(failures) > 0 {
-			fmt.Printf("%d of %d failed validation\n", len(failures), len(docs))
-			fmt.Println(strings.Join(failures, "\n"))
+			fmt.Fprintf(w, "%d of %d failed validation\n", len(failures), len(docs))
+			fmt.Fprintln(w, strings.Join(failures, "\n"))
 		}
 		if len(errors) > 0 {
-			fmt.Printf("%d of %d malformed documents\n", len(errors), len(docs))
-			fmt.Println(strings.Join(errors, "\n"))
+			fmt.Fprintf(w, "%d of %d malformed documents\n", len(errors), len(docs))
+			fmt.Fprintln(w, strings.Join(errors, "\n"))
 		}
 	}
 	exit := 0
@@ -235,18 +234,14 @@ func glob(pattern string) []string {
 	if err != nil {
 		log.Fatal(err)
 	}
-	universalPaths := make([]string, 0)
 	paths, err := filepath.Glob(pattern)
-	for _, mypath := range paths {
-		universalPaths = append(universalPaths, filepath.ToSlash(mypath))
-	}
 	if err != nil {
 		log.Fatal(err)
 	}
-	if len(universalPaths) == 0 {
+	if len(paths) == 0 {
 		log.Fatalf("%s: no such file or directory", pattern)
 	}
-	return universalPaths
+	return paths
 }
 
 type stringFlags []string
diff --git a/main_test.go b/main_test.go
index 875ba19..a2883d1 100644
--- a/main_test.go
+++ b/main_test.go
@@ -1,118 +1,92 @@
-// +build windows !windows
-
 package main
 
 import (
-	"log"
+	"path/filepath"
+	"sort"
+	"strings"
+	"testing"
 )
 
-func ExampleMain_pass_ymlschema_ymldoc() {
-	exit := realMain([]string{"-s", "testdata/schema.yml", "testdata/data-pass.yml"})
-	if exit != 0 {
-		log.Fatalf("exit: got %d, want 0", exit)
-	}
-	// Output:
-	// testdata/data-pass.yml: pass
-}
-
-func ExampleMain_pass_jsonschema_ymldoc() {
-	exit := realMain([]string{"-s", "testdata/schema.json", "testdata/data-pass.yml"})
-	if exit != 0 {
-		log.Fatalf("exit: got %d, want 0", exit)
-	}
-	// Output:
-	// testdata/data-pass.yml: pass
-}
-
-func ExampleMain_pass_jsonschema_jsondoc() {
-	exit := realMain([]string{"-s", "testdata/schema.json", "testdata/data-pass.json"})
-	if exit != 0 {
-		log.Fatalf("exit: got %d, want 0", exit)
-	}
-	// Output:
-	// testdata/data-pass.json: pass
-}
-
-func ExampleMain_pass_ymlschema_jsondoc() {
-	exit := realMain([]string{"-s", "testdata/schema.yml", "testdata/data-pass.json"})
-	if exit != 0 {
-		log.Fatalf("exit: got %d, want 0", exit)
-	}
-	// Output:
-	// testdata/data-pass.json: pass
-}
-
-func ExampleMain_fail_ymlschema_ymldoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.yml", "testdata/data-fail.yml"})
-	if exit != 1 {
-		log.Fatalf("exit: got %d, want 1", exit)
-	}
-	// Output:
-	// testdata/data-fail.yml: fail: (root): foo is required
-}
-
-func ExampleMain_fail_jsonschema_ymldoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.json", "testdata/data-fail.yml"})
-	if exit != 1 {
-		log.Fatalf("exit: got %d, want 1", exit)
-	}
-	// Output:
-	// testdata/data-fail.yml: fail: (root): foo is required
-}
-
-func ExampleMain_fail_jsonschema_jsondoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.json", "testdata/data-fail.json"})
-	if exit != 1 {
-		log.Fatalf("exit: got %d, want 1", exit)
+func TestMain(t *testing.T) {
+	tests := []struct {
+		in string
+		out []string
+		exit int
+	} {
+		{
+			"-s testdata/schema.yml testdata/data-pass.yml",
+			[]string{"testdata/data-pass.yml: pass"},
+			0,
+		}, {
+			"-s testdata/schema.json testdata/data-pass.yml",
+			[]string{"testdata/data-pass.yml: pass"},
+			0,
+		}, {
+			"-s testdata/schema.json testdata/data-pass.json",
+			[]string{"testdata/data-pass.json: pass"},
+			0,
+		}, {
+			"-s testdata/schema.yml testdata/data-pass.json",
+			[]string{"testdata/data-pass.json: pass"},
+			0,
+		}, {
+			"-q -s testdata/schema.yml testdata/data-fail.yml",
+			[]string{"testdata/data-fail.yml: fail: (root): foo is required"},
+			1,
+		}, {
+			"-q -s testdata/schema.json testdata/data-fail.yml",
+			[]string{"testdata/data-fail.yml: fail: (root): foo is required"},
+			1,
+		}, {
+			"-q -s testdata/schema.json testdata/data-fail.json",
+			[]string{"testdata/data-fail.json: fail: (root): foo is required"},
+			1,
+		}, {
+			"-q -s testdata/schema.yml testdata/data-fail.json",
+			[]string{"testdata/data-fail.json: fail: (root): foo is required"},
+			1,
+		}, {
+			"-q -s testdata/schema.json testdata/data-error.json",
+			[]string{"testdata/data-error.json: error: validate: invalid character 'o' in literal null (expecting 'u')"},
+			2,
+		}, {
+			"-q -s testdata/schema.yml testdata/data-error.yml",
+			[]string{"testdata/data-error.yml: error: load doc: yaml: found unexpected end of stream"},
+			2,
+		}, {
+			"-q -s testdata/schema.json testdata/data-*.json",
+			[]string{
+				"testdata/data-fail.json: fail: (root): foo is required",
+				"testdata/data-error.json: error: validate: invalid character 'o' in literal null (expecting 'u')",
+			}, 3,
+		}, {
+			"-q -s testdata/schema.yml testdata/data-*.yml",
+			[]string{
+				"testdata/data-error.yml: error: load doc: yaml: found unexpected end of stream",
+				"testdata/data-fail.yml: fail: (root): foo is required",
+			}, 3,
+		},
 	}
-	// Output:
-	// testdata/data-fail.json: fail: (root): foo is required
-}
 
-func ExampleMain_fail_ymlschema_jsondoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.yml", "testdata/data-fail.json"})
-	if exit != 1 {
-		log.Fatalf("exit: got %d, want 1", exit)
-	}
-	// Output:
-	// testdata/data-fail.json: fail: (root): foo is required
-}
+	for _, tt := range tests {
+		in := strings.ReplaceAll(tt.in, "/", string(filepath.Separator))
+		sort.Strings(tt.out)
+		out := strings.Join(tt.out, "\n")
+		out = strings.ReplaceAll(out, "/", string(filepath.Separator))
 
-func ExampleMain_error_jsonschema_jsondoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.json", "testdata/data-error.json"})
-	if exit != 2 {
-		log.Fatalf("exit: got %d, want 2", exit)
+		t.Run(in, func(t *testing.T) {
+			var w strings.Builder
+			exit := realMain(strings.Split(in, " "), &w)
+			if exit != tt.exit {
+				t.Fatalf("exit: got %d, want %d", exit, tt.exit)
+			}
+			lines := strings.Split(w.String(), "\n")
+			sort.Strings(lines)
+			got := strings.Join(lines[1:], "\n")
+			if got != out {
+				t.Errorf("got\n%s\nwant\n%s", got, out)
+			}
+		})
 	}
-	// Output:
-	// testdata/data-error.json: error: validate: invalid character 'o' in literal null (expecting 'u')
 }
 
-func ExampleMain_error_ymlschema_ymldoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.yml", "testdata/data-error.yml"})
-	if exit != 2 {
-		log.Fatalf("exit: got %d, want 2", exit)
-	}
-	// Output:
-	// testdata/data-error.yml: error: load doc yaml: found unexpected end of stream
-}
-
-func ExampleMain_glob_jsonschema_jsondoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.json", "testdata/data-*.json"})
-	if exit != 3 {
-		log.Fatalf("exit: got %d, want 3", exit)
-	}
-	// Unordered output:
-	// testdata/data-error.json: error: validate: invalid character 'o' in literal null (expecting 'u')
-	// testdata/data-fail.json: fail: (root): foo is required
-}
-
-func ExampleMain_glob_ymlschema_ymldoc() {
-	exit := realMain([]string{"-q", "-s", "testdata/schema.yml", "testdata/data-*.yml"})
-	if exit != 3 {
-		log.Fatalf("exit: got %d, want 3", exit)
-	}
-	// Unordered output:
-	// testdata/data-fail.yml: fail: (root): foo is required
-	//
-	// testdata/data-error.yml: error: load doc yaml: found unexpected end of stream
-}
-- 
GitLab