shithub: hugo

Download patch

ref: 82eefded1353f0198fd8fe9f7df1aa620d3d50eb
parent: e1728349930e2cc1b6580766473de98adb0f3e50
author: Vas Sudanagunta <[email protected]>
date: Sun Feb 11 11:27:47 EST 2018

hugolib: Refactor tests for JSON, YAML and TOML equivalency, add coverage

The same code now cycles through equivalent JSON, YAML and TOML data sets,
verifying output both proper and identical. Coverage increased by applying
previous tests for one format to the others.

More DRY. Added tests for numeric and bool value types.

--- a/hugolib/datafiles_test.go
+++ b/hugolib/datafiles_test.go
@@ -32,13 +32,12 @@
 	"github.com/stretchr/testify/require"
 )
 
-func TestDataDirJSON(t *testing.T) {
+func TestDataDir(t *testing.T) {
 	t.Parallel()
-
-	sources := [][2]string{
-		{filepath.FromSlash("data/test/a.json"), `{ "b" : { "c1": "red" , "c2": "blue" } }`},
-	}
-
+	equivDataDirs := make([]dataDir, 3)
+	equivDataDirs[0].addSource("data/test/a.json", `{ "b" : { "c1": "red" , "c2": "blue" } }`)
+	equivDataDirs[1].addSource("data/test/a.yaml", "b:\n  c1: red\n  c2: blue")
+	equivDataDirs[2].addSource("data/test/a.toml", "[b]\nc1 = \"red\"\nc2 = \"blue\"\n")
 	expected := map[string]interface{}{
 		"test": map[string]interface{}{
 			"a": map[string]interface{}{
@@ -49,85 +48,67 @@
 			},
 		},
 	}
-
-	doTestDataDir(t, expected, sources)
+	doTestEquivalentDataDirs(t, equivDataDirs, expected)
 }
 
-func TestDataDirYAML(t *testing.T) {
+// Unable to enforce equivalency for int values as
+// the JSON, YAML and TOML parsers return
+// float64, int, int64 respectively. They all return
+// float64 for float values though:
+func TestDataDirNumeric(t *testing.T) {
 	t.Parallel()
-
-	sources := [][2]string{
-		{"data/test/a.yaml", "b:\n  c1: red\n  c2: blue"},
-	}
-
+	equivDataDirs := make([]dataDir, 3)
+	equivDataDirs[0].addSource("data/test/a.json", `{ "b" : { "c1": 1.7 , "c2": 2.9 } }`)
+	equivDataDirs[1].addSource("data/test/a.yaml", "b:\n  c1: 1.7\n  c2: 2.9")
+	equivDataDirs[2].addSource("data/test/a.toml", "[b]\nc1 = 1.7\nc2 = 2.9\n")
 	expected := map[string]interface{}{
 		"test": map[string]interface{}{
 			"a": map[string]interface{}{
 				"b": map[string]interface{}{
-					"c1": "red",
-					"c2": "blue",
+					"c1": 1.7,
+					"c2": 2.9,
 				},
 			},
 		},
 	}
-
-	doTestDataDir(t, expected, sources)
+	doTestEquivalentDataDirs(t, equivDataDirs, expected)
 }
 
-func TestDataDirToml(t *testing.T) {
+func TestDataDirBoolean(t *testing.T) {
 	t.Parallel()
-
-	sources := [][2]string{
-		{"data/test/a.toml", "[b]\nc1 = \"red\"\nc2 = \"blue\"\n"},
-	}
-
+	equivDataDirs := make([]dataDir, 3)
+	equivDataDirs[0].addSource("data/test/a.json", `{ "b" : { "c1": true , "c2": false } }`)
+	equivDataDirs[1].addSource("data/test/a.yaml", "b:\n  c1: true\n  c2: false")
+	equivDataDirs[2].addSource("data/test/a.toml", "[b]\nc1 = true\nc2 = false\n")
 	expected := map[string]interface{}{
 		"test": map[string]interface{}{
 			"a": map[string]interface{}{
 				"b": map[string]interface{}{
-					"c1": "red",
-					"c2": "blue",
+					"c1": true,
+					"c2": false,
 				},
 			},
 		},
 	}
-
-	doTestDataDir(t, expected, sources)
+	doTestEquivalentDataDirs(t, equivDataDirs, expected)
 }
 
-func TestDataDirJSON2(t *testing.T) {
+func TestDataDirTwoFiles(t *testing.T) {
 	t.Parallel()
+	equivDataDirs := make([]dataDir, 2)
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/test/foo.json"), `{ "bar": "foofoo"  }`},
-		{filepath.FromSlash("data/test.json"), `{ "hello": [ { "world": "foo" } ] }`},
-	}
+	equivDataDirs[0].addSource("data/test/foo.json", `{ "bar": "foofoo"  }`)
+	equivDataDirs[0].addSource("data/test.json", `{ "hello": [ { "world": "foo" } ] }`)
 
-	expected :=
-		map[string]interface{}{
-			"test": map[string]interface{}{
-				"hello": []interface{}{
-					map[string]interface{}{"world": "foo"},
-				},
-				"foo": map[string]interface{}{
-					"bar": "foofoo",
-				},
-			},
-		}
+	equivDataDirs[1].addSource("data/test/foo.yaml", "bar: foofoo")
+	equivDataDirs[1].addSource("data/test.yaml", "hello:\n- world: foo")
 
-	doTestDataDir(t, expected, sources)
-}
+	// TODO Unresolved Issue #3890
+	/*
+		equivDataDirs[2].addSource("data/test/foo.toml", "bar = \"foofoo\"")
+		equivDataDirs[2].addSource("data/test.toml", "[[hello]]\nworld = \"foo\"")
+	*/
 
-func TestDataDirYAML2(t *testing.T) {
-	t.Parallel()
-
-	sources := [][2]string{
-		{filepath.FromSlash("data/test/foo.yaml"), "bar: foofoo"},
-		{filepath.FromSlash("data/test.yaml"), "hello:\n- world: foo"},
-	}
-
-	//This is what we want: consistent use of map[string]interface{} for nested YAML maps
-	// the same as TestDataDirJSON
 	expected :=
 		map[string]interface{}{
 			"test": map[string]interface{}{
@@ -140,43 +121,29 @@
 			},
 		}
 
-	doTestDataDir(t, expected, sources)
+	doTestEquivalentDataDirs(t, equivDataDirs, expected)
 }
 
-func TestDataDirToml2(t *testing.T) {
+func TestDataDirOverriddenValue(t *testing.T) {
 	t.Parallel()
+	equivDataDirs := make([]dataDir, 3)
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/test/foo.toml"), "bar = \"foofoo\""},
-		{filepath.FromSlash("data/test.toml"), "[[hello]]\nworld = \"foo\""},
-	}
+	// filepath.Walk walks the files in lexical order, '/' comes before '.'. Simulate this:
+	equivDataDirs[0].addSource("data/a.json", `{"a": "1"}`)
+	equivDataDirs[0].addSource("data/test/v1.json", `{"v1-2": "2"}`)
+	equivDataDirs[0].addSource("data/test/v2.json", `{"v2": ["2", "3"]}`)
+	equivDataDirs[0].addSource("data/test.json", `{"v1": "1"}`)
 
-	expected :=
-		map[string]interface{}{
-			"test": map[string]interface{}{
-				"hello": []map[string]interface{}{
-					map[string]interface{}{"world": "foo"},
-				},
-				"foo": map[string]interface{}{
-					"bar": "foofoo",
-				},
-			},
-		}
+	equivDataDirs[1].addSource("data/a.yaml", "a: \"1\"")
+	equivDataDirs[1].addSource("data/test/v1.yaml", "v1-2: \"2\"")
+	equivDataDirs[1].addSource("data/test/v2.yaml", "v2:\n- \"2\"\n- \"3\"")
+	equivDataDirs[1].addSource("data/test.yaml", "v1: \"1\"")
 
-	doTestDataDir(t, expected, sources)
-}
+	equivDataDirs[2].addSource("data/a.toml", "a = \"1\"")
+	equivDataDirs[2].addSource("data/test/v1.toml", "v1-2 = \"2\"")
+	equivDataDirs[2].addSource("data/test/v2.toml", "v2 = [\"2\", \"3\"]")
+	equivDataDirs[2].addSource("data/test.toml", "v1 = \"1\"")
 
-func TestDataDirJSONWithOverriddenValue(t *testing.T) {
-	t.Parallel()
-
-	sources := [][2]string{
-		// filepath.Walk walks the files in lexical order, '/' comes before '.'. Simulate this:
-		{filepath.FromSlash("data/a.json"), `{"a": "1"}`},
-		{filepath.FromSlash("data/test/v1.json"), `{"v1-2": "2"}`},
-		{filepath.FromSlash("data/test/v2.json"), `{"v2": ["2", "3"]}`},
-		{filepath.FromSlash("data/test.json"), `{"v1": "1"}`},
-	}
-
 	expected :=
 		map[string]interface{}{
 			"a": map[string]interface{}{"a": "1"},
@@ -186,39 +153,15 @@
 			},
 		}
 
-	doTestDataDir(t, expected, sources)
+	doTestEquivalentDataDirs(t, equivDataDirs, expected)
 }
 
-func TestDataDirYAMLWithOverridenValue(t *testing.T) {
-	t.Parallel()
-
-	sources := [][2]string{
-		// filepath.Walk walks the files in lexical order, '/' comes before '.'. Simulate this:
-		{filepath.FromSlash("data/a.yaml"), "a: 1"},
-		{filepath.FromSlash("data/test/v1.yaml"), "v1-2: 2"},
-		{filepath.FromSlash("data/test/v2.yaml"), "v2:\n- 2\n- 3"},
-		{filepath.FromSlash("data/test.yaml"), "v1: 1"},
-	}
-
-	expected :=
-		map[string]interface{}{
-			"a": map[string]interface{}{"a": 1},
-			"test": map[string]interface{}{
-				"v1": map[string]interface{}{"v1-2": 2},
-				"v2": map[string]interface{}{"v2": []interface{}{2, 3}},
-			},
-		}
-
-	doTestDataDir(t, expected, sources)
-}
-
 // Issue #4361
 func TestDataDirJSONArrayAtTopLevelOfFile(t *testing.T) {
 	t.Parallel()
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/test.json"), `[ { "hello": "world" }, { "what": "time" }, { "is": "lunch?" } ]`},
-	}
+	var dd dataDir
+	dd.addSource("data/test.json", `[ { "hello": "world" }, { "what": "time" }, { "is": "lunch?" } ]`)
 
 	expected :=
 		map[string]interface{}{
@@ -229,7 +172,7 @@
 			},
 		}
 
