Merge remote-tracking branch 'origin/pr/163'
diff --git a/.gitignore b/.gitignore
index 5514532..720c13c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
 .DS_Store
 *.test
 .
+.idea
+gomega.iml
diff --git a/.travis.yml b/.travis.yml
index 79780ec..8ed1e44 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,8 @@
 language: go
 go:
-  - 1.4
   - 1.5
+  - 1.6.2
+  - stable
 
 install:
   - go get -v ./...
diff --git a/format/format_test.go b/format/format_test.go
index fd926f5..59517fe 100644
--- a/format/format_test.go
+++ b/format/format_test.go
@@ -2,11 +2,11 @@
 
 import (
 	"fmt"
-	"strings"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 	. "github.com/onsi/gomega/format"
 	"github.com/onsi/gomega/types"
+	"strings"
 )
 
 //recursive struct
diff --git a/gbytes/say_matcher_test.go b/gbytes/say_matcher_test.go
index d0ddf1f..63fb3b3 100644
--- a/gbytes/say_matcher_test.go
+++ b/gbytes/say_matcher_test.go
@@ -1,8 +1,8 @@
 package gbytes_test
 
 import (
-	"time"
 	. "github.com/onsi/gomega/gbytes"
+	"time"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
diff --git a/gexec/build.go b/gexec/build.go
index 3e9bf9f..220c8c4 100644
--- a/gexec/build.go
+++ b/gexec/build.go
@@ -9,9 +9,13 @@
 	"path"
 	"path/filepath"
 	"runtime"
+	"sync"
 )
 
-var tmpDir string
+var (
+	mu     sync.Mutex
+	tmpDir string
+)
 
 /*
 Build uses go build to compile the package at packagePath.  The resulting binary is saved off in a temporary directory.
@@ -60,13 +64,18 @@
 gexec. In Ginkgo this is typically done in an AfterSuite callback.
 */
 func CleanupBuildArtifacts() {
+	mu.Lock()
+	defer mu.Unlock()
 	if tmpDir != "" {
 		os.RemoveAll(tmpDir)
+		tmpDir = ""
 	}
 }
 
 func temporaryDirectory() (string, error) {
 	var err error
+	mu.Lock()
+	defer mu.Unlock()
 	if tmpDir == "" {
 		tmpDir, err = ioutil.TempDir("", "gexec_artifacts")
 		if err != nil {
diff --git a/gexec/build_test.go b/gexec/build_test.go
new file mode 100644
index 0000000..7bd62fe
--- /dev/null
+++ b/gexec/build_test.go
@@ -0,0 +1,37 @@
+package gexec_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"github.com/onsi/gomega/gexec"
+)
+
+var _ = Describe(".Build", func() {
+	var packagePath = "./_fixture/firefly"
+
+	Context("when there have been previous calls to Build", func() {
+		BeforeEach(func() {
+			_, err := gexec.Build(packagePath)
+			Ω(err).ShouldNot(HaveOccurred())
+		})
+
+		It("compiles the specified package", func() {
+			compiledPath, err := gexec.Build(packagePath)
+			Ω(err).ShouldNot(HaveOccurred())
+			Ω(compiledPath).Should(BeAnExistingFile())
+		})
+
+		Context("and CleanupBuildArtifacts has been called", func() {
+			BeforeEach(func() {
+				gexec.CleanupBuildArtifacts()
+			})
+
+			It("compiles the specified package", func() {
+				var err error
+				fireflyPath, err = gexec.Build(packagePath)
+				Ω(err).ShouldNot(HaveOccurred())
+				Ω(fireflyPath).Should(BeAnExistingFile())
+			})
+		})
+	})
+})
diff --git a/gexec/exit_matcher_test.go b/gexec/exit_matcher_test.go
index 9f18e2d..79615dd 100644
--- a/gexec/exit_matcher_test.go
+++ b/gexec/exit_matcher_test.go
@@ -1,9 +1,9 @@
 package gexec_test
 
 import (
+	. "github.com/onsi/gomega/gexec"
 	"os/exec"
 	"time"
-	. "github.com/onsi/gomega/gexec"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
diff --git a/ghttp/test_server.go b/ghttp/test_server.go
index fde65be..093550d 100644
--- a/ghttp/test_server.go
+++ b/ghttp/test_server.go
@@ -202,7 +202,9 @@
 
 	server := s.HTTPTestServer
 	s.HTTPTestServer = nil
-	server.Close()
+	if server != nil {
+		server.Close()
+	}
 }
 
 //ServeHTTP() makes Server an http.Handler
@@ -223,7 +225,7 @@
 
 		//If the handler panics GHTTP will silently succeed.  This is bad™.
 		//To catch this case we need to fail the test if the handler has panicked.
-		//However, if the handler is panicking because Ginkgo's causing it to panic (i.e. an asswertion failed)
+		//However, if the handler is panicking because Ginkgo's causing it to panic (i.e. an assertion failed)
 		//then we shouldn't double-report the error as this will confuse people.
 
 		//So: step 1, if this is a Ginkgo panic - do nothing, Ginkgo's aware of the failure
@@ -349,6 +351,17 @@
 	return s.requestHandlers[index]
 }
 
+func (s *Server) Reset() {
+	s.writeLock.Lock()
+	defer s.writeLock.Unlock()
+
+	s.HTTPTestServer.CloseClientConnections()
+	s.calls = 0
+	s.receivedRequests = nil
+	s.requestHandlers = nil
+	s.routedHandlers = nil
+}
+
 //WrapHandler combines the passed in handler with the handler registered at the passed in index.
 //This is useful, for example, when a server has been set up in a shared context but must be tweaked
 //for a particular test.
diff --git a/ghttp/test_server_test.go b/ghttp/test_server_test.go
index 292b51d..88b3246 100644
--- a/ghttp/test_server_test.go
+++ b/ghttp/test_server_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"bytes"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -31,29 +32,56 @@
 		s.Close()
 	})
 
