stevenbooru/vendor/src/github.com/vincent-petithory/dataurl/dataurl_test.go

588 lines
14 KiB
Go

package dataurl
import (
"bytes"
"encoding/base64"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"regexp"
"strings"
"testing"
)
type dataURLTest struct {
InputRawDataURL string
ExpectedItems []item
ExpectedDataURL DataURL
}
func genTestTable() []dataURLTest {
return []dataURLTest{
dataURLTest{
`data:;base64,aGV5YQ==`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemParamSemicolon, ";"},
item{itemBase64Enc, "base64"},
item{itemDataComma, ","},
item{itemData, "aGV5YQ=="},
item{itemEOF, ""},
},
DataURL{
defaultMediaType(),
EncodingBase64,
[]byte("heya"),
},
},
dataURLTest{
`data:text/plain;base64,aGV5YQ==`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemMediaType, "text"},
item{itemMediaSep, "/"},
item{itemMediaSubType, "plain"},
item{itemParamSemicolon, ";"},
item{itemBase64Enc, "base64"},
item{itemDataComma, ","},
item{itemData, "aGV5YQ=="},
item{itemEOF, ""},
},
DataURL{
MediaType{
"text",
"plain",
map[string]string{},
},
EncodingBase64,
[]byte("heya"),
},
},
dataURLTest{
`data:text/plain;charset=utf-8;base64,aGV5YQ==`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemMediaType, "text"},
item{itemMediaSep, "/"},
item{itemMediaSubType, "plain"},
item{itemParamSemicolon, ";"},
item{itemParamAttr, "charset"},
item{itemParamEqual, "="},
item{itemParamVal, "utf-8"},
item{itemParamSemicolon, ";"},
item{itemBase64Enc, "base64"},
item{itemDataComma, ","},
item{itemData, "aGV5YQ=="},
item{itemEOF, ""},
},
DataURL{
MediaType{
"text",
"plain",
map[string]string{
"charset": "utf-8",
},
},
EncodingBase64,
[]byte("heya"),
},
},
dataURLTest{
`data:text/plain;charset=utf-8;foo=bar;base64,aGV5YQ==`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemMediaType, "text"},
item{itemMediaSep, "/"},
item{itemMediaSubType, "plain"},
item{itemParamSemicolon, ";"},
item{itemParamAttr, "charset"},
item{itemParamEqual, "="},
item{itemParamVal, "utf-8"},
item{itemParamSemicolon, ";"},
item{itemParamAttr, "foo"},
item{itemParamEqual, "="},
item{itemParamVal, "bar"},
item{itemParamSemicolon, ";"},
item{itemBase64Enc, "base64"},
item{itemDataComma, ","},
item{itemData, "aGV5YQ=="},
item{itemEOF, ""},
},
DataURL{
MediaType{
"text",
"plain",
map[string]string{
"charset": "utf-8",
"foo": "bar",
},
},
EncodingBase64,
[]byte("heya"),
},
},
dataURLTest{
`data:application/json;charset=utf-8;foo="b\"<@>\"r";style=unformatted%20json;base64,eyJtc2ciOiAiaGV5YSJ9`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemMediaType, "application"},
item{itemMediaSep, "/"},
item{itemMediaSubType, "json"},
item{itemParamSemicolon, ";"},
item{itemParamAttr, "charset"},
item{itemParamEqual, "="},
item{itemParamVal, "utf-8"},
item{itemParamSemicolon, ";"},
item{itemParamAttr, "foo"},
item{itemParamEqual, "="},
item{itemLeftStringQuote, "\""},
item{itemParamVal, `b\"<@>\"r`},
item{itemRightStringQuote, "\""},
item{itemParamSemicolon, ";"},
item{itemParamAttr, "style"},
item{itemParamEqual, "="},
item{itemParamVal, "unformatted%20json"},
item{itemParamSemicolon, ";"},
item{itemBase64Enc, "base64"},
item{itemDataComma, ","},
item{itemData, "eyJtc2ciOiAiaGV5YSJ9"},
item{itemEOF, ""},
},
DataURL{
MediaType{
"application",
"json",
map[string]string{
"charset": "utf-8",
"foo": `b"<@>"r`,
"style": "unformatted json",
},
},
EncodingBase64,
[]byte(`{"msg": "heya"}`),
},
},
dataURLTest{
`data:xxx;base64,aGV5YQ==`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemError, "invalid character for media type"},
},
DataURL{},
},
dataURLTest{
`data:,`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemDataComma, ","},
item{itemEOF, ""},
},
DataURL{
defaultMediaType(),
EncodingASCII,
[]byte(""),
},
},
dataURLTest{
`data:,A%20brief%20note`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemDataComma, ","},
item{itemData, "A%20brief%20note"},
item{itemEOF, ""},
},
DataURL{
defaultMediaType(),
EncodingASCII,
[]byte("A brief note"),
},
},
dataURLTest{
`data:image/svg+xml-im.a.fake;base64,cGllLXN0b2NrX1RoaXJ0eQ==`,
[]item{
item{itemDataPrefix, dataPrefix},
item{itemMediaType, "image"},
item{itemMediaSep, "/"},
item{itemMediaSubType, "svg+xml-im.a.fake"},
item{itemParamSemicolon, ";"},
item{itemBase64Enc, "base64"},
item{itemDataComma, ","},
item{itemData, "cGllLXN0b2NrX1RoaXJ0eQ=="},
item{itemEOF, ""},
},
DataURL{
MediaType{
"image",
"svg+xml-im.a.fake",
map[string]string{},
},
EncodingBase64,
[]byte("pie-stock_Thirty"),
},
},
}
}
func expectItems(expected, actual []item) bool {
if len(expected) != len(actual) {
return false
}
for i := range expected {
if expected[i].t != actual[i].t {
return false
}
if expected[i].val != actual[i].val {
return false
}
}
return true
}
func equal(du1, du2 *DataURL) (bool, error) {
if !reflect.DeepEqual(du1.MediaType, du2.MediaType) {
return false, nil
}
if du1.Encoding != du2.Encoding {
return false, nil
}
if du1.Data == nil || du2.Data == nil {
return false, fmt.Errorf("nil Data")
}
if !bytes.Equal(du1.Data, du2.Data) {
return false, nil
}
return true, nil
}
func TestLexDataURLs(t *testing.T) {
for _, test := range genTestTable() {
l := lex(test.InputRawDataURL)
var items []item
for item := range l.items {
items = append(items, item)
}
if !expectItems(test.ExpectedItems, items) {
t.Errorf("Expected %v, got %v", test.ExpectedItems, items)
}
}
}
func testDataURLs(t *testing.T, factory func(string) (*DataURL, error)) {
for _, test := range genTestTable() {
var expectedItemError string
for _, item := range test.ExpectedItems {
if item.t == itemError {
expectedItemError = item.String()
break
}
}
dataURL, err := factory(test.InputRawDataURL)
if expectedItemError == "" && err != nil {
t.Error(err)
continue
} else if expectedItemError != "" && err == nil {
t.Errorf("Expected error \"%s\", got nil", expectedItemError)
continue
} else if expectedItemError != "" && err != nil {
if err.Error() != expectedItemError {
t.Errorf("Expected error \"%s\", got \"%s\"", expectedItemError, err.Error())
}
continue
}
if ok, err := equal(dataURL, &test.ExpectedDataURL); err != nil {
t.Error(err)
} else if !ok {
t.Errorf("Expected %v, got %v", test.ExpectedDataURL, *dataURL)
}
}
}
func TestDataURLsWithDecode(t *testing.T) {
testDataURLs(t, func(s string) (*DataURL, error) {
return Decode(strings.NewReader(s))
})
}
func TestDataURLsWithDecodeString(t *testing.T) {
testDataURLs(t, func(s string) (*DataURL, error) {
return DecodeString(s)
})
}
func TestDataURLsWithUnmarshalText(t *testing.T) {
testDataURLs(t, func(s string) (*DataURL, error) {
d := &DataURL{}
err := d.UnmarshalText([]byte(s))
return d, err
})
}
func TestRoundTrip(t *testing.T) {
tests := []struct {
s string
roundTripOk bool
}{
{`data:text/plain;charset=utf-8;foo=bar;base64,aGV5YQ==`, true},
{`data:;charset=utf-8;foo=bar;base64,aGV5YQ==`, false},
{`data:text/plain;charset=utf-8;foo="bar";base64,aGV5YQ==`, false},
{`data:text/plain;charset=utf-8;foo="bar",A%20brief%20note`, false},
{`data:text/plain;charset=utf-8;foo=bar,A%20brief%20note`, true},
}
for _, test := range tests {
dataURL, err := DecodeString(test.s)
if err != nil {
t.Error(err)
continue
}
dus := dataURL.String()
if test.roundTripOk && dus != test.s {
t.Errorf("Expected %s, got %s", test.s, dus)
} else if !test.roundTripOk && dus == test.s {
t.Errorf("Found %s, expected something else", test.s)
}
txt, err := dataURL.MarshalText()
if err != nil {
t.Error(err)
continue
}
if test.roundTripOk && string(txt) != test.s {
t.Errorf("MarshalText roundtrip: got '%s', want '%s'", txt, test.s)
} else if !test.roundTripOk && string(txt) == test.s {
t.Errorf("MarshalText roundtrip: got '%s', want something else", txt)
}
}
}
func TestNew(t *testing.T) {
tests := []struct {
Data []byte
MediaType string
ParamPairs []string
WillPanic bool
ExpectedDataURL *DataURL
}{
{
[]byte(`{"msg": "heya"}`),
"application/json",
[]string{},
false,
&DataURL{
MediaType{
"application",
"json",
map[string]string{},
},
EncodingBase64,
[]byte(`{"msg": "heya"}`),
},
},
{
[]byte(``),
"application//json",
[]string{},
true,
nil,
},
{
[]byte(``),
"",
[]string{},
true,
nil,
},
{
[]byte(`{"msg": "heya"}`),
"text/plain",
[]string{"charset", "utf-8"},
false,
&DataURL{
MediaType{
"text",
"plain",
map[string]string{
"charset": "utf-8",
},
},
EncodingBase64,
[]byte(`{"msg": "heya"}`),
},
},
{
[]byte(`{"msg": "heya"}`),
"text/plain",
[]string{"charset", "utf-8", "name"},
true,
nil,
},
}
for _, test := range tests {
var dataURL *DataURL
func() {
defer func() {
if test.WillPanic {
if e := recover(); e == nil {
t.Error("Expected panic didn't happen")
}
} else {
if e := recover(); e != nil {
t.Errorf("Unexpected panic: %v", e)
}
}
}()
dataURL = New(test.Data, test.MediaType, test.ParamPairs...)
}()
if test.WillPanic {
if dataURL != nil {
t.Error("Expected nil DataURL")
}
} else {
if ok, err := equal(dataURL, test.ExpectedDataURL); err != nil {
t.Error(err)
} else if !ok {
t.Errorf("Expected %v, got %v", test.ExpectedDataURL, *dataURL)
}
}
}
}
var golangFavicon = strings.Replace(`AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAD///8AVE44//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb/
/uF2/1ROOP////8A////AFROOP/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+
4Xb//uF2//7hdv9UTjj/////AP///wBUTjj//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7h
dv/+4Xb//uF2//7hdv/+4Xb/VE44/////wD///8AVE44//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2
//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2/1ROOP////8A////AFROOP/+4Xb//uF2//7hdv/+4Xb/
/uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv9UTjj/////AP///wBUTjj//uF2//7hdv/+
4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb//uF2//7hdv/+4Xb/VE44/////wD///8AVE44//7h
dv/+4Xb//uF2//7hdv/+4Xb/z7t5/8Kyev/+4Xb//993///dd///3Xf//uF2/1ROOP////8A////
AFROOP/+4Xb//uF2//7hdv//4Hn/dIzD//v8///7/P//dIzD//7hdv//3Xf//913//7hdv9UTjj/
////AP///wBUTjj//uF2///fd//+4Xb//uF2/6ajif90jMP/dIzD/46Zpv/+4Xb//+F1///feP/+
4Xb/VE44/////wD///8AVE44//7hdv/z1XT////////////Is3L/HyAj/x8gI//Is3L/////////
///z1XT//uF2/1ROOP////8A19nd/1ROOP/+4Xb/5+HS//v+//8RExf/Liwn//7hdv/+4Xb/5+HS
//v8//8RExf/Liwn//7hdv9UTjj/19nd/1ROOP94aDT/yKdO/+fh0v//////ERMX/y4sJ//+4Xb/
/uF2/+fh0v//////ERMX/y4sJ//Ip07/dWU3/1ROOP9UTjj/yKdO/6qSSP/Is3L/9fb7//f6///I
s3L//uF2//7hdv/Is3L////////////Is3L/qpJI/8inTv9UTjj/19nd/1ROOP97c07/qpJI/8in
Tv/Ip07//uF2//7hdv/+4Xb//uF2/8zBlv/Kv4//pZJU/3tzTv9UTjj/19nd/////wD///8A4eLl
/6CcjP97c07/e3NO/1dOMf9BOiX/TkUn/2VXLf97c07/e3NO/6CcjP/h4uX/////AP///wD///8A
////AP///wD///8A////AP///wDq6/H/3N/j/9fZ3f/q6/H/////AP///wD///8A////AP///wD/
//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAA==`, "\n", "", -1)
func TestEncodeBytes(t *testing.T) {
mustDecode := func(s string) []byte {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil {
panic(err)
}
return data
}
tests := []struct {
Data []byte
ExpectedString string
}{
{
[]byte(`A brief note`),
"data:text/plain;charset=utf-8;base64,QSBicmllZiBub3Rl",
},
{
[]byte{0xA, 0xFF, 0x99, 0x34, 0x56, 0x34, 0x00},
`data:application/octet-stream;base64,Cv+ZNFY0AA==`,
},
{
mustDecode(golangFavicon),
`data:image/vnd.microsoft.icon;base64,` + golangFavicon,
},
}
for _, test := range tests {
str := EncodeBytes(test.Data)
if str != test.ExpectedString {
t.Errorf("Expected %s, got %s", test.ExpectedString, str)
}
}
}
func BenchmarkLex(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, test := range genTestTable() {
l := lex(test.InputRawDataURL)
for _ = range l.items {
}
}
}
}
const rep = `^data:(?P<mediatype>\w+/[\w\+\-\.]+)?(?P<parameter>(?:;[\w\-]+="?[\w\-\\<>@,";:%]*"?)+)?(?P<base64>;base64)?,(?P<data>.*)$`
func TestRegexp(t *testing.T) {
re, err := regexp.Compile(rep)
if err != nil {
t.Fatal(err)
}
for _, test := range genTestTable() {
shouldMatch := true
for _, item := range test.ExpectedItems {
if item.t == itemError {
shouldMatch = false
break
}
}
// just test it matches, do not parse
if re.MatchString(test.InputRawDataURL) && !shouldMatch {
t.Error("doesn't match", test.InputRawDataURL)
} else if !re.MatchString(test.InputRawDataURL) && shouldMatch {
t.Error("match", test.InputRawDataURL)
}
}
}
func BenchmarkRegexp(b *testing.B) {
re, err := regexp.Compile(rep)
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
for _, test := range genTestTable() {
_ = re.FindStringSubmatch(test.InputRawDataURL)
}
}
}
func ExampleDecodeString() {
dataURL, err := DecodeString(`data:text/plain;charset=utf-8;base64,aGV5YQ==`)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s, %s", dataURL.MediaType.ContentType(), string(dataURL.Data))
// Output: text/plain, heya
}
func ExampleDecode() {
r, err := http.NewRequest(
"POST", "/",
strings.NewReader(`data:image/vnd.microsoft.icon;name=golang%20favicon;base64,`+golangFavicon),
)
if err != nil {
fmt.Println(err)
return
}
var dataURL *DataURL
h := func(w http.ResponseWriter, r *http.Request) {
var err error
dataURL, err = Decode(r.Body)
defer r.Body.Close()
if err != nil {
fmt.Println(err)
}
}
w := httptest.NewRecorder()
h(w, r)
fmt.Printf("%s: %s", dataURL.Params["name"], dataURL.ContentType())
// Output: golang favicon: image/vnd.microsoft.icon
}