Skip to content

Adds support for MatchXML #209

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ language: go
go:
- 1.6
- 1.7
- 1.8

install:
- go get -v ./...
Expand Down
14 changes: 14 additions & 0 deletions matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,20 @@ func MatchJSON(json interface{}) types.GomegaMatcher {
}
}

//MatchXML succeeds if actual is a string or stringer of XML that matches
//the expected XML. The XMLs are decoded and the resulting objects are compared via
//reflect.DeepEqual so things like key-ordering and whitespace shouldn't matter.
//The comparison of attribute values is not supported for go1.7 and before.
//For go1.7, the two following XMLs are equal:
//<person gender="female">
//
//<person gender="male">
func MatchXML(xml interface{}) types.GomegaMatcher {
return &matchers.MatchXMLMatcher{
XMLToMatch: xml,
}
}

//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.
Expand Down
58 changes: 58 additions & 0 deletions matchers/match_xml_matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package matchers

import (
"encoding/xml"
"fmt"
"reflect"

"github.com/onsi/gomega/format"
)

type MatchXMLMatcher struct {
XMLToMatch interface{}
}

func (matcher *MatchXMLMatcher) Match(actual interface{}) (success bool, err error) {
actualString, expectedString, err := matcher.formattedPrint(actual)
if err != nil {
return false, err
}

aval := &xmlNode{}
eval := &xmlNode{}

if err := xml.Unmarshal([]byte(actualString), aval); err != nil {
return false, fmt.Errorf("Actual '%s' should be valid XML, but it is not.\nUnderlying error:%s", actualString, err)
}
if err := xml.Unmarshal([]byte(expectedString), eval); err != nil {
return false, fmt.Errorf("Expected '%s' should be valid XML, but it is not.\nUnderlying error:%s", expectedString, err)
}

aval.Clean()
eval.Clean()

return reflect.DeepEqual(aval, eval), nil
}

func (matcher *MatchXMLMatcher) FailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.formattedPrint(actual)
return fmt.Sprintf("Expected\n%s\nto match XML of\n%s", actualString, expectedString)
}

func (matcher *MatchXMLMatcher) NegatedFailureMessage(actual interface{}) (message string) {
actualString, expectedString, _ := matcher.formattedPrint(actual)
return fmt.Sprintf("Expected\n%s\nnot to match XML of\n%s", actualString, expectedString)
}

func (matcher *MatchXMLMatcher) formattedPrint(actual interface{}) (actualString, expectedString string, err error) {
var ok bool
actualString, ok = toString(actual)
if !ok {
return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
}
expectedString, ok = toString(matcher.XMLToMatch)
if !ok {
return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.XMLToMatch, 1))
}
return actualString, expectedString, nil
}
22 changes: 22 additions & 0 deletions matchers/match_xml_matcher_go1.8_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// +build go1.8

package matchers_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("MatchXMLMatcher Go 1.8", func() {

var (
sample_09 = readFileContents("test_data/xml/sample_09.xml")
sample_10 = readFileContents("test_data/xml/sample_10.xml")
)

Context("When passed stringifiables", func() {
It("should succeed if the XML matches", func() {
Ω(sample_09).ShouldNot(MatchXML(sample_10)) // same structures with different attribute values
})
})
})
85 changes: 85 additions & 0 deletions matchers/match_xml_matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package matchers_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/matchers"
)

