diff --git a/.travis.yml b/.travis.yml
index b27d96ceb63..073a166baf2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,6 +17,7 @@ go:
- 1.13.x
- 1.14.x
- 1.15.x
+ - 1.16.x
- tip
matrix:
@@ -38,6 +39,8 @@ matrix:
go: 1.14.x
- os: windows
go: 1.15.x
+ - os: windows
+ go: 1.16.x
- os: windows
go: tip
@@ -46,7 +49,8 @@ before_install:
script:
- if [ -z $(go env GOMOD) ]; then
- if [ "$TRAVIS_GO_VERSION" == "1.13.x" ] ||
+ if [ "$TRAVIS_GO_VERSION" == "1.14.x" ] ||
+ [ "$TRAVIS_GO_VERSION" == "1.13.x" ] ||
[ "$TRAVIS_GO_VERSION" == "1.12.x" ] ||
[ "$TRAVIS_GO_VERSION" == "1.11.x" ] ||
[ "$TRAVIS_GO_VERSION" == "1.10.x" ]; then
@@ -55,8 +59,8 @@ script:
make get-deps;
fi;
if [ "$TRAVIS_GO_VERSION" == "tip" ] ||
- [ "$TRAVIS_GO_VERSION" == "1.15.x" ] ||
- [ "$TRAVIS_GO_VERSION" == "1.14.x" ]; then
+ [ "$TRAVIS_GO_VERSION" == "1.16.x" ] ||
+ [ "$TRAVIS_GO_VERSION" == "1.15.x" ]; then
if [ "$TRAVIS_OS_NAME" = "windows" ]; then
make unit-no-verify;
diff --git a/Makefile b/Makefile
index dbb7f334cae..9f7b50a9379 100644
--- a/Makefile
+++ b/Makefile
@@ -187,6 +187,13 @@ sandbox-go1.15: sandbox-build-go1.15
sandbox-test-go1.15: sandbox-build-go1.15
docker run -t aws-sdk-go-1.15
+sandbox-build-go1.16:
+ docker build -f ./awstesting/sandbox/Dockerfile.test.go1.16 -t "aws-sdk-go-1.16" .
+sandbox-go1.16: sandbox-build-go1.16
+ docker run -i -t aws-sdk-go-1.16 bash
+sandbox-test-go1.16: sandbox-build-go1.16
+ docker run -t aws-sdk-go-1.16
+
sandbox-build-gotip:
@echo "Run make update-aws-golang-tip, if this test fails because missing aws-golang:tip container"
docker build -f ./awstesting/sandbox/Dockerfile.test.gotip -t "aws-sdk-go-tip" .
diff --git a/aws/credentials/credentials_bench_test.go b/aws/credentials/credentials_bench_test.go
index 01a5d633bf8..5265cc1b8a4 100644
--- a/aws/credentials/credentials_bench_test.go
+++ b/aws/credentials/credentials_bench_test.go
@@ -25,7 +25,7 @@ func BenchmarkCredentials_Get(b *testing.B) {
for j := 0; j < b.N; j++ {
v, err := creds.Get()
if err != nil {
- b.Fatalf("expect no error %v, %v", v, err)
+ b.Errorf("expect no error %v, %v", v, err)
}
}
wg.Done()
@@ -55,7 +55,7 @@ func BenchmarkCredentials_Get_Expire(b *testing.B) {
for j := 0; j < b.N; j++ {
v, err := creds.Get()
if err != nil {
- b.Fatalf("expect no error %v, %v", v, err)
+ b.Errorf("expect no error %v, %v", v, err)
}
// periodically expire creds to cause rwlock
if id == 0 && j%expRate == 0 {
diff --git a/awstesting/assert.go b/awstesting/assert.go
index 3854c6538d8..8ca3a425081 100644
--- a/awstesting/assert.go
+++ b/awstesting/assert.go
@@ -1,9 +1,7 @@
package awstesting
import (
- "bytes"
"encoding/json"
- "encoding/xml"
"fmt"
"net/url"
"reflect"
@@ -12,7 +10,7 @@ import (
"strings"
"testing"
- "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
+ "github.com/aws/aws-sdk-go/internal/smithytesting"
)
// Match is a testing helper to test for testing error by comparing expected
@@ -114,26 +112,14 @@ func AssertJSON(t *testing.T, expect, actual string, msgAndArgs ...interface{})
return equal(t, expectVal, actualVal, msgAndArgs...)
}
-// AssertXML verifies that the expect xml string matches the actual.
-// Elements in actual must match the order they appear in expect to match equally
+// AssertXML verifies that the expect XML string matches the actual.
func AssertXML(t *testing.T, expect, actual string, msgAndArgs ...interface{}) bool {
- buffer := bytes.NewReader([]byte(expect))
- decoder := xml.NewDecoder(buffer)
-
- expectVal, err := xmlutil.XMLToStruct(decoder, nil)
- if err != nil {
- t.Fatalf("failed to umarshal xml to struct: %v", err)
- }
-
- buffer = bytes.NewReader([]byte(actual))
- decoder = xml.NewDecoder(buffer)
-
- actualVal, err := xmlutil.XMLToStruct(decoder, nil)
- if err != nil {
- t.Fatalf("failed to umarshal xml to struct: %v", err)
+ if err := smithytesting.XMLEqual([]byte(expect), []byte(actual)); err != nil {
+ t.Error("expect XML match", err, messageFromMsgAndArgs(msgAndArgs))
+ return false
}
- return equal(t, expectVal, actualVal, msgAndArgs...)
+ return true
}
// DidPanic returns if the function paniced and returns true if the function paniced.
diff --git a/awstesting/sandbox/Dockerfile.test.go1.16 b/awstesting/sandbox/Dockerfile.test.go1.16
new file mode 100644
index 00000000000..95ad2637002
--- /dev/null
+++ b/awstesting/sandbox/Dockerfile.test.go1.16
@@ -0,0 +1,12 @@
+FROM golang:1.16
+
+ENV GOPROXY=direct
+
+ADD . /go/src/github.com/aws/aws-sdk-go
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ vim \
+ && rm -rf /var/list/apt/lists/*
+
+WORKDIR /go/src/github.com/aws/aws-sdk-go
+CMD ["make", "get-deps-verify", "unit"]
diff --git a/internal/smithytesting/document.go b/internal/smithytesting/document.go
new file mode 100644
index 00000000000..87c10eb8902
--- /dev/null
+++ b/internal/smithytesting/document.go
@@ -0,0 +1,32 @@
+package smithytesting
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+
+ "github.com/aws/aws-sdk-go/internal/smithytesting/xml"
+)
+
+// XMLEqual asserts two XML documents by sorting the XML and comparing the
+// strings It returns an error in case of mismatch or in case of malformed XML
+// found while sorting. In case of mismatched XML, the error string will
+// contain the diff between the two XML documents.
+func XMLEqual(expectBytes, actualBytes []byte) error {
+ actualString, err := xml.SortXML(bytes.NewBuffer(actualBytes), true)
+ if err != nil {
+ return err
+ }
+
+ expectString, err := xml.SortXML(bytes.NewBuffer(expectBytes), true)
+ if err != nil {
+ return err
+ }
+
+ if !reflect.DeepEqual(expectString, actualString) {
+ return fmt.Errorf("unexpected XML mismatch\nexpect: %+v\nactual: %+v",
+ expectString, actualString)
+ }
+
+ return nil
+}
diff --git a/internal/smithytesting/document_test.go b/internal/smithytesting/document_test.go
new file mode 100644
index 00000000000..db99b671d17
--- /dev/null
+++ b/internal/smithytesting/document_test.go
@@ -0,0 +1,157 @@
+// +build go1.9
+
+package smithytesting
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestEqualXMLUtil(t *testing.T) {
+ cases := map[string]struct {
+ expectedXML string
+ actualXML string
+ expectErr string
+ }{
+ "empty": {
+ expectedXML: ``,
+ actualXML: ``,
+ },
+ "emptyWithDiff": {
+ expectedXML: ``,
+ actualXML: ``,
+ expectErr: "XML mismatch",
+ },
+ "simpleXML": {
+ expectedXML: ``,
+ actualXML: ``,
+ },
+ "simpleXMLWithDiff": {
+ expectedXML: ``,
+ actualXML: `abc`,
+ expectErr: "XML mismatch",
+ },
+ "nestedXML": {
+ expectedXML: `123xyz`,
+ actualXML: `123xyz`,
+ },
+ "nestedXMLWithExpectedDiff": {
+ expectedXML: `123xyz234`,
+ actualXML: `123xyz`,
+ expectErr: "XML mismatch",
+ },
+ "nestedXMLWithActualDiff": {
+ expectedXML: `123xyz`,
+ actualXML: `123xyz234`,
+ expectErr: "XML mismatch",
+ },
+ "Array": {
+ expectedXML: `xyzabc
`,
+ actualXML: `xyzabc
`,
+ },
+ "ArrayWithSecondDiff": {
+ expectedXML: `xyz123
`,
+ actualXML: `xyz345
`,
+ expectErr: "XML mismatch",
+ },
+ "ArrayWithFirstDiff": {
+ expectedXML: `abc345
`,
+ actualXML: `xyz345
`,
+ expectErr: "XML mismatch",
+ },
+ "ArrayWithMixedDiff": {
+ expectedXML: `345xyz
`,
+ actualXML: `xyz345
`,
+ },
+ "ArrayWithRepetitiveMembers": {
+ expectedXML: `xyzxyz
`,
+ actualXML: `xyzxyz
`,
+ },
+ "Map": {
+ expectedXML: ``,
+ actualXML: ``,
+ },
+ "MapWithFirstDiff": {
+ expectedXML: ``,
+ actualXML: ``,
+ expectErr: "XML mismatch",
+ },
+ "MapWithSecondDiff": {
+ expectedXML: ``,
+ actualXML: ``,
+ expectErr: "XML mismatch",
+ },
+ "MapWithMixedDiff": {
+ expectedXML: ``,
+ actualXML: ``,
+ },
+ "MismatchCheckforKeyValue": {
+ expectedXML: ``,
+ actualXML: ``,
+ expectErr: "XML mismatch",
+ },
+ "MixMapAndListNestedXML": {
+ expectedXML: `mem1
mem2
`,
+ actualXML: `mem1
mem2
`,
+ },
+ "MixMapAndListNestedXMLWithDiff": {
+ expectedXML: `mem1
mem2
`,
+ actualXML: `mem1
mem2
`,
+ expectErr: "XML mismatch",
+ },
+ "xmlWithNamespaceAndAttr": {
+ expectedXML: `value`,
+ actualXML: `value`,
+ },
+ "xmlUnorderedAttributes": {
+ expectedXML: `v`,
+ actualXML: `v`,
+ },
+ "xmlAttributesWithDiff": {
+ expectedXML: `v`,
+ actualXML: `v`,
+ expectErr: "XML mismatch",
+ },
+ "xmlUnorderedNamespaces": {
+ expectedXML: `v`,
+ actualXML: `v`,
+ },
+ "xmlNamespaceWithDiff": {
+ expectedXML: `v`,
+ actualXML: `v`,
+ expectErr: "XML mismatch",
+ },
+ "NestedWithNamespaceAndAttributes": {
+ expectedXML: `mem1mem2`,
+ actualXML: `mem1mem2`,
+ },
+ "NestedWithNamespaceAndAttributesWithDiff": {
+ expectedXML: `mem2
mem2`,
+ actualXML: `mem1
mem2`,
+ expectErr: "XML mismatch",
+ },
+ "MalformedXML": {
+ expectedXML: `aa2vbb2w`,
+ actualXML: `aa2vbb2w`,
+ expectErr: "malformed xml",
+ },
+ }
+
+ for name, c := range cases {
+ t.Run(name, func(t *testing.T) {
+ actual := []byte(c.actualXML)
+ expected := []byte(c.expectedXML)
+
+ err := XMLEqual(actual, expected)
+ if err != nil {
+ if len(c.expectErr) == 0 {
+ t.Fatalf("expected no error while parsing xml, got %v", err)
+ } else if !strings.Contains(err.Error(), c.expectErr) {
+ t.Fatalf("expected expected XML err to contain %s, got %s", c.expectErr, err.Error())
+ }
+ } else if len(c.expectErr) != 0 {
+ t.Fatalf("expected error %s, got none", c.expectErr)
+ }
+ })
+ }
+}
diff --git a/internal/smithytesting/xml/doc.go b/internal/smithytesting/xml/doc.go
new file mode 100644
index 00000000000..611fc441a76
--- /dev/null
+++ b/internal/smithytesting/xml/doc.go
@@ -0,0 +1,7 @@
+// Package xml is XML testing package that supports XML comparison utility.
+// The package consists of ToStruct and StructToXML utils that help sort XML
+// elements as per their nesting level. ToStruct function converts an XML
+// document into a sorted tree node structure, while StructToXML converts the
+// sorted XML nodes into a sorted XML document. SortXML function should be
+// used to sort an XML document. It can be configured to ignore indentation
+package xml
diff --git a/internal/smithytesting/xml/sort.go b/internal/smithytesting/xml/sort.go
new file mode 100644
index 00000000000..e4fcd328bf3
--- /dev/null
+++ b/internal/smithytesting/xml/sort.go
@@ -0,0 +1,48 @@
+package xml
+
+import (
+ "bytes"
+ "encoding/xml"
+ "io"
+ "strings"
+)
+
+type xmlAttrSlice []xml.Attr
+
+func (x xmlAttrSlice) Len() int {
+ return len(x)
+}
+
+func (x xmlAttrSlice) Less(i, j int) bool {
+ spaceI, spaceJ := x[i].Name.Space, x[j].Name.Space
+ localI, localJ := x[i].Name.Local, x[j].Name.Local
+ valueI, valueJ := x[i].Value, x[j].Value
+
+ spaceCmp := strings.Compare(spaceI, spaceJ)
+ localCmp := strings.Compare(localI, localJ)
+ valueCmp := strings.Compare(valueI, valueJ)
+
+ if spaceCmp == -1 || (spaceCmp == 0 && (localCmp == -1 || (localCmp == 0 && valueCmp == -1))) {
+ return true
+ }
+
+ return false
+}
+
+func (x xmlAttrSlice) Swap(i, j int) {
+ x[i], x[j] = x[j], x[i]
+}
+
+// SortXML sorts the reader's XML elements
+func SortXML(r io.Reader, ignoreIndentation bool) (string, error) {
+ var buf bytes.Buffer
+ d := xml.NewDecoder(r)
+ root, err := ToStruct(d, nil, ignoreIndentation)
+ if err != nil {
+ return buf.String(), err
+ }
+
+ e := xml.NewEncoder(&buf)
+ err = StructToXML(e, root, true)
+ return buf.String(), err
+}
diff --git a/internal/smithytesting/xml/sort_test.go b/internal/smithytesting/xml/sort_test.go
new file mode 100644
index 00000000000..02f02019da9
--- /dev/null
+++ b/internal/smithytesting/xml/sort_test.go
@@ -0,0 +1,20 @@
+package xml
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+func TestSortXML(t *testing.T) {
+ xmlInput := bytes.NewReader([]byte(`xyz123- 1
`))
+ sortedXML, err := SortXML(xmlInput, false)
+ expectedsortedXML := `123xyz- 1
`
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ if !reflect.DeepEqual(sortedXML, expectedsortedXML) {
+ t.Errorf("expect match\nexpect: %+v\nactual: %+v\n", expectedsortedXML, sortedXML)
+ }
+}
diff --git a/internal/smithytesting/xml/xmlToStruct.go b/internal/smithytesting/xml/xmlToStruct.go
new file mode 100644
index 00000000000..cbbd52acfef
--- /dev/null
+++ b/internal/smithytesting/xml/xmlToStruct.go
@@ -0,0 +1,339 @@
+package xml
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+)
+
+// A Node contains the values to be encoded or decoded.
+type Node struct {
+ Name xml.Name `json:",omitempty"`
+ Children map[string][]*Node `json:",omitempty"`
+ Text string `json:",omitempty"`
+ Attr []xml.Attr `json:",omitempty"`
+
+ namespaces map[string]string
+ parent *Node
+}
+
+// NewXMLElement returns a pointer to a new Node initialized to default values.
+func NewXMLElement(name xml.Name) *Node {
+ return &Node{
+ Name: name,
+ Children: map[string][]*Node{},
+ Attr: []xml.Attr{},
+ }
+}
+
+// AddChild adds child to the Node.
+func (n *Node) AddChild(child *Node) {
+ child.parent = n
+ if _, ok := n.Children[child.Name.Local]; !ok {
+ // flattened will have multiple children with same tag name
+ n.Children[child.Name.Local] = []*Node{}
+ }
+ n.Children[child.Name.Local] = append(n.Children[child.Name.Local], child)
+}
+
+// ToStruct converts a xml.Decoder stream to Node with nested values.
+func ToStruct(d *xml.Decoder, s *xml.StartElement, ignoreIndentation bool) (*Node, error) {
+ out := &Node{}
+
+ for {
+ tok, err := d.Token()
+ if err != nil {
+ if err == io.EOF {
+ break
+ } else {
+ return out, err
+ }
+ }
+
+ if tok == nil {
+ break
+ }
+
+ switch typed := tok.(type) {
+ case xml.CharData:
+ text := string(typed.Copy())
+ if ignoreIndentation {
+ text = strings.TrimSpace(text)
+ }
+ if len(text) != 0 {
+ out.Text = text
+ }
+ case xml.StartElement:
+ el := typed.Copy()
+ out.Attr = el.Attr
+ if out.Children == nil {
+ out.Children = map[string][]*Node{}
+ }
+
+ name := typed.Name.Local
+ slice := out.Children[name]
+ if slice == nil {
+ slice = []*Node{}
+ }
+ node, e := ToStruct(d, &el, ignoreIndentation)
+ out.findNamespaces()
+ if e != nil {
+ return out, e
+ }
+
+ node.Name = typed.Name
+ node.findNamespaces()
+
+ // Add attributes onto the node
+ node.Attr = el.Attr
+
+ tempOut := *out
+ // Save into a temp variable, simply because out gets squashed during
+ // loop iterations
+ node.parent = &tempOut
+ slice = append(slice, node)
+ out.Children[name] = slice
+ case xml.EndElement:
+ if s != nil && s.Name.Local == typed.Name.Local { // matching end token
+ return out, nil
+ }
+ out = &Node{}
+ }
+ }
+ return out, nil
+}
+
+func (n *Node) findNamespaces() {
+ ns := map[string]string{}
+ for _, a := range n.Attr {
+ if a.Name.Space == "xmlns" {
+ ns[a.Value] = a.Name.Local
+ }
+ }
+
+ n.namespaces = ns
+}
+
+func (n *Node) findElem(name string) (string, bool) {
+ for node := n; node != nil; node = node.parent {
+ for _, a := range node.Attr {
+ namespace := a.Name.Space
+ if v, ok := node.namespaces[namespace]; ok {
+ namespace = v
+ }
+ if name == fmt.Sprintf("%s:%s", namespace, a.Name.Local) {
+ return a.Value, true
+ }
+ }
+ }
+ return "", false
+}
+
+// StructToXML writes an Node to a xml.Encoder as tokens.
+func StructToXML(e *xml.Encoder, node *Node, sorted bool) error {
+ var err error
+ // Sort Attributes
+ attrs := node.Attr
+ if sorted {
+ sortedAttrs := make([]xml.Attr, len(attrs))
+ for _, k := range node.Attr {
+ sortedAttrs = append(sortedAttrs, k)
+ }
+ sort.Sort(xmlAttrSlice(sortedAttrs))
+ attrs = sortedAttrs
+ }
+
+ st := xml.StartElement{Name: node.Name, Attr: attrs}
+ e.EncodeToken(st)
+ // return fmt.Errorf("encoder string : %s, %s, %s", node.Name.Local, node.Name.Space, st.Attr)
+
+ if node.Text != "" {
+ e.EncodeToken(xml.CharData([]byte(node.Text)))
+ } else if sorted {
+ sortedNames := []string{}
+ for k := range node.Children {
+ sortedNames = append(sortedNames, k)
+ }
+ sort.Strings(sortedNames)
+
+ for _, k := range sortedNames {
+ // we should sort the []*xml.Node for each key if len >1
+ flattenedNodes := node.Children[k]
+ // Meaning this has multiple nodes
+ if len(flattenedNodes) > 1 {
+ // sort flattened nodes
+ flattenedNodes, err = sortFlattenedNodes(flattenedNodes)
+ if err != nil {
+ return err
+ }
+ }
+
+ for _, v := range flattenedNodes {
+ err = StructToXML(e, v, sorted)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ } else {
+ for _, c := range node.Children {
+ for _, v := range c {
+ err = StructToXML(e, v, sorted)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+
+ e.EncodeToken(xml.EndElement{Name: node.Name})
+ return e.Flush()
+}
+
+// sortFlattenedNodes sorts nodes with nodes having same element tag
+// but overall different values. The function will return list of pointer to
+// Node and an error.
+//
+// Overall sort order is followed is:
+// Nodes with concrete value (no nested node as value) are given precedence
+// and are added to list after sorting them
+//
+// Next nested nodes within a flattened list are given precedence.
+//
+// Next nodes within a flattened map are sorted based on either key or value
+// which ever has lower value and then added to the global sorted list.
+// If value was initially chosen, but has nested nodes; key will be chosen as comparable
+// as it is unique and will always have concrete data ie. string.
+func sortFlattenedNodes(nodes []*Node) ([]*Node, error) {
+ var sortedNodes []*Node
+
+ // concreteNodeMap stores concrete value associated with a list of nodes
+ // This is possible in case multiple members of a flatList has same values.
+ concreteNodeMap := make(map[string][]*Node, 0)
+
+ // flatListNodeMap stores flat list or wrapped list members associated with a list of nodes
+ // This will have only flattened list with members that are Nodes and not concrete values.
+ flatListNodeMap := make(map[string][]*Node, 0)
+
+ // flatMapNodeMap stores flat map or map entry members associated with a list of nodes
+ // This will have only flattened map concrete value members. It is possible to limit this
+ // to concrete value as map key is expected to be concrete.
+ flatMapNodeMap := make(map[string][]*Node, 0)
+
+ // nodes with concrete value are prioritized and appended based on sorting order
+ sortedNodesWithConcreteValue := []string{}
+
+ // list with nested nodes are second in priority and appended based on sorting order
+ sortedNodesWithListValue := []string{}
+
+ // map are last in priority and appended based on sorting order
+ sortedNodesWithMapValue := []string{}
+
+ for _, node := range nodes {
+ // node has no children element, then we consider it as having concrete value
+ if len(node.Children) == 0 {
+ sortedNodesWithConcreteValue = append(sortedNodesWithConcreteValue, node.Text)
+ if v, ok := concreteNodeMap[node.Text]; ok {
+ concreteNodeMap[node.Text] = append(v, node)
+ } else {
+ concreteNodeMap[node.Text] = []*Node{node}
+ }
+ }
+
+ // if node has a single child, then it is a flattened list node
+ if len(node.Children) == 1 {
+ for _, nestedNodes := range node.Children {
+ nestedNodeName := nestedNodes[0].Name.Local
+
+ // append to sorted node name for list value
+ sortedNodesWithListValue = append(sortedNodesWithListValue, nestedNodeName)
+
+ if v, ok := flatListNodeMap[nestedNodeName]; ok {
+ flatListNodeMap[nestedNodeName] = append(v, nestedNodes[0])
+ } else {
+ flatListNodeMap[nestedNodeName] = []*Node{nestedNodes[0]}
+ }
+ }
+ }
+
+ // if node has two children, then it is a flattened map node
+ if len(node.Children) == 2 {
+ nestedPair := []*Node{}
+ for _, k := range node.Children {
+ nestedPair = append(nestedPair, k[0])
+ }
+
+ comparableValues := []string{nestedPair[0].Name.Local, nestedPair[1].Name.Local}
+ sort.Strings(comparableValues)
+
+ comparableValue := comparableValues[0]
+ for _, nestedNode := range nestedPair {
+ if comparableValue == nestedNode.Name.Local && len(nestedNode.Children) != 0 {
+ // if value was selected and is nested node, skip it and use key instead
+ comparableValue = comparableValues[1]
+ continue
+ }
+
+ // now we are certain there is no nested node
+ if comparableValue == nestedNode.Name.Local {
+ // get chardata for comparison
+ comparableValue = nestedNode.Text
+ sortedNodesWithMapValue = append(sortedNodesWithMapValue, comparableValue)
+
+ if v, ok := flatMapNodeMap[comparableValue]; ok {
+ flatMapNodeMap[comparableValue] = append(v, node)
+ } else {
+ flatMapNodeMap[comparableValue] = []*Node{node}
+ }
+ break
+ }
+ }
+ }
+
+ // we don't support multiple same name nodes in an xml doc except for in flattened maps, list.
+ if len(node.Children) > 2 {
+ return nodes, fmt.Errorf("malformed xml: multiple nodes with same key name exist, " +
+ "but are not associated with flattened maps (2 children) or list (0 or 1 child)")
+ }
+ }
+
+ // sort concrete value node name list and append corresponding nodes
+ // to sortedNodes
+ sort.Strings(sortedNodesWithConcreteValue)
+ for _, name := range sortedNodesWithConcreteValue {
+ for _, node := range concreteNodeMap[name] {
+ sortedNodes = append(sortedNodes, node)
+ }
+ }
+
+ // sort nested nodes with a list and append corresponding nodes
+ // to sortedNodes
+ sort.Strings(sortedNodesWithListValue)
+ for _, name := range sortedNodesWithListValue {
+ // if two nested nodes have same name, then sort them separately.
+ if len(flatListNodeMap[name]) > 1 {
+ // return nodes, fmt.Errorf("flat list node name are %s %v", flatListNodeMap[name][0].Name.Local, len(flatListNodeMap[name]))
+ nestedFlattenedList, err := sortFlattenedNodes(flatListNodeMap[name])
+ if err != nil {
+ return nodes, err
+ }
+ // append the identical but sorted nodes
+ for _, nestedNode := range nestedFlattenedList {
+ sortedNodes = append(sortedNodes, nestedNode)
+ }
+ } else {
+ // append the sorted nodes
+ sortedNodes = append(sortedNodes, flatListNodeMap[name][0])
+ }
+ }
+
+ // sorted nodes with a map and append corresponding nodes to sortedNodes
+ sort.Strings(sortedNodesWithMapValue)
+ for _, name := range sortedNodesWithMapValue {
+ sortedNodes = append(sortedNodes, flatMapNodeMap[name][0])
+ }
+
+ return sortedNodes, nil
+}
diff --git a/private/model/cli/gen-protocol-tests/main.go b/private/model/cli/gen-protocol-tests/main.go
index cab5d5b7efb..f53699993e2 100644
--- a/private/model/cli/gen-protocol-tests/main.go
+++ b/private/model/cli/gen-protocol-tests/main.go
@@ -198,12 +198,7 @@ func (t tplInputTestCaseData) BodyAssertions() string {
protocol := t.TestCase.TestSuite.API.Metadata.Protocol
// Extract the body bytes
- switch protocol {
- case "rest-xml":
- fmt.Fprintln(code, "body := util.SortXML(r.Body)")
- default:
- fmt.Fprintln(code, "body, _ := ioutil.ReadAll(r.Body)")
- }
+ fmt.Fprintln(code, "body, _ := ioutil.ReadAll(r.Body)")
// Generate the body verification code
expectedBody := util.Trim(t.TestCase.InputTest.Body)
@@ -213,7 +208,7 @@ func (t tplInputTestCaseData) BodyAssertions() string {
expectedBody)
case "rest-xml":
if strings.HasPrefix(expectedBody, "<") {
- fmt.Fprintf(code, "awstesting.AssertXML(t, `%s`, util.Trim(body))",
+ fmt.Fprintf(code, "awstesting.AssertXML(t, `%s`, util.Trim(string(body)))",
expectedBody)
} else {
code.WriteString(fmtAssertEqual(fmt.Sprintf("%q", expectedBody), "util.Trim(string(body))"))
@@ -294,7 +289,7 @@ func (i *testCase) TestCase(idx int) string {
m, _ := url.ParseQuery(i.InputTest.Body)
i.InputTest.Body = m.Encode()
case "rest-xml":
- i.InputTest.Body = util.SortXML(bytes.NewReader([]byte(i.InputTest.Body)))
+ // Nothing to do
case "json", "rest-json":
// Nothing to do
}
diff --git a/private/protocol/restxml/build_test.go b/private/protocol/restxml/build_test.go
index 0069f5d2cab..aaad31323fe 100644
--- a/private/protocol/restxml/build_test.go
+++ b/private/protocol/restxml/build_test.go
@@ -6058,8 +6058,8 @@ func TestInputService1ProtocolTestBasicXMLSerializationCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `barfoo`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foobar`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6087,8 +6087,8 @@ func TestInputService1ProtocolTestBasicXMLSerializationCase2(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `barfoo`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foobar`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6137,8 +6137,8 @@ func TestInputService2ProtocolTestSerializeOtherScalarTypesCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `true3false1.2`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `truefalse1.23`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6169,8 +6169,8 @@ func TestInputService3ProtocolTestNestedStructuresCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `bazba`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `abbaz`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6200,8 +6200,8 @@ func TestInputService3ProtocolTestNestedStructuresCase2(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `baza`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `abaz`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6229,8 +6229,8 @@ func TestInputService4ProtocolTestNestedStructuresCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `baz`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `baz`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6261,8 +6261,8 @@ func TestInputService5ProtocolTestNonFlattenedListsCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `onetwothree`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `onetwothree`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6293,8 +6293,8 @@ func TestInputService6ProtocolTestNonFlattenedListsWithLocationNameCase1(t *test
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `onetwothree`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `onetwothree`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6325,8 +6325,8 @@ func TestInputService7ProtocolTestFlattenedListsCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `onetwothree`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `onetwothree`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6357,8 +6357,8 @@ func TestInputService8ProtocolTestFlattenedListsWithLocationNameCase1(t *testing
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `- one
- two
- three
`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `- one
- two
- three
`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6395,8 +6395,8 @@ func TestInputService9ProtocolTestListOfStructuresCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `- one
- two
- three
`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `- one
- two
- three
`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6425,8 +6425,8 @@ func TestInputService10ProtocolTestBlobShapesCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `Zm9v`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `Zm9v`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone", r.URL.String())
@@ -6461,8 +6461,8 @@ func TestInputService11ProtocolTestTimestampShapesCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `2015-01-25T08:00:00ZSun, 25 Jan 2015 08:00:00 GMTSun, 25 Jan 2015 08:00:00 GMT`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `2015-01-25T08:00:00ZSun, 25 Jan 2015 08:00:00 GMTSun, 25 Jan 2015 08:00:00 GMT`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/2014-01-01/hostedzone?TimeQuery=2015-01-25T08%3A00%3A00Z&TimeCustomQuery=1422172800&TimeFormatQuery=1422172800", r.URL.String())
@@ -6650,7 +6650,7 @@ func TestInputService17ProtocolTestStringPayloadCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
+ body, _ := ioutil.ReadAll(r.Body)
if e, a := "bar", util.Trim(string(body)); e != a {
t.Errorf("expect %v, got %v", e, a)
}
@@ -6680,7 +6680,7 @@ func TestInputService18ProtocolTestBlobPayloadCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
+ body, _ := ioutil.ReadAll(r.Body)
if e, a := "bar", util.Trim(string(body)); e != a {
t.Errorf("expect %v, got %v", e, a)
}
@@ -6731,8 +6731,8 @@ func TestInputService19ProtocolTestStructurePayloadCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `bar`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `bar`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/", r.URL.String())
@@ -6778,8 +6778,8 @@ func TestInputService19ProtocolTestStructurePayloadCase3(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, ``, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, ``, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/", r.URL.String())
@@ -6830,8 +6830,8 @@ func TestInputService20ProtocolTestXMLAttributeCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foo@example.com`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foo@example.com`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/", r.URL.String())
@@ -6922,8 +6922,8 @@ func TestInputService23ProtocolTestRecursiveShapesCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foo`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foo`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -6954,8 +6954,8 @@ func TestInputService23ProtocolTestRecursiveShapesCase2(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foo`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foo`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -6990,8 +6990,8 @@ func TestInputService23ProtocolTestRecursiveShapesCase3(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foo`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foo`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -7027,8 +7027,8 @@ func TestInputService23ProtocolTestRecursiveShapesCase4(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foobar`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foobar`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -7066,8 +7066,8 @@ func TestInputService23ProtocolTestRecursiveShapesCase5(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foobar`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foobar`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -7103,8 +7103,8 @@ func TestInputService23ProtocolTestRecursiveShapesCase6(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `barbarfoofoo`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `barbarfoofoo`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -7131,8 +7131,8 @@ func TestInputService24ProtocolTestIdempotencyTokenAutoFillCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `abc123`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `abc123`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -7157,8 +7157,8 @@ func TestInputService24ProtocolTestIdempotencyTokenAutoFillCase2(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `00000000-0000-4000-8000-000000000000`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `00000000-0000-4000-8000-000000000000`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/path", r.URL.String())
@@ -7197,8 +7197,8 @@ func TestInputService25ProtocolTestEnumCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `foofoobar`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `foofoobar`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://test/Enum/bar?ListEnums=0&ListEnums=&ListEnums=1", r.URL.String())
@@ -7249,8 +7249,8 @@ func TestInputService26ProtocolTestEndpointHostTraitCase1(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `myname`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `myname`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://data-service.region.amazonaws.com/path", r.URL.String())
@@ -7277,8 +7277,8 @@ func TestInputService26ProtocolTestEndpointHostTraitCase2(t *testing.T) {
if r.Body == nil {
t.Errorf("expect body not to be nil")
}
- body := util.SortXML(r.Body)
- awstesting.AssertXML(t, `myname`, util.Trim(body))
+ body, _ := ioutil.ReadAll(r.Body)
+ awstesting.AssertXML(t, `myname`, util.Trim(string(body)))
// assert URL
awstesting.AssertURL(t, "https://foo-myname.service.region.amazonaws.com/path", r.URL.String())
diff --git a/private/util/util.go b/private/util/util.go
index 5f2dab25e20..73b492763d3 100644
--- a/private/util/util.go
+++ b/private/util/util.go
@@ -42,6 +42,8 @@ func Capitalize(s string) string {
}
// SortXML sorts the reader's XML elements
+//
+// Deprecated: incorrectly handles XML namespaces, should not be used.
func SortXML(r io.Reader) string {
var buf bytes.Buffer
d := xml.NewDecoder(r)
diff --git a/service/kinesis/cust_integ_eventstream_test.go b/service/kinesis/cust_integ_eventstream_test.go
index 2e809c19a32..60638503530 100644
--- a/service/kinesis/cust_integ_eventstream_test.go
+++ b/service/kinesis/cust_integ_eventstream_test.go
@@ -52,7 +52,7 @@ func TestInteg_SubscribeToShard(t *testing.T) {
sub, err := svc.SubscribeToShardWithContext(ctx, params)
if err != nil {
- t.Fatalf("expect no error, %v, %v", err, *s.ShardId)
+ t.Errorf("expect no error, %v, %v", err, *s.ShardId)
}
defer sub.EventStream.Close()
@@ -66,7 +66,7 @@ func TestInteg_SubscribeToShard(t *testing.T) {
atomic.AddInt32(&goodCount, 1)
for _, r := range e.Records {
if len(r.Data) == 0 {
- t.Fatalf("expect data in record, got none")
+ t.Errorf("expect data in record, got none")
}
}
}
@@ -79,7 +79,7 @@ func TestInteg_SubscribeToShard(t *testing.T) {
}
if err := sub.EventStream.Err(); err != nil {
- t.Fatalf("expect no error, %v, %v", err, *s.ShardId)
+ t.Errorf("expect no error, %v, %v", err, *s.ShardId)
}
}(i, shard)
}