|
4 | 4 | "encoding/xml" |
5 | 5 | "errors" |
6 | 6 | "fmt" |
| 7 | + "math" |
| 8 | + "strconv" |
7 | 9 | "strings" |
8 | 10 | "testing" |
9 | 11 | "time" |
@@ -145,7 +147,7 @@ func TestStdEncoder_Encode(t *testing.T) { |
145 | 147 | Int: 123, |
146 | 148 | Double: float64(12345), |
147 | 149 | }, |
148 | | - paramValidator: exactParamsValidator(`<param><value><int>123</int></value></param><param><value><double>12345.000000</double></value></param>`), |
| 150 | + paramValidator: exactParamsValidator(`<param><value><int>123</int></value></param><param><value><double>12345.0</double></value></param>`), |
149 | 151 | }, |
150 | 152 | { |
151 | 153 | name: "String arg - simple", |
@@ -536,7 +538,7 @@ func Test_encodeMap(t *testing.T) { |
536 | 538 | "<member><name>string</name><value><string>value</string></value></member>", |
537 | 539 | "<member><name>int</name><value><int>42</int></value></member>", |
538 | 540 | "<member><name>bool</name><value><boolean>1</boolean></value></member>", |
539 | | - "<member><name>float</name><value><double>3.140000</double></value></member>", |
| 541 | + "<member><name>float</name><value><double>3.14</double></value></member>", |
540 | 542 | }, |
541 | 543 | err: nil, |
542 | 544 | }, |
@@ -660,6 +662,73 @@ func Test_encodeTime_writerErrors(t *testing.T) { |
660 | 662 | } |
661 | 663 | } |
662 | 664 |
|
| 665 | +func Test_encodeDouble(t *testing.T) { |
| 666 | + tests := []struct { |
| 667 | + name string |
| 668 | + input float64 |
| 669 | + expect string |
| 670 | + errMsg string |
| 671 | + }{ |
| 672 | + {name: "zero", input: 0, expect: "<double>0.0</double>"}, |
| 673 | + {name: "negative zero", input: math.Copysign(0, -1), expect: "<double>-0.0</double>"}, |
| 674 | + {name: "integral", input: 12345, expect: "<double>12345.0</double>"}, |
| 675 | + {name: "negative", input: -12.214, expect: "<double>-12.214</double>"}, |
| 676 | + {name: "no padding to six decimals", input: 3.14, expect: "<double>3.14</double>"}, |
| 677 | + // %f used to truncate all of these to six decimal places |
| 678 | + {name: "full float64 precision", input: 3.14159265358979, expect: "<double>3.14159265358979</double>"}, |
| 679 | + {name: "many significant digits", input: 0.1234567890123, expect: "<double>0.1234567890123</double>"}, |
| 680 | + {name: "small magnitude", input: 1e-10, expect: "<double>0.0000000001</double>"}, |
| 681 | + {name: "large magnitude", input: 1e20, expect: "<double>100000000000000000000.0</double>"}, |
| 682 | + {name: "no exponent notation", input: 1e21, expect: "<double>1000000000000000000000.0</double>"}, |
| 683 | + {name: "NaN is rejected", input: math.NaN(), errMsg: "unsupported value NaN"}, |
| 684 | + {name: "positive infinity is rejected", input: math.Inf(1), errMsg: "unsupported value +Inf"}, |
| 685 | + {name: "negative infinity is rejected", input: math.Inf(-1), errMsg: "unsupported value -Inf"}, |
| 686 | + } |
| 687 | + |
| 688 | + for _, tt := range tests { |
| 689 | + t.Run(tt.name, func(t *testing.T) { |
| 690 | + buf := new(strings.Builder) |
| 691 | + x := newXMLWriter(buf) |
| 692 | + |
| 693 | + err := (&StdEncoder{}).encodeDouble(x, tt.input) |
| 694 | + if tt.errMsg != "" { |
| 695 | + require.EqualError(t, err, tt.errMsg) |
| 696 | + |
| 697 | + return |
| 698 | + } |
| 699 | + |
| 700 | + require.NoError(t, err) |
| 701 | + require.NoError(t, x.err) |
| 702 | + require.Equal(t, tt.expect, buf.String()) |
| 703 | + }) |
| 704 | + } |
| 705 | +} |
| 706 | + |
| 707 | +func Test_encodeDouble_roundTrips(t *testing.T) { |
| 708 | + // Every emitted value must parse back to the exact same float64 |
| 709 | + values := []float64{ |
| 710 | + 0, 1, -1, 0.5, 3.14159265358979, 0.1234567890123, 1e-10, 1e20, 1e21, |
| 711 | + math.SmallestNonzeroFloat64, math.MaxFloat64, -math.MaxFloat64, |
| 712 | + } |
| 713 | + |
| 714 | + for _, want := range values { |
| 715 | + t.Run(strconv.FormatFloat(want, 'g', -1, 64), func(t *testing.T) { |
| 716 | + buf := new(strings.Builder) |
| 717 | + x := newXMLWriter(buf) |
| 718 | + require.NoError(t, (&StdEncoder{}).encodeDouble(x, want)) |
| 719 | + |
| 720 | + wire := strings.TrimSuffix(strings.TrimPrefix(buf.String(), "<double>"), "</double>") |
| 721 | + got, err := strconv.ParseFloat(wire, 64) |
| 722 | + require.NoError(t, err) |
| 723 | + require.Equal(t, want, got, "wire value %q must round-trip", wire) |
| 724 | + |
| 725 | + // The specification permits only decimal point notation |
| 726 | + require.Contains(t, wire, ".", "wire value %q must carry a decimal point", wire) |
| 727 | + require.NotContains(t, wire, "e", "wire value %q must not use exponent notation", wire) |
| 728 | + }) |
| 729 | + } |
| 730 | +} |
| 731 | + |
663 | 732 | func Test_Encode_escapesCallerSuppliedNames(t *testing.T) { |
664 | 733 | tests := []struct { |
665 | 734 | name string |
|
0 commit comments