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(),
+ }))
+
---