+	Describe("Resetting the server", func() {
+		BeforeEach(func() {
+			s.RouteToHandler("GET", "/", func(w http.ResponseWriter, req *http.Request) {})
+			s.AppendHandlers(func(w http.ResponseWriter, req *http.Request) {})
+			http.Get(s.URL() + "/")
+
+			Ω(s.ReceivedRequests()).Should(HaveLen(1))
+		})
+
+		It("clears all handlers and call counts", func() {
+			s.Reset()
+			Ω(s.ReceivedRequests()).Should(HaveLen(0))
+			Ω(func() { s.GetHandler(0) }).Should(Panic())
+		})
+	})
+
 	Describe("closing client connections", func() {
 		It("closes", func() {
-			s.AppendHandlers(
+			s.RouteToHandler("GET", "/",
 				func(w http.ResponseWriter, req *http.Request) {
-					w.Write([]byte("hello"))
-				},
-				func(w http.ResponseWriter, req *http.Request) {
-					s.CloseClientConnections()
+					io.WriteString(w, req.RemoteAddr)
 				},
 			)
-
-			resp, err := http.Get(s.URL())
+			client := http.Client{Transport: &http.Transport{DisableKeepAlives: true}}
+			resp, err := client.Get(s.URL())
 			Ω(err).ShouldNot(HaveOccurred())
 			Ω(resp.StatusCode).Should(Equal(200))
 
 			body, err := ioutil.ReadAll(resp.Body)
 			resp.Body.Close()
 			Ω(err).ShouldNot(HaveOccurred())
-			Ω(body).Should(Equal([]byte("hello")))
 
-			resp, err = http.Get(s.URL())
-			Ω(err).Should(HaveOccurred())
-			Ω(resp).Should(BeNil())
+			s.CloseClientConnections()
+
+			resp, err = client.Get(s.URL())
+			Ω(err).ShouldNot(HaveOccurred())
+			Ω(resp.StatusCode).Should(Equal(200))
+
+			body2, err := ioutil.ReadAll(resp.Body)
+			resp.Body.Close()
+			Ω(err).ShouldNot(HaveOccurred())
+
+			Ω(body2).ShouldNot(Equal(body))
+		})
+	})
+
+	Describe("closing server mulitple times", func() {
+		It("should not fail", func() {
+			s.Close()
+			Ω(s.Close).ShouldNot(Panic())
 		})
 	})
 
diff --git a/matchers.go b/matchers.go
index b6110c4..0c30aa1 100644
--- a/matchers.go
+++ b/matchers.go
@@ -26,6 +26,15 @@
 	}
 }
 
+//BeIdenticalTo uses the == operator to compare actual with expected.
+//BeIdenticalTo is strict about types when performing comparisons.
+//It is an error for both actual and expected to be nil.  Use BeNil() instead.
+func BeIdenticalTo(expected interface{}) types.GomegaMatcher {
+	return &matchers.BeIdenticalToMatcher{
+		Expected: expected,
+	}
+}
+
 //BeNil succeeds if actual is nil
 func BeNil() types.GomegaMatcher {
 	return &matchers.BeNilMatcher{}
@@ -205,6 +214,15 @@
 	}
 }
 
+//MatchYAML succeeds if actual is a string or stringer of YAML that matches
+//the expected YAML.  The YAML's are decoded and the resulting objects are compared via
+//reflect.DeepEqual so things like key-ordering and whitespace shouldn't matter.
+func MatchYAML(yaml interface{}) types.GomegaMatcher {
+	return &matchers.MatchYAMLMatcher{
+		YAMLToMatch: yaml,
+	}
+}
+
 //BeEmpty succeeds if actual is empty.  Actual must be of type string, array, map, chan, or slice.
 func BeEmpty() types.GomegaMatcher {
 	return &matchers.BeEmptyMatcher{}
@@ -217,6 +235,13 @@
 	}
 }
 
