//go:build go1.7
// +build go1.7
package xmlutil
import (
"encoding/xml"
"fmt"
"io"
"math"
"reflect"
"strconv"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
)
type mockBody struct {
DoneErr error
Body io.Reader
}
func (m *mockBody) Read(p []byte) (int, error) {
n, err := m.Body.Read(p)
if (n == 0 || err == io.EOF) && m.DoneErr != nil {
return n, m.DoneErr
}
return n, err
}
type mockOutput struct {
_ struct{} `type:"structure"`
String *string `type:"string"`
Integer *int64 `type:"integer"`
Float *float64 `type:"double"`
Nested *mockNestedStruct `type:"structure"`
List []*mockListElem `locationName:"List" locationNameList:"Elem" type:"list"`
Closed *mockClosedTags `type:"structure"`
}
type mockNestedStruct struct {
_ struct{} `type:"structure"`
NestedString *string `type:"string"`
NestedInt *int64 `type:"integer"`
}
type mockClosedTags struct {
_ struct{} `type:"structure" xmlPrefix:"xsi" xmlURI:"http://www.w3.org/2001/XMLSchema-instance"`
Attr *string `locationName:"xsi:attrval" type:"string" xmlAttribute:"true"`
}
type mockListElem struct {
_ struct{} `type:"structure" xmlPrefix:"xsi" xmlURI:"http://www.w3.org/2001/XMLSchema-instance"`
String *string `type:"string"`
NestedElem *mockNestedListElem `type:"structure"`
}
type mockNestedListElem struct {
_ struct{} `type:"structure" xmlPrefix:"xsi" xmlURI:"http://www.w3.org/2001/XMLSchema-instance"`
String *string `type:"string"`
Type *string `locationName:"xsi:type" type:"string" xmlAttribute:"true"`
}
func TestUnmarshal(t *testing.T) {
cases := []struct {
Body string
Expect mockOutput
ExpectFn func(t *testing.T, actual mockOutput)
}{
{
Body: `
string value
123
nested string value
321
nested elem string value
elem string value
`,
Expect: mockOutput{
String: aws.String("string value"),
Integer: aws.Int64(123),
Closed: &mockClosedTags{
Attr: aws.String("attr value"),
},
Nested: &mockNestedStruct{
NestedString: aws.String("nested string value"),
NestedInt: aws.Int64(321),
},
List: []*mockListElem{
{
String: aws.String("elem string value"),
NestedElem: &mockNestedListElem{
String: aws.String("nested elem string value"),
Type: aws.String("type"),
},
},
},
},
},
{
Body: `123456789.123`,
Expect: mockOutput{Float: aws.Float64(123456789.123)},
},
{
Body: `Infinity`,
ExpectFn: func(t *testing.T, actual mockOutput) {
if a := aws.Float64Value(actual.Float); !math.IsInf(a, 1) {
t.Errorf("expect infinity, got %v", a)
}
},
},
{
Body: `-Infinity`,
ExpectFn: func(t *testing.T, actual mockOutput) {
if a := aws.Float64Value(actual.Float); !math.IsInf(a, -1) {
t.Errorf("expect -infinity, got %v", a)
}
},
},
{
Body: `NaN`,
ExpectFn: func(t *testing.T, actual mockOutput) {
if a := aws.Float64Value(actual.Float); !math.IsNaN(a) {
t.Errorf("expect NaN, got %v", a)
}
},
},
}
for i, tt := range cases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
actual := mockOutput{}
decoder := xml.NewDecoder(strings.NewReader(tt.Body))
err := UnmarshalXML(&actual, decoder, "")
if err != nil {
t.Fatalf("expect no error, got %v", err)
}
if tt.ExpectFn != nil {
tt.ExpectFn(t, actual)
return
}
if !reflect.DeepEqual(tt.Expect, actual) {
t.Errorf("expect unmarshal to match\nExpect: %s\nActual: %s",
awsutil.Prettify(tt.Expect), awsutil.Prettify(actual))
}
})
}
}
func TestUnmarshal_UnexpectedEOF(t *testing.T) {
const partialXMLBody = `
first value
Second val`
out := struct {
First *string `locationName:"First" type:"string"`
Second *string `locationName:"Second" type:"string"`
}{}
expect := out
expect.First = aws.String("first")
expect.Second = aws.String("second")
expectErr := fmt.Errorf("expected read error")
body := &mockBody{
DoneErr: expectErr,
Body: strings.NewReader(partialXMLBody),
}
decoder := xml.NewDecoder(body)
err := UnmarshalXML(&out, decoder, "")
if err == nil {
t.Fatalf("expect error, got none")
}
if e, a := expectErr, err; e != a {
t.Errorf("expect %v error in %v, but was not", e, a)
}
}