Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/vendor/github.com/onsi/gomega/matchers/match_xml_matcher.go
2880 views
1
package matchers
2
3
import (
4
"bytes"
5
"encoding/xml"
6
"errors"
7
"fmt"
8
"io"
9
"reflect"
10
"sort"
11
"strings"
12
13
"github.com/onsi/gomega/format"
14
"golang.org/x/net/html/charset"
15
)
16
17
type MatchXMLMatcher struct {
18
XMLToMatch any
19
}
20
21
func (matcher *MatchXMLMatcher) Match(actual any) (success bool, err error) {
22
actualString, expectedString, err := matcher.formattedPrint(actual)
23
if err != nil {
24
return false, err
25
}
26
27
aval, err := parseXmlContent(actualString)
28
if err != nil {
29
return false, fmt.Errorf("Actual '%s' should be valid XML, but it is not.\nUnderlying error:%s", actualString, err)
30
}
31
32
eval, err := parseXmlContent(expectedString)
33
if err != nil {
34
return false, fmt.Errorf("Expected '%s' should be valid XML, but it is not.\nUnderlying error:%s", expectedString, err)
35
}
36
37
return reflect.DeepEqual(aval, eval), nil
38
}
39
40
func (matcher *MatchXMLMatcher) FailureMessage(actual any) (message string) {
41
actualString, expectedString, _ := matcher.formattedPrint(actual)
42
return fmt.Sprintf("Expected\n%s\nto match XML of\n%s", actualString, expectedString)
43
}
44
45
func (matcher *MatchXMLMatcher) NegatedFailureMessage(actual any) (message string) {
46
actualString, expectedString, _ := matcher.formattedPrint(actual)
47
return fmt.Sprintf("Expected\n%s\nnot to match XML of\n%s", actualString, expectedString)
48
}
49
50
func (matcher *MatchXMLMatcher) formattedPrint(actual any) (actualString, expectedString string, err error) {
51
var ok bool
52
actualString, ok = toString(actual)
53
if !ok {
54
return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got actual:\n%s", format.Object(actual, 1))
55
}
56
expectedString, ok = toString(matcher.XMLToMatch)
57
if !ok {
58
return "", "", fmt.Errorf("MatchXMLMatcher matcher requires a string, stringer, or []byte. Got expected:\n%s", format.Object(matcher.XMLToMatch, 1))
59
}
60
return actualString, expectedString, nil
61
}
62
63
func parseXmlContent(content string) (*xmlNode, error) {
64
allNodes := []*xmlNode{}
65
66
dec := newXmlDecoder(strings.NewReader(content))
67
for {
68
tok, err := dec.Token()
69
if err != nil {
70
if err == io.EOF {
71
break
72
}
73
return nil, fmt.Errorf("failed to decode next token: %v", err) // untested section
74
}
75
76
lastNodeIndex := len(allNodes) - 1
77
var lastNode *xmlNode
78
if len(allNodes) > 0 {
79
lastNode = allNodes[lastNodeIndex]
80
} else {
81
lastNode = &xmlNode{}
82
}
83
84
switch tok := tok.(type) {
85
case xml.StartElement:
86
attrs := attributesSlice(tok.Attr)
87
sort.Sort(attrs)
88
allNodes = append(allNodes, &xmlNode{XMLName: tok.Name, XMLAttr: tok.Attr})
89
case xml.EndElement:
90
if len(allNodes) > 1 {
91
allNodes[lastNodeIndex-1].Nodes = append(allNodes[lastNodeIndex-1].Nodes, lastNode)
92
allNodes = allNodes[:lastNodeIndex]
93
}
94
case xml.CharData:
95
lastNode.Content = append(lastNode.Content, tok.Copy()...)
96
case xml.Comment:
97
lastNode.Comments = append(lastNode.Comments, tok.Copy()) // untested section
98
case xml.ProcInst:
99
lastNode.ProcInsts = append(lastNode.ProcInsts, tok.Copy())
100
}
101
}
102
103
if len(allNodes) == 0 {
104
return nil, errors.New("found no nodes")
105
}
106
firstNode := allNodes[0]
107
trimParentNodesContentSpaces(firstNode)
108
109
return firstNode, nil
110
}
111
112
func newXmlDecoder(reader io.Reader) *xml.Decoder {
113
dec := xml.NewDecoder(reader)
114
dec.CharsetReader = charset.NewReaderLabel
115
return dec
116
}
117
118
func trimParentNodesContentSpaces(node *xmlNode) {
119
if len(node.Nodes) > 0 {
120
node.Content = bytes.TrimSpace(node.Content)
121
for _, childNode := range node.Nodes {
122
trimParentNodesContentSpaces(childNode)
123
}
124
}
125
}
126
127
type xmlNode struct {
128
XMLName xml.Name
129
Comments []xml.Comment
130
ProcInsts []xml.ProcInst
131
XMLAttr []xml.Attr
132
Content []byte
133
Nodes []*xmlNode
134
}
135
136