+//HaveCap succeeds if actual has the passed-in capacity.  Actual must be of type array, chan, or slice.
+func HaveCap(count int) types.GomegaMatcher {
+	return &matchers.HaveCapMatcher{
+		Count: count,
+	}
+}
+
 //BeZero succeeds if actual is the zero value for its type or if actual is nil.
 func BeZero() types.GomegaMatcher {
 	return &matchers.BeZeroMatcher{}
diff --git a/matchers/be_identical_to.go b/matchers/be_identical_to.go
new file mode 100644
index 0000000..fdcda4d
--- /dev/null
+++ b/matchers/be_identical_to.go
@@ -0,0 +1,37 @@
+package matchers
+
+import (
+	"fmt"
+	"runtime"
+
+	"github.com/onsi/gomega/format"
+)
+
+type BeIdenticalToMatcher struct {
+	Expected interface{}
+}
+
+func (matcher *BeIdenticalToMatcher) Match(actual interface{}) (success bool, matchErr error) {
+	if actual == nil && matcher.Expected == nil {
+		return false, fmt.Errorf("Refusing to compare <nil> to <nil>.\nBe explicit and use BeNil() instead.  This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.")
+	}
+
+	defer func() {
+		if r := recover(); r != nil {
+			if _, ok := r.(runtime.Error); ok {
+				success = false
+				matchErr = nil
+			}
+		}
+	}()
+
+	return actual == matcher.Expected, nil
+}
+
+func (matcher *BeIdenticalToMatcher) FailureMessage(actual interface{}) string {
+	return format.Message(actual, "to be identical to", matcher.Expected)
+}
+
+func (matcher *BeIdenticalToMatcher) NegatedFailureMessage(actual interface{}) string {
+	return format.Message(actual, "not to be identical to", matcher.Expected)
+}
diff --git a/matchers/be_identical_to_test.go b/matchers/be_identical_to_test.go
new file mode 100644
index 0000000..8b90a1a
--- /dev/null
+++ b/matchers/be_identical_to_test.go
@@ -0,0 +1,61 @@
+package matchers_test
+
+import (
+	"errors"
+
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	. "github.com/onsi/gomega/matchers"
+)
+
+var _ = Describe("BeIdenticalTo", func() {
+	Context("when asserting that nil equals nil", func() {
+		It("should error", func() {
+			success, err := (&BeIdenticalToMatcher{Expected: nil}).Match(nil)
+
+			Ω(success).Should(BeFalse())
+			Ω(err).Should(HaveOccurred())
+		})
+	})
+
+	It("should treat the same pointer to a struct as identical", func() {
+		mySpecialStruct := myCustomType{}
+		Ω(&mySpecialStruct).Should(BeIdenticalTo(&mySpecialStruct))
+		Ω(&myCustomType{}).ShouldNot(BeIdenticalTo(&mySpecialStruct))
+	})
+
+	It("should be strict about types", func() {
+		Ω(5).ShouldNot(BeIdenticalTo("5"))
+		Ω(5).ShouldNot(BeIdenticalTo(5.0))
+		Ω(5).ShouldNot(BeIdenticalTo(3))
+	})
+
+	It("should treat primtives as identical", func() {
+		Ω("5").Should(BeIdenticalTo("5"))
+		Ω("5").ShouldNot(BeIdenticalTo("55"))
+
+		Ω(5.55).Should(BeIdenticalTo(5.55))
+		Ω(5.55).ShouldNot(BeIdenticalTo(6.66))
+
+		Ω(5).Should(BeIdenticalTo(5))
+		Ω(5).ShouldNot(BeIdenticalTo(55))
+	})
+
+	It("should treat the same pointers to a slice as identical", func() {
+		mySlice := []int{1, 2}
+		Ω(&mySlice).Should(BeIdenticalTo(&mySlice))
+		Ω(&mySlice).ShouldNot(BeIdenticalTo(&[]int{1, 2}))
+	})
+
+	It("should treat the same pointers to a map as identical", func() {
+		myMap := map[string]string{"a": "b", "c": "d"}
+		Ω(&myMap).Should(BeIdenticalTo(&myMap))
+		Ω(myMap).ShouldNot(BeIdenticalTo(map[string]string{"a": "b", "c": "d"}))
+	})
+
+	It("should treat the same pointers to an error as identical", func() {
+		myError := errors.New("foo")
+		Ω(&myError).Should(BeIdenticalTo(&myError))
+		Ω(errors.New("foo")).ShouldNot(BeIdenticalTo(errors.New("bar")))
+	})
+})
diff --git a/matchers/be_sent_matcher_test.go b/matchers/be_sent_matcher_test.go
index 381c2b4..205d71f 100644
--- a/matchers/be_sent_matcher_test.go
+++ b/matchers/be_sent_matcher_test.go
@@ -1,8 +1,8 @@
 package matchers_test
 
 import (
-	"time"
 	. "github.com/onsi/gomega/matchers"
+	"time"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
diff --git a/matchers/consist_of_test.go b/matchers/consist_of_test.go
index 0b230e3..dcd1afe 100644
--- a/matchers/consist_of_test.go
+++ b/matchers/consist_of_test.go
@@ -54,10 +54,10 @@
 			Ω([]string{"foo", "bar", "baz"}).ShouldNot(ConsistOf("foo", MatchRegexp("^ba"), MatchRegexp("turducken")))
 		})
 