var _ = Describe("MatchXMLMatcher", func() {

var (
sample_01 = readFileContents("test_data/xml/sample_01.xml")
sample_02 = readFileContents("test_data/xml/sample_02.xml")
sample_03 = readFileContents("test_data/xml/sample_03.xml")
sample_04 = readFileContents("test_data/xml/sample_04.xml")
sample_05 = readFileContents("test_data/xml/sample_05.xml")
sample_06 = readFileContents("test_data/xml/sample_06.xml")
sample_07 = readFileContents("test_data/xml/sample_07.xml")
sample_08 = readFileContents("test_data/xml/sample_08.xml")
)

Context("When passed stringifiables", func() {
It("should succeed if the XML matches", func() {
Ω(sample_01).Should(MatchXML(sample_01)) // same XML
Ω(sample_01).Should(MatchXML(sample_02)) // same XML with blank lines
Ω(sample_01).Should(MatchXML(sample_03)) // same XML with different formatting
Ω(sample_01).ShouldNot(MatchXML(sample_04)) // same structures with different values
Ω(sample_01).ShouldNot(MatchXML(sample_05)) // different structures
Ω(sample_06).ShouldNot(MatchXML(sample_07)) // same xml names with different namespaces
Ω(sample_07).ShouldNot(MatchXML(sample_08)) // same structures with different values
})

It("should work with byte arrays", func() {
Ω([]byte(sample_01)).Should(MatchXML([]byte(sample_01)))
Ω([]byte(sample_01)).Should(MatchXML(sample_01))
Ω(sample_01).Should(MatchXML([]byte(sample_01)))
})
})

Context("when the expected is not valid XML", func() {
It("should error and explain why", func() {
success, err := (&MatchXMLMatcher{XMLToMatch: sample_01}).Match(`oops`)
Ω(success).Should(BeFalse())
Ω(err).Should(HaveOccurred())
Ω(err.Error()).Should(ContainSubstring("Actual 'oops' should be valid XML"))
})
})

Context("when the actual is not valid XML", func() {
It("should error and explain why", func() {
success, err := (&MatchXMLMatcher{XMLToMatch: `oops`}).Match(sample_01)
Ω(success).Should(BeFalse())
Ω(err).Should(HaveOccurred())
Ω(err.Error()).Should(ContainSubstring("Expected 'oops' should be valid XML"))
})
})

Context("when the expected is neither a string nor a stringer nor a byte array", func() {
It("should error", func() {
success, err := (&MatchXMLMatcher{XMLToMatch: 2}).Match(sample_01)
Ω(success).Should(BeFalse())
Ω(err).Should(HaveOccurred())
Ω(err.Error()).Should(ContainSubstring("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n <int>: 2"))

success, err = (&MatchXMLMatcher{XMLToMatch: nil}).Match(sample_01)
Ω(success).Should(BeFalse())
Ω(err).Should(HaveOccurred())
Ω(err.Error()).Should(ContainSubstring("MatchXMLMatcher 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 := (&MatchXMLMatcher{XMLToMatch: sample_01}).Match(2)
Ω(success).Should(BeFalse())
Ω(err).Should(HaveOccurred())
Ω(err.Error()).Should(ContainSubstring("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n <int>: 2"))

success, err = (&MatchXMLMatcher{XMLToMatch: sample_01}).Match(nil)
Ω(success).Should(BeFalse())
Ω(err).Should(HaveOccurred())
Ω(err.Error()).Should(ContainSubstring("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n <nil>: nil"))
})
})
})
20 changes: 20 additions & 0 deletions matchers/matcher_tests_suite_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package matchers_test

import (
"fmt"
"io/ioutil"
"os"
"testing"

. "github.com/onsi/ginkgo"
Expand Down Expand Up @@ -28,3 +31,20 @@ func Test(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Gomega Matchers")
}

func readFileContents(filePath string) []byte {
f := openFile(filePath)
b, err := ioutil.ReadAll(f)
if err != nil {
panic(fmt.Errorf("failed to read file contents: %v", err))
}
return b
}

func openFile(filePath string) *os.File {
f, err := os.Open(filePath)
if err != nil {
panic(fmt.Errorf("failed to open file: %v", err))
}
return f
}
6 changes: 6 additions & 0 deletions matchers/test_data/xml/sample_01.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
9 changes: 9 additions & 0 deletions matchers/test_data/xml/sample_02.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@


<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>

1 change: 1 addition & 0 deletions matchers/test_data/xml/sample_03.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<note> <to>Tove</to> <from>Jani</from> <heading>Reminder</heading> <body>Don't forget me this weekend!</body> </note>
6 changes: 6 additions & 0 deletions matchers/test_data/xml/sample_04.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<note>
<to>Tove</to>
<from>John</from>
<heading>Doe</heading>
<body>Don't forget me this weekend!</body>
</note>
Loading