-	doTestDataDir(t, expected, sources)
+	doTestDataDir(t, dd, expected)
 }
 
 // TODO Issue #3890 unresolved
@@ -236,13 +179,12 @@
 func TestDataDirYAMLArrayAtTopLevelOfFile(t *testing.T) {
 	t.Parallel()
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/test.yaml"), `
+	var dd dataDir
+	dd.addSource("data/test.yaml", `
 - hello: world
 - what: time
 - is: lunch?
-`},
-	}
+`)
 
 	//TODO decide whether desired structure map[interface {}]interface{} as shown
 	// and as the YAML parser produces, or should it be map[string]interface{}
@@ -261,7 +203,7 @@
 		map[string]interface{}{}
 	_ = expected
 
-	doTestDataDir(t, expectedV0_34, sources)
+	doTestDataDir(t, dd, expectedV0_34)
 }
 
 // Issue #892
@@ -268,11 +210,10 @@
 func TestDataDirMultipleSources(t *testing.T) {
 	t.Parallel()
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/test/first.yaml"), "bar: 1"},
-		{filepath.FromSlash("themes/mytheme/data/test/first.yaml"), "bar: 2"},
-		{filepath.FromSlash("data/test/second.yaml"), "tender: 2"},
-	}
+	var dd dataDir
+	dd.addSource("data/test/first.yaml", "bar: 1")
+	dd.addSource("themes/mytheme/data/test/first.yaml", "bar: 2")
+	dd.addSource("data/test/second.yaml", "tender: 2")
 
 	expected :=
 		map[string]interface{}{
@@ -286,21 +227,21 @@
 			},
 		}
 
-	doTestDataDir(t, expected, sources,
+	doTestDataDir(t, dd, expected,
 		"theme", "mytheme")
 
 }
 
-// test (and show) the way values from four different sources commingle and override
+// test (and show) the way values from four different sources,
+// including theme data, commingle and override
 func TestDataDirMultipleSourcesCommingled(t *testing.T) {
 	t.Parallel()
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/a.json"), `{ "b1" : { "c1": "data/a" }, "b2": "data/a", "b3": ["x", "y", "z"] }`},
-		{filepath.FromSlash("themes/mytheme/data/a.json"), `{ "b1": "mytheme/data/a",  "b2": "mytheme/data/a", "b3": "mytheme/data/a" }`},
-		{filepath.FromSlash("themes/mytheme/data/a/b1.json"), `{ "c1": "mytheme/data/a/b1", "c2": "mytheme/data/a/b1" }`},
-		{filepath.FromSlash("data/a/b1.json"), `{ "c1": "data/a/b1" }`},
-	}
+	var dd dataDir
+	dd.addSource("data/a.json", `{ "b1" : { "c1": "data/a" }, "b2": "data/a", "b3": ["x", "y", "z"] }`)
+	dd.addSource("themes/mytheme/data/a.json", `{ "b1": "mytheme/data/a",  "b2": "mytheme/data/a", "b3": "mytheme/data/a" }`)
+	dd.addSource("themes/mytheme/data/a/b1.json", `{ "c1": "mytheme/data/a/b1", "c2": "mytheme/data/a/b1" }`)
+	dd.addSource("data/a/b1.json", `{ "c1": "data/a/b1" }`)
 
 	// Per handleDataFile() comment:
 	// 1. A theme uses the same key; the main data folder wins
@@ -317,17 +258,17 @@
 			},
 		}
 
-	doTestDataDir(t, expected, sources,
-		"theme", "mytheme")
+	doTestDataDir(t, dd, expected, "theme", "mytheme")
 }
 
-func TestDataDirMultipleSourcesCollidingChildArrays(t *testing.T) {
+// TODO Issue #4366 unresolved
+func _TestDataDirMultipleSourcesCollidingChildArrays(t *testing.T) {
 	t.Parallel()
 
-	sources := [][2]string{
-		{filepath.FromSlash("data/a.json"), `{ "b1" : "data/a", "b2" : ["x", "y", "z"] }`},
-		{filepath.FromSlash("data/a/b2.json"), `["1", "2", "3"]`},
-	}
+	var dd dataDir
+	dd.addSource("themes/mytheme/data/a/b2.json", `["Q", "R", "S"]`)
+	dd.addSource("data/a.json", `{ "b1" : "data/a", "b2" : ["x", "y", "z"] }`)
+	dd.addSource("data/a/b2.json", `["1", "2", "3"]`)
 
 	// Per handleDataFile() comment:
 	// 1. A theme uses the same key; the main data folder wins
@@ -340,18 +281,16 @@
 			},
 		}
 
-	doTestDataDir(t, expected, sources,
-		"theme", "mytheme")
+	doTestDataDir(t, dd, expected, "theme", "mytheme")
 }
 
 // TODO Issue #4366 unresolved
-func TestDataDirMultipleSourcesCollidingTopLevelArrays(t *testing.T) {
+func _TestDataDirMultipleSourcesCollidingTopLevelArrays(t *testing.T) {
 	t.Parallel()
 
-	sources := [][2]string{
-		{filepath.FromSlash("themes/mytheme/data/a/b1.json"), `["x", "y", "z"]`},
-		{filepath.FromSlash("data/a/b1.json"), `["1", "2", "3"]`},
-	}
+	var dd dataDir
+	dd.addSource("themes/mytheme/data/a/b1.json", `["x", "y", "z"]`)
+	dd.addSource("data/a/b1.json", `["1", "2", "3"]`)
 
 	expected :=
 		map[string]interface{}{
@@ -360,16 +299,34 @@
 			},
 		}
 
-	// as of v0.34 this test results in a go Panic
-	_ = sources
-	_ = expected
-	/*
-		doTestDataDir(t, expectedV0_35, sources,
-			"theme", "mytheme")
-	*/
+	doTestDataDir(t, dd, expected, "theme", "mytheme")
 }
 
-func doTestDataDir(t *testing.T, expected interface{}, sources [][2]string, configKeyValues ...interface{}) {
+type dataDir struct {
+	sources [][2]string
+}
+
+func (d *dataDir) addSource(path, content string) {
+	d.sources = append(d.sources, [2]string{path, content})
+}
+
+func doTestEquivalentDataDirs(t *testing.T, equivDataDirs []dataDir, expected interface{}, configKeyValues ...interface{}) {
+	for i, dd := range equivDataDirs {
+		err := doTestDataDirImpl(t, dd, expected, configKeyValues...)
+		if err != "" {
+			t.Errorf("equivDataDirs[%d]: %s", i, err)
+		}
+	}
+}
+
+func doTestDataDir(t *testing.T, dd dataDir, expected interface{}, configKeyValues ...interface{}) {
+	err := doTestDataDirImpl(t, dd, expected, configKeyValues...)
+	if err != "" {
+		t.Error(err)
+	}
+}
+
+func doTestDataDirImpl(t *testing.T, dd dataDir, expected interface{}, configKeyValues ...interface{}) (err string) {
 	var (
 		cfg, fs = newTestCfg()
 	)
@@ -384,7 +341,7 @@
 	)
 
 	writeSource(t, fs, filepath.Join("content", "dummy.md"), "content")
-	writeSourcesToSource(t, "", fs, sources...)
+	writeSourcesToSource(t, "", fs, dd.sources...)
 
 	expectBuildError := false
 
@@ -405,17 +362,26 @@
 	s := buildSingleSiteExpected(t, expectBuildError, depsCfg, BuildCfg{SkipRender: true})
 
 	if !expectBuildError && !reflect.DeepEqual(expected, s.Data) {
-		exp := fmt.Sprintf("%#v", expected)
-		got := fmt.Sprintf("%#v", s.Data)
-		if exp == got { //TODO: This workaround seems to be triggered only by the TOML tests
-			t.Logf("WARNING: reflect.DeepEqual returned FALSE for values that appear equal.\n"+
-				"Treating as equal for the purpose of the test, but this maybe should be investigated.\n"+
-				"Expected data:\n%v got\n%v\n\nExpected type structure:\n%#[1]v got\n%#[2]v", expected, s.Data)
-			return
-		}
+		// This disabled code detects the situation described in the WARNING message below.
+		// The situation seems to only occur for TOML data with integer values.
+		// Perhaps the TOML parser returns ints in another type.
+		// Re-enable temporarily to debug fails that should be passing.
+		// Re-enable permanently if reflect.DeepEqual is simply too strict.
+		/*
+			exp := fmt.Sprintf("%#v", expected)
+			got := fmt.Sprintf("%#v", s.Data)
+			if exp == got {
+				t.Logf("WARNING: reflect.DeepEqual returned FALSE for values that appear equal.\n"+
+					"Treating as equal for the purpose of the test, but this maybe should be investigated.\n"+
+					"Expected data:\n%v got\n%v\n\nExpected type structure:\n%#[1]v got\n%#[2]v", expected, s.Data)
+				return
+			}
+		*/
 
-		t.Errorf("Expected data:\n%v got\n%v\n\nExpected type structure:\n%#[1]v got\n%#[2]v", expected, s.Data)
+		return fmt.Sprintf("Expected data:\n%v got\n%v\n\nExpected type structure:\n%#[1]v got\n%#[2]v", expected, s.Data)
 	}
+
+	return
 }
 
 func TestDataFromShortcode(t *testing.T) {