-        It("should not depend on the order of the matchers", func() {
+		It("should not depend on the order of the matchers", func() {
 			Ω([][]int{[]int{1, 2}, []int{2}}).Should(ConsistOf(ContainElement(1), ContainElement(2)))
 			Ω([][]int{[]int{1, 2}, []int{2}}).Should(ConsistOf(ContainElement(2), ContainElement(1)))
-        })
+		})
 
 		Context("when a matcher errors", func() {
 			It("should soldier on", func() {
diff --git a/matchers/have_cap_matcher.go b/matchers/have_cap_matcher.go
new file mode 100644
index 0000000..7ace93d
--- /dev/null
+++ b/matchers/have_cap_matcher.go
@@ -0,0 +1,28 @@
+package matchers
+
+import (
+	"fmt"
+
+	"github.com/onsi/gomega/format"
+)
+
+type HaveCapMatcher struct {
+	Count int
+}
+
+func (matcher *HaveCapMatcher) Match(actual interface{}) (success bool, err error) {
+	length, ok := capOf(actual)
+	if !ok {
+		return false, fmt.Errorf("HaveCap matcher expects a array/channel/slice.  Got:\n%s", format.Object(actual, 1))
+	}
+
+	return length == matcher.Count, nil
+}
+
+func (matcher *HaveCapMatcher) FailureMessage(actual interface{}) (message string) {
+	return fmt.Sprintf("Expected\n%s\nto have capacity %d", format.Object(actual, 1), matcher.Count)
+}
+
+func (matcher *HaveCapMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+	return fmt.Sprintf("Expected\n%s\nnot to have capacity %d", format.Object(actual, 1), matcher.Count)
+}
diff --git a/matchers/have_cap_matcher_test.go b/matchers/have_cap_matcher_test.go
new file mode 100644
index 0000000..a92a177
--- /dev/null
+++ b/matchers/have_cap_matcher_test.go
@@ -0,0 +1,50 @@
+package matchers_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	. "github.com/onsi/gomega/matchers"
+)
+
+var _ = Describe("HaveCap", func() {
+	Context("when passed a supported type", func() {
+		It("should do the right thing", func() {
+			Ω([0]int{}).Should(HaveCap(0))
+			Ω([2]int{1}).Should(HaveCap(2))
+
+			Ω([]int{}).Should(HaveCap(0))
+			Ω([]int{1, 2, 3, 4, 5}[:2]).Should(HaveCap(5))
+			Ω(make([]int, 0, 5)).Should(HaveCap(5))
+
+			c := make(chan bool, 3)
+			Ω(c).Should(HaveCap(3))
+			c <- true
+			c <- true
+			Ω(c).Should(HaveCap(3))
+
+			Ω(make(chan bool)).Should(HaveCap(0))
+		})
+	})
+
+	Context("when passed a correctly typed nil", func() {
+		It("should operate succesfully on the passed in value", func() {
+			var nilSlice []int
+			Ω(nilSlice).Should(HaveCap(0))
+
+			var nilChan chan int
+			Ω(nilChan).Should(HaveCap(0))
+		})
+	})
+
+	Context("when passed an unsupported type", func() {
+		It("should error", func() {
+			success, err := (&HaveCapMatcher{Count: 0}).Match(0)
+			Ω(success).Should(BeFalse())
+			Ω(err).Should(HaveOccurred())
+
+			success, err = (&HaveCapMatcher{Count: 0}).Match(nil)
+			Ω(success).Should(BeFalse())
+			Ω(err).Should(HaveOccurred())
+		})
+	})
+})
diff --git a/matchers/have_occurred_matcher.go b/matchers/have_occurred_matcher.go
index cdc1d54..ebdd717 100644
--- a/matchers/have_occurred_matcher.go
+++ b/matchers/have_occurred_matcher.go
@@ -10,15 +10,18 @@
 }
 
 func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) {
-	if isNil(actual) {
+	// is purely nil?
+	if actual == nil {
 		return false, nil
 	}
 
-	if isError(actual) {
-		return true, nil
+	// must be an 'error' type
+	if !isError(actual) {
+		return false, fmt.Errorf("Expected an error-type.  Got:\n%s", format.Object(actual, 1))
 	}
 
-	return false, fmt.Errorf("Expected an error.  Got:\n%s", format.Object(actual, 1))
+	// must be non-nil (or a pointer to a non-nil)
+	return !isNil(actual), nil
 }
 
 func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) {
diff --git a/matchers/have_occurred_matcher_test.go b/matchers/have_occurred_matcher_test.go
index 0fc35a9..009e23e 100644
--- a/matchers/have_occurred_matcher_test.go
+++ b/matchers/have_occurred_matcher_test.go
@@ -34,6 +34,18 @@
 		Ω(err).Should(HaveOccurred())
 	})
 
+	It("doesn't support non-error type", func() {
+		success, err := (&HaveOccurredMatcher{}).Match(AnyType{})
+		Ω(success).Should(BeFalse())
+		Ω(err).Should(MatchError("Expected an error-type.  Got:\n    <matchers_test.AnyType>: {}"))
+	})
+
+	It("doesn't support non-error pointer type", func() {
+		success, err := (&HaveOccurredMatcher{}).Match(&AnyType{})
+		Ω(success).Should(BeFalse())
+		Ω(err).Should(MatchError(MatchRegexp(`Expected an error-type.  Got:\n    <*matchers_test.AnyType | 0x[[:xdigit:]]+>: {}`)))
+	})
+
 	It("should succeed with pointer types that conform to error interface", func() {
 		err := &CustomErr{"ohai"}
 		Ω(err).Should(HaveOccurred())
diff --git a/matchers/have_suffix_matcher.go b/matchers/have_suffix_matcher.go
index eb1b284..afc78fc 100644
--- a/matchers/have_suffix_matcher.go
+++ b/matchers/have_suffix_matcher.go
@@ -16,7 +16,7 @@
 		return false, fmt.Errorf("HaveSuffix matcher requires a string or stringer.  Got:\n%s", format.Object(actual, 1))
 	}
 	suffix := matcher.suffix()
-	return len(actualString) >= len(suffix) && actualString[len(actualString) - len(suffix):] == suffix, nil
+	return len(actualString) >= len(suffix) && actualString[len(actualString)-len(suffix):] == suffix, nil
 }
 
 func (matcher *HaveSuffixMatcher) suffix() string {
diff --git a/matchers/match_json_matcher.go b/matchers/match_json_matcher.go
index efc5e15..e61978a 100644
--- a/matchers/match_json_matcher.go
+++ b/matchers/match_json_matcher.go
@@ -4,8 +4,9 @@
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"github.com/onsi/gomega/format"
 	"reflect"
+
+	"github.com/onsi/gomega/format"
 )
 
 type MatchJSONMatcher struct {
@@ -39,22 +40,24 @@
 }
 
 func (matcher *MatchJSONMatcher) prettyPrint(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
-	actualString, aok := toString(actual)
-	expectedString, eok := toString(matcher.JSONToMatch)
-
-	if !(aok && eok) {
-		return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string or stringer.  Got:\n%s", format.Object(actual, 1))
+	actualString, ok := toString(actual)
+	if !ok {
+		return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte.  Got actual:\n%s", format.Object(actual, 1))
+	}
+	expectedString, ok := toString(matcher.JSONToMatch)
+	if !ok {
+		return "", "", fmt.Errorf("MatchJSONMatcher matcher requires a string, stringer, or []byte.  Got expected:\n%s", format.Object(matcher.JSONToMatch, 1))
 	}
 
 	abuf := new(bytes.Buffer)
 	ebuf := new(bytes.Buffer)
 
 	if err := json.Indent(abuf, []byte(actualString), "", "  "); err != nil {
-		return "", "", err
+		return "", "", fmt.Errorf("Actual '%s' should be valid JSON, but it is not.\nUnderlying error:%s", actualString, err)
 	}
 
 	if err := json.Indent(ebuf, []byte(expectedString), "", "  "); err != nil {
-		return "", "", err
+		return "", "", fmt.Errorf("Expected '%s' should be valid JSON, but it is not.\nUnderlying error:%s", expectedString, err)
 	}
 
 	return abuf.String(), ebuf.String(), nil
diff --git a/matchers/match_json_matcher_test.go b/matchers/match_json_matcher_test.go
index c1924ba..755c4ad 100644
--- a/matchers/match_json_matcher_test.go
+++ b/matchers/match_json_matcher_test.go
@@ -25,35 +25,49 @@
 		})
 	})
 
