1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00

ROUTE53 works on all tests

This commit is contained in:
Tom Limoncelli
2023-11-12 12:42:18 -05:00
parent b034c66df7
commit 09582484ec
7 changed files with 272 additions and 8 deletions

View File

@@ -45,6 +45,8 @@ export AZURE_CLIENT_SECRET=BBBBBBBBB
```
{% endcode %}
NOTE: The ResourceGroup is case sensitive.
## Metadata
This provider does not recognize any special metadata fields unique to Azure DNS.

View File

@@ -1099,6 +1099,7 @@ func makeTests(t *testing.T) []*TestGroup {
// TODO(tlim): Re-add this when we fix the RFC1035 escaped-quotes issue.
//tc("Create TXT with frequently escaped characters", txt("fooex", `!^.*$@#%^&()([][{}{<></:;-_=+\`)),
//clear(),
),
//

View File

@@ -1,6 +1,9 @@
package models
import "testing"
import (
"strings"
"testing"
)
func TestIsQuoted(t *testing.T) {
tests := []struct {
@@ -48,6 +51,8 @@ func TestStripQuotes(t *testing.T) {
}
}
func r(s string, c int) string { return strings.Repeat(s, c) }
func TestParseQuotedTxt(t *testing.T) {
tests := []struct {
d1 string
@@ -59,15 +64,35 @@ func TestParseQuotedTxt(t *testing.T) {
{`foo bar`, []string{`foo bar`}},
{`"aaa" "bbb"`, []string{`aaa`, `bbb`}},
{`"a"a" "bbb"`, []string{`a"a`, `bbb`}},
// Seen in live traffic:
{"\"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\"",
[]string{r("B", 254)}},
{"\"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\"",
[]string{r("C", 255)}},
{"\"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\" \"D\"",
[]string{r("D", 255), "D"}},
{"\"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\" \"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\"",
[]string{r("E", 255), r("E", 255)}},
{"\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" \"F\"",
[]string{r("F", 255), r("F", 255), "F"}},
{"\"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\" \"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\" \"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\"",
[]string{r("G", 255), r("G", 255), r("G", 255)}},
{"\"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"H\"",
[]string{r("H", 255), r("H", 255), r("H", 255), "H"}},
{"\"quo'te\"", []string{`quo'te`}},
{"\"blah`blah\"", []string{"blah`blah"}},
{"\"quo\\\"te\"", []string{`quo"te`}},
{"\"q\\\"uo\\\"te\"", []string{`q"uo"te`}},
{"\"backs\\\\lash\"", []string{`back\slash`}},
}
for i, test := range tests {
ls := ParseQuotedTxt(test.d1)
if len(ls) != len(test.e2) {
t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e2, ls)
t.Errorf("%v: expected TxtStringsLEN=(%q) got (%q)", i, test.e2, ls)
}
for i := range ls {
if ls[i] != test.e2[i] {
t.Errorf("%v: expected TxtStrings=(%v) got (%v)", i, test.e2, ls)
t.Errorf("%v: expected TxtStrings=(%q) got (%q)", i, test.e2, ls)
}
}
}

View File

@@ -14,7 +14,6 @@ import (
"github.com/StackExchange/dnscontrol/v4/models"
"github.com/StackExchange/dnscontrol/v4/pkg/diff2"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
"github.com/StackExchange/dnscontrol/v4/pkg/txtutil"
"github.com/StackExchange/dnscontrol/v4/providers"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
@@ -344,9 +343,22 @@ func (r *route53Provider) GetZoneRecordsCorrections(dc *models.DomainConfig, exi
var rr r53Types.ResourceRecord
if instType == "TXT" {
//printer.Printf("DEBUG: txt=%q\n", r.GetTargetField())
t := txtutil.RFC1035ChunkedAndQuoted(r.GetTargetField())
//printer.Printf("DEBUG: t=%s\n", t)
// //printer.Printf("DEBUG: txt originalv=%v\n", r.GetTargetField())
// //t := txtutil.RFC1035ChunkedAndQuoted(r.GetTargetField())
// //printer.Printf("DEBUG: txt outbound=%q\n", t)
// ts := r.GetTargetTXTChunked255()
// //t = strings.ReplaceAll(t, `\`, `\\`)
// //t = strings.ReplaceAll(t, `"`, `\"`)
// for i := range ts {
// ts[i] = strings.ReplaceAll(ts[i], `\`, `\\`)
// ts[i] = strings.ReplaceAll(ts[i], `"`, `\"`)
// }
// t := `"` + strings.Join(ts, `" "`) + `"`
// printer.Printf("DEBUG: txt outboundv=%v\n", t)
t := txtEncode(r.GetTargetTXTChunked255())
//printer.Printf("XXXXXXXXX %v\n", t)
rr = r53Types.ResourceRecord{
Value: aws.String(t),
}
@@ -502,7 +514,20 @@ func nativeToRecords(set r53Types.ResourceRecordSet, origin string) ([]*models.R
rc.Original = set
switch rtypeString {
case "TXT":
err = rc.SetTargetTXTs(models.ParseQuotedTxt(val))
//printer.Printf("DEBUG: txt inboundv=%v\n", val)
//printer.Printf("DEBUG: txt decoded=%v\n", models.ParseQuotedTxt(val)[0])
//err = rc.SetTargetTXTs(models.ParseQuotedTxt(val))
//dt, _ := models.ParseQuotedFields(val)
//printer.Printf("DEBUG: txt decodedv=%v\n", dt)
//err = rc.SetTargetTXTs(dt)
var t string
t, err = txtDecode(val)
if err == nil {
err = rc.SetTargetTXT(t)
}
default:
err = rc.PopulateFromString(rtypeString, val, origin)
}

View File

@@ -0,0 +1,28 @@
// Code generated by "stringer -type=State"; DO NOT EDIT.
package route53
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[StateStart-0]
_ = x[StateQuoted-1]
_ = x[StateBackslash-2]
_ = x[StateQuotedBackslash-3]
_ = x[StateWantSpace-4]
_ = x[StateWantSpaceOrQuote-5]
}
const _State_name = "StateStartStateQuotedStateBackslashStateQuotedBackslashStateWantSpaceStateWantSpaceOrQuote"
var _State_index = [...]uint8{0, 10, 21, 35, 55, 69, 90}
func (i State) String() string {
if i < 0 || i >= State(len(_State_index)-1) {
return "State(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _State_name[_State_index[i]:_State_index[i+1]]
}

View File

@@ -0,0 +1,130 @@
//go:generate stringer -type=State
package route53
import (
"bytes"
"fmt"
"strings"
"github.com/StackExchange/dnscontrol/v4/pkg/printer"
)
type State int
const (
StateStart State = iota // Normal text
StateQuoted // Quoted text
StateBackslash // last char was backslash
StateQuotedBackslash // last char was backlash in a quoted string
StateWantSpace // expect space after closing quote
StateWantSpaceOrQuote // expect open quote after `" `
)
func isRemaining(s string, i, r int) bool {
return (len(s) - 1 - i) > r
}
// txtDecode is like strings.Fields except individual fields
// might be quoted using `"`.
func txtDecode(s string) (string, error) {
printer.Printf("DEBUG: route53 txt inboundv=%v\n", s)
// Parse according to RFC1035 zonefile specifications.
// "foo" -> one string: `foo``
// "foo" "bar" -> two strings: `foo` and `bar`
// if s == `""` {
// r := []string{}
// printer.Printf("DEBUG: route53 txt Z decodedv=%v\n", r)
// return r, nil
// }
b := &bytes.Buffer{}
state := StateStart
for i, c := range s {
//printer.Printf("DEBUG: state=%v rune=%v\n", state, string(c))
switch state {
case StateStart:
if c == '"' {
state = StateQuoted
} else if c == ' ' {
state = StateQuoted
} else if c == '\\' {
if isRemaining(s, i, 1) {
state = StateBackslash
} else {
return "", fmt.Errorf("txtDecode string ends with backslash q(%q)", s)
}
} else {
b.WriteRune(c)
}
case StateBackslash:
b.WriteRune(c)
state = StateStart
case StateQuoted:
if c == '\\' {
if isRemaining(s, i, 1) {
state = StateQuotedBackslash
} else {
return "", fmt.Errorf("txtDecode quoted string ends with backslash q(%q)", s)
}
} else if c == '"' {
state = StateWantSpace
} else {
b.WriteRune(c)
}
case StateQuotedBackslash:
b.WriteRune(c)
state = StateQuoted
case StateWantSpace:
if c == ' ' {
state = StateWantSpaceOrQuote
} else {
return "", fmt.Errorf("txtDecode expected whitespace after close quote q(%q)", s)
}
case StateWantSpaceOrQuote:
if c == ' ' {
state = StateWantSpaceOrQuote
} else if c == '"' {
state = StateQuoted
} else {
state = StateStart
b.WriteRune(c)
}
}
}
r := b.String()
printer.Printf("DEBUG: route53 txt decodedv=%v\n", r)
return r, nil
}
func txtEncode(ts []string) string {
printer.Printf("DEBUG: route53 txt outboundv=%v\n", ts)
if len(ts) == 0 {
t := `""`
printer.Printf("DEBUG: route53 txt Z encodedv=%v\n", t)
return t
}
for i := range ts {
ts[i] = strings.ReplaceAll(ts[i], `\`, `\\`)
ts[i] = strings.ReplaceAll(ts[i], `"`, `\"`)
}
t := `"` + strings.Join(ts, `" "`) + `"`
printer.Printf("DEBUG: route53 txt encodedv=%v\n", t)
return t
}

View File

@@ -0,0 +1,53 @@
package route53
import (
"strings"
"testing"
)
func r(s string, c int) string { return strings.Repeat(s, c) }
func TestTxtDecode(t *testing.T) {
tests := []struct {
data string
expected []string
}{
{`foo`, []string{`foo`}},
{`"foo"`, []string{`foo`}},
{`"foo bar"`, []string{`foo bar`}},
{`foo bar`, []string{`foo`, `bar`}},
{`"aaa" "bbb"`, []string{`aaa`, `bbb`}},
{`"a\"a" "bbb"`, []string{`a"a`, `bbb`}},
// Seen in live traffic:
{"\"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\"",
[]string{r("B", 254)}},
{"\"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\"",
[]string{r("C", 255)}},
{"\"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\" \"D\"",
[]string{r("D", 255), "D"}},
{"\"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\" \"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\"",
[]string{r("E", 255), r("E", 255)}},
{"\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" \"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\" \"F\"",
[]string{r("F", 255), r("F", 255), "F"}},
{"\"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\" \"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\" \"GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\"",
[]string{r("G", 255), r("G", 255), r("G", 255)}},
{"\"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\" \"H\"",
[]string{r("H", 255), r("H", 255), r("H", 255), "H"}},
{"\"quo'te\"", []string{`quo'te`}},
{"\"blah`blah\"", []string{"blah`blah"}},
{"\"quo\\\"te\"", []string{`quo"te`}},
{"\"q\\\"uo\\\"te\"", []string{`q"uo"te`}},
{"\"backs\\\\lash\"", []string{`backs\lash`}},
}
for i, test := range tests {
got, err := txtDecode(test.data)
if err != nil {
t.Error(err)
}
want := strings.Join(test.expected, "")
if got != want {
t.Errorf("%v: expected TxtStrings=(%q) got (%q)", i, want, got)
}
}
}