Merge pull request #172 from timstclair/gstruct-docs

Add gstruct docs
diff --git a/_includes/gomega_sidebar.html b/_includes/gomega_sidebar.html
index caefb04..61a3d99 100644
--- a/_includes/gomega_sidebar.html
+++ b/_includes/gomega_sidebar.html
@@ -252,4 +252,8 @@
         <a href="#gexec-testing-external-processes"><code>gexec</code>: Testing External Processes
         </a>
     </li>
+    <li>
+        <a href="#gstruct-testing-external-processes"><code>gstruct</code>: Testing Complex Data Types
+        </a>
+    </li>
 </ul>
diff --git a/index.md b/index.md
index ce19f67..30753b7 100644
--- a/index.md
+++ b/index.md
@@ -1582,4 +1582,130 @@
 
     Ω(session.Wait().Out.Contents()).Should(ContainSubstring("finished successfully"))
 
+## `gstruct`: Testing Complex Data Types
+
+`gstruct` simplifies testing large and nested structs and slices. It is used for building up complex matchers that apply different tests to each field or element.
+
+### Testing type `struct`
+
+`gstruct` provides the `FieldsMatcher` through the `MatchAllFields` and `MatchFields` functions for applying a separate matcher to each field of a struct:
+
+    actual := struct{
+        A int
+        B bool
+        C string
+    }{5, true, "foo"}
+    Expect(actual).To(MatchAllFields(Fields{
+        "A": BeNumerically("<", 10),
+        "B": BeTrue(),
+        "C": Equal("foo"),
+    })
+
+`MatchAllFields` requires that every field is matched, and each matcher is mapped to a field. To match a subset or superset of a struct, you should use the `MatchFields` function with the `IgnoreExtras` and `IgnoreMissing` options. `IgnoreExtras` will ignore fields that don't map to a matcher, e.g.
+
+    Expect(actual).To(MatchFields(IgnoreExtras, Fields{
+        "A": BeNumerically("<", 10),
+        "B": BeTrue(),
+        // Ignore lack of "C" in the matcher.
+    })
+
+`IgnoreMissing` will ignore matchers that don't map to a field, e.g.
+
+    Expect(actual).To(MatchFields(IgnoreExtras, Fields{
+        "A": BeNumerically("<", 10),
+        "B": BeTrue(),
+        "C": Equal("foo"),
+        "D": Equal("bar"), // Ignored, since actual.D does not exist.
+    })
+
+The options can be combined with the binary or: `IgnoreMissing|IgnoreExtras`.
+
+### Testing type slice
+
+`gstruct` provides the `ElementsMatcher` through the `MatchAllElements` and `MatchElements` function for applying a separate matcher to each element, identified by an `Identifier` function:
+
+    actual := []string{
+        "A: foo bar baz",
+        "B: once upon a time",
+        "C: the end",
+    }
+    id := func(element interface{}) {
+        return element.(string)[0]
+    }
+    Expect(actual).To(MatchAllElements(id, Elements{
+        "A": Not(BeZero()),
+        "B": MatchRegexp("[A-Z]: [a-z ]+"),
+        "C": ContainSubstring("end"),
+    })
+
+`MatchAllElements` requires that there is a 1:1 mapping from every element to every matcher. To match a subset or superset of elements, you should use the `MatchElements` function with the `IgnoreExtras` and `IgnoreMissing` options. `IgnoreExtras` will ignore elements that don't map to a matcher, e.g.
+
+    Expect(actual).To(MatchElements(IgnoreExtras, Fields{
+        "A": Not(BeZero()),
+        "B": MatchRegexp("[A-Z]: [a-z ]+"),
+        // Ignore lack of "C" in the matcher.
+    })
+
+`IgnoreMissing` will ignore matchers that don't map to an element, e.g.
+
+    Expect(actual).To(MatchFields(IgnoreExtras, Fields{
+        "A": Not(BeZero()),
+        "B": MatchRegexp("[A-Z]: [a-z ]+"),
+        "C": ContainSubstring("end"),
+        "D": Equal("bar"), // Ignored, since actual.D does not exist.
+    })
+
+The options can be combined with the binary or: `IgnoreMissing|IgnoreExtras`.
+
+### Testing pointer values
+
+`gstruct` provides the `PointTo` function to apply a matcher to the value pointed-to. It will fail if the pointer value is `nil`:
+
+    foo := 5
+    Expect(&foo).To(PointTo(Equal(5)))
+    var bar *int
+    Expect(bar).NotTo(PointTo(BeNil()))
+
+### Putting it all together: testing complex structures
+
+The `gsturct` matchers are intended to be composable, and can be combined to apply fuzzy-matching to large and deeply nested structures. The additional `Ignore()` and `Reject()` matchers are provided for ignoring (always succeed) fields and elements, or rejecting (always fail) fields and elements.
+
+Example:
+
+    coreID := func(element interface{}) string {
+        return strconv.Itoa(element.(CoreStats).Index)
+    }
+    Expect(actual).To(MatchAllFields(Fields{
+	    "Name":      Ignore(),
+		"StartTime": BeTemporally(">=", time.Now().Add(-100 * time.Hour)),
+		"CPU": PointTo(MatchAllFields(Fields{
+			"Time":                 BeTemporally(">=", time.Now().Add(-time.Hour)),
+			"UsageNanoCores":       BeNumerically("~", 1E9, 1E8),
+			"UsageCoreNanoSeconds": BeNumerically(">", 1E6),
+            "Cores": MatchElements(coreID, IgnoreExtras, Elements{
+                "0": MatchAllFields(Fields{
+                    Index: Ignore(),
+	                "UsageNanoCores":       BeNumerically("<", 1E9),
+	                "UsageCoreNanoSeconds": BeNumerically(">", 1E5),
+                }),
+                "1": MatchAllFields(Fields{
+                    Index: Ignore(),
+	                "UsageNanoCores":       BeNumerically("<", 1E9),
+	                "UsageCoreNanoSeconds": BeNumerically(">", 1E5),
+                }),
+            }),
+		})),
+		"Memory": PointTo(MatchAllFields(Fields{
+			"Time": BeTemporally(">=", time.Now().Add(-time.Hour)),
+			"AvailableBytes":  BeZero(),
+			"UsageBytes":      BeNumerically(">", 5E6),
+			"WorkingSetBytes": BeNumerically(">", 5E6),
+			"RSSBytes":        BeNumerically("<", 1E9),
+			"PageFaults":      BeNumerically("~", 1000, 100),
+			"MajorPageFaults": BeNumerically("~", 100, 50),
+		})),
+		"Rootfs":             m.Ignore(),
+		"Logs":               m.Ignore(),
+	}))
+
 ---