-	Context("when either side is not valid JSON", func() {
-		It("should error", func() {
-			success, err := (&MatchJSONMatcher{JSONToMatch: `oops`}).Match(`{}`)
+	Context("when the expected is not valid JSON", func() {
+		It("should error and explain why", func() {
+			success, err := (&MatchJSONMatcher{JSONToMatch: `{}`}).Match(`oops`)
 			Ω(success).Should(BeFalse())
 			Ω(err).Should(HaveOccurred())
-
-			success, err = (&MatchJSONMatcher{JSONToMatch: `{}`}).Match(`oops`)
-			Ω(success).Should(BeFalse())
-			Ω(err).Should(HaveOccurred())
+			Ω(err.Error()).Should(ContainSubstring("Actual 'oops' should be valid JSON"))
 		})
 	})
 
-	Context("when either side is neither a string nor a stringer", func() {
-		It("should error", func() {
-			success, err := (&MatchJSONMatcher{JSONToMatch: "{}"}).Match(2)
+	Context("when the actual is not valid JSON", func() {
+		It("should error and explain why", func() {
+			success, err := (&MatchJSONMatcher{JSONToMatch: `oops`}).Match(`{}`)
 			Ω(success).Should(BeFalse())
 			Ω(err).Should(HaveOccurred())
+			Ω(err.Error()).Should(ContainSubstring("Expected 'oops' should be valid JSON"))
+		})
+	})
 
-			success, err = (&MatchJSONMatcher{JSONToMatch: 2}).Match("{}")
+	Context("when the expected is neither a string nor a stringer nor a byte array", func() {
+		It("should error", func() {
+			success, err := (&MatchJSONMatcher{JSONToMatch: 2}).Match("{}")
 			Ω(success).Should(BeFalse())
 			Ω(err).Should(HaveOccurred())
+			Ω(err.Error()).Should(ContainSubstring("MatchJSONMatcher matcher requires a string, stringer, or []byte.  Got expected:\n    <int>: 2"))
 
 			success, err = (&MatchJSONMatcher{JSONToMatch: nil}).Match("{}")
 			Ω(success).Should(BeFalse())
 			Ω(err).Should(HaveOccurred())
+			Ω(err.Error()).Should(ContainSubstring("MatchJSONMatcher matcher requires a string, stringer, or []byte.  Got expected:\n    <nil>: nil"))
+		})
+	})
 
-			success, err = (&MatchJSONMatcher{JSONToMatch: 2}).Match(nil)
+	Context("when the actual is neither a string nor a stringer nor a byte array", func() {
+		It("should error", func() {
+			success, err := (&MatchJSONMatcher{JSONToMatch: "{}"}).Match(2)
 			Ω(success).Should(BeFalse())
 			Ω(err).Should(HaveOccurred())
+			Ω(err.Error()).Should(ContainSubstring("MatchJSONMatcher matcher requires a string, stringer, or []byte.  Got actual:\n    <int>: 2"))
+
+			success, err = (&MatchJSONMatcher{JSONToMatch: "{}"}).Match(nil)
+			Ω(success).Should(BeFalse())
+			Ω(err).Should(HaveOccurred())
+			Ω(err.Error()).Should(ContainSubstring("MatchJSONMatcher matcher requires a string, stringer, or []byte.  Got actual:\n    <nil>: nil"))
 		})
 	})
 })
diff --git a/matchers/match_yaml_matcher.go b/matchers/match_yaml_matcher.go
new file mode 100644
index 0000000..69fb51a
--- /dev/null
+++ b/matchers/match_yaml_matcher.go
@@ -0,0 +1,74 @@
+package matchers
+
+import (
+	"fmt"
+	"reflect"
+	"strings"
+
+	"github.com/onsi/gomega/format"
+	"gopkg.in/yaml.v2"
+)
+
+type MatchYAMLMatcher struct {
+	YAMLToMatch interface{}
+}
+
+func (matcher *MatchYAMLMatcher) Match(actual interface{}) (success bool, err error) {
+	actualString, expectedString, err := matcher.toStrings(actual)
+	if err != nil {
+		return false, err
+	}
+
+	var aval interface{}
+	var eval interface{}
+
+	if err := yaml.Unmarshal([]byte(actualString), &aval); err != nil {
+		return false, fmt.Errorf("Actual '%s' should be valid YAML, but it is not.\nUnderlying error:%s", actualString, err)
+	}
+	if err := yaml.Unmarshal([]byte(expectedString), &eval); err != nil {
+		return false, fmt.Errorf("Expected '%s' should be valid YAML, but it is not.\nUnderlying error:%s", expectedString, err)
+	}
+
+	return reflect.DeepEqual(aval, eval), nil
+}
+
+func (matcher *MatchYAMLMatcher) FailureMessage(actual interface{}) (message string) {
+	actualString, expectedString, _ := matcher.toNormalisedStrings(actual)
+	return format.Message(actualString, "to match YAML of", expectedString)
+}
+
+func (matcher *MatchYAMLMatcher) NegatedFailureMessage(actual interface{}) (message string) {
+	actualString, expectedString, _ := matcher.toNormalisedStrings(actual)
+	return format.Message(actualString, "not to match YAML of", expectedString)
+}
+
+func (matcher *MatchYAMLMatcher) toNormalisedStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
+	actualString, expectedString, err := matcher.toStrings(actual)
+	return normalise(actualString), normalise(expectedString), err
+}
+
+func normalise(input string) string {
+	var val interface{}
+	err := yaml.Unmarshal([]byte(input), &val)
+	if err != nil {
+		panic(err) // guarded by Match
+	}
+	output, err := yaml.Marshal(val)
+	if err != nil {
+		panic(err) // guarded by Unmarshal
+	}
+	return strings.TrimSpace(string(output))
+}
+
+func (matcher *MatchYAMLMatcher) toStrings(actual interface{}) (actualFormatted, expectedFormatted string, err error) {
+	actualString, ok := toString(actual)
+	if !ok {
+		return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte.  Got actual:\n%s", format.Object(actual, 1))
+	}
+	expectedString, ok := toString(matcher.YAMLToMatch)
+	if !ok {
+		return "", "", fmt.Errorf("MatchYAMLMatcher matcher requires a string, stringer, or []byte.  Got expected:\n%s", format.Object(matcher.YAMLToMatch, 1))
+	}
+
+	return actualString, expectedString, nil
+}
diff --git a/matchers/match_yaml_matcher_test.go b/matchers/match_yaml_matcher_test.go
new file mode 100644
index 0000000..8e63de1
--- /dev/null
+++ b/matchers/match_yaml_matcher_test.go
@@ -0,0 +1,94 @@
+package matchers_test
+
+import (
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+
+	. "github.com/onsi/gomega/matchers"
+)
+
+var _ = Describe("MatchYAMLMatcher", func() {
+	Context("When passed stringifiables", func() {
+		It("should succeed if the YAML matches", func() {
+			Expect("---").Should(MatchYAML(""))
+			Expect("a: 1").Should(MatchYAML(`{"a":1}`))
+			Expect("a: 1\nb: 2").Should(MatchYAML(`{"b":2, "a":1}`))
+		})
+
+		It("should explain if the YAML does not match when it should", func() {
+			message := (&MatchYAMLMatcher{YAMLToMatch: "a: 1"}).FailureMessage("b: 2")
+			Expect(message).To(MatchRegexp(`Expected\s+<string>: b: 2\s+to match YAML of\s+<string>: a: 1`))
+		})
+
+		It("should normalise the expected and actual when explaining if the YAML does not match when it should", func() {
+			message := (&MatchYAMLMatcher{YAMLToMatch: "a: 'one'"}).FailureMessage("{b: two}")
+			Expect(message).To(MatchRegexp(`Expected\s+<string>: b: two\s+to match YAML of\s+<string>: a: one`))
+		})
+
+		It("should explain if the YAML matches when it should not", func() {
+			message := (&MatchYAMLMatcher{YAMLToMatch: "a: 1"}).NegatedFailureMessage("a: 1")
+			Expect(message).To(MatchRegexp(`Expected\s+<string>: a: 1\s+not to match YAML of\s+<string>: a: 1`))
+		})
+
+		It("should normalise the expected and actual when explaining if the YAML matches when it should not", func() {
+			message := (&MatchYAMLMatcher{YAMLToMatch: "a: 'one'"}).NegatedFailureMessage("{a: one}")
+			Expect(message).To(MatchRegexp(`Expected\s+<string>: a: one\s+not to match YAML of\s+<string>: a: one`))
+		})
+
+		It("should fail if the YAML does not match", func() {
+			Expect("a: 1").ShouldNot(MatchYAML(`{"b":2, "a":1}`))
+		})
+
+		It("should work with byte arrays", func() {
+			Expect([]byte("a: 1")).Should(MatchYAML([]byte("a: 1")))
+			Expect("a: 1").Should(MatchYAML([]byte("a: 1")))
+			Expect([]byte("a: 1")).Should(MatchYAML("a: 1"))
+		})
+	})
+
+	Context("when the expected is not valid YAML", func() {
+		It("should error and explain why", func() {
+			success, err := (&MatchYAMLMatcher{YAMLToMatch: ""}).Match("good:\nbad")
+			Expect(success).Should(BeFalse())
+			Expect(err).Should(HaveOccurred())
+			Expect(err.Error()).Should(ContainSubstring("Actual 'good:\nbad' should be valid YAML"))
+		})
+	})
+
+	Context("when the actual is not valid YAML", func() {
+		It("should error and explain why", func() {
+			success, err := (&MatchYAMLMatcher{YAMLToMatch: "good:\nbad"}).Match("")
+			Expect(success).Should(BeFalse())
+			Expect(err).Should(HaveOccurred())
+			Expect(err.Error()).Should(ContainSubstring("Expected 'good:\nbad' should be valid YAML"))
+		})
+	})
+
+	Context("when the expected is neither a string nor a stringer nor a byte array", func() {
+		It("should error", func() {
+			success, err := (&MatchYAMLMatcher{YAMLToMatch: 2}).Match("")
+			Expect(success).Should(BeFalse())
+			Expect(err).Should(HaveOccurred())
+			Expect(err.Error()).Should(ContainSubstring("MatchYAMLMatcher matcher requires a string, stringer, or []byte.  Got expected:\n    <int>: 2"))
+
+			success, err = (&MatchYAMLMatcher{YAMLToMatch: nil}).Match("")
+			Expect(success).Should(BeFalse())
+			Expect(err).Should(HaveOccurred())
+			Expect(err.Error()).Should(ContainSubstring("MatchYAMLMatcher matcher requires a string, stringer, or []byte.  Got expected:\n    <nil>: nil"))
+		})
+	})
+
+	Context("when the actual is neither a string nor a stringer nor a byte array", func() {
+		It("should error", func() {
+			success, err := (&MatchYAMLMatcher{YAMLToMatch: ""}).Match(2)
+			Expect(success).Should(BeFalse())
+			Expect(err).Should(HaveOccurred())
+			Expect(err.Error()).Should(ContainSubstring("MatchYAMLMatcher matcher requires a string, stringer, or []byte.  Got actual:\n    <int>: 2"))
+
+			success, err = (&MatchYAMLMatcher{YAMLToMatch: ""}).Match(nil)
+			Expect(success).Should(BeFalse())
+			Expect(err).Should(HaveOccurred())
+			Expect(err.Error()).Should(ContainSubstring("MatchYAMLMatcher matcher requires a string, stringer, or []byte.  Got actual:\n    <nil>: nil"))
+		})
+	})
+})
diff --git a/matchers/succeed_matcher.go b/matchers/succeed_matcher.go
index f7dd853..721ed55 100644
--- a/matchers/succeed_matcher.go
+++ b/matchers/succeed_matcher.go
@@ -10,15 +10,18 @@
 }
 
 func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) {
+	// is purely nil?
 	if actual == nil {
 		return true, nil
 	}
 
-	if isError(actual) {
-		return false, nil
+	// must be an 'error' type
+	if !isError(actual) {
+		return false, fmt.Errorf("Expected an error-type.  Got:\n%s", format.Object(actual, 1))
 	}
 
-	return false, fmt.Errorf("Expected an error-type.  Got:\n%s", format.Object(actual, 1))
+	// must be nil (or a pointer to a nil)
+	return isNil(actual), nil
 }
 
 func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) {
diff --git a/matchers/succeed_matcher_test.go b/matchers/succeed_matcher_test.go
index 3562e70..6b62c8b 100644
--- a/matchers/succeed_matcher_test.go
+++ b/matchers/succeed_matcher_test.go
@@ -34,6 +34,29 @@
 	It("should not if passed a non-error", func() {
 		success, err := (&SucceedMatcher{}).Match(Invalid())
 		Ω(success).Should(BeFalse())
-		Ω(err).Should(HaveOccurred())
+		Ω(err).Should(MatchError("Expected an error-type.  Got:\n    <*matchers_test.AnyType | 0x0>: nil"))
 	})
+
+	It("doesn't support non-error type", func() {
+		success, err := (&SucceedMatcher{}).Match(AnyType{})
+		Ω(success).Should(BeFalse())
+		Ω(err).Should(MatchError("Expected an error-type.  Got:\n    <matchers_test.AnyType>: {}"))
+	})
+
+	It("doesn't support non-error pointer type", func() {
+		success, err := (&SucceedMatcher{}).Match(&AnyType{})
+		Ω(success).Should(BeFalse())
+		Ω(err).Should(MatchError(MatchRegexp(`Expected an error-type.  Got:\n    <*matchers_test.AnyType | 0x[[:xdigit:]]+>: {}`)))
+	})
+
+	It("should not succeed with pointer types that conform to error interface", func() {
+		err := &CustomErr{"ohai"}
+		Ω(err).ShouldNot(Succeed())
+	})
+
+	It("should succeed with nil pointers to types that conform to error interface", func() {
+		var err *CustomErr = nil
+		Ω(err).Should(Succeed())
+	})
+
 })
diff --git a/matchers/support/goraph/util/util.go b/matchers/support/goraph/util/util.go
index a24cd27..d76a1ee 100644
--- a/matchers/support/goraph/util/util.go
+++ b/matchers/support/goraph/util/util.go
@@ -3,5 +3,5 @@
 import "math"
 
 func Odd(n int) bool {
-    return math.Mod(float64(n), 2.0) == 1.0
-} 
+	return math.Mod(float64(n), 2.0) == 1.0
+}
diff --git a/matchers/type_support.go b/matchers/type_support.go
index ef9b448..04020f0 100644
--- a/matchers/type_support.go
+++ b/matchers/type_support.go
@@ -150,6 +150,17 @@
 		return 0, false
 	}
 }
+func capOf(a interface{}) (int, bool) {
+	if a == nil {
+		return 0, false
+	}
+	switch reflect.TypeOf(a).Kind() {
+	case reflect.Array, reflect.Chan, reflect.Slice:
+		return reflect.ValueOf(a).Cap(), true
+	default:
+		return 0, false
+	}
+}
 
 func isNil(a interface{}) bool {
 	if a == nil {