| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package base32 | |
| import ( | |
| "bytes" | |
| "io" | |
| "io/ioutil" | |
| "strings" | |
| "testing" | |
| ) | |
| type testpair struct { | |
| decoded, encoded string | |
| } | |
| var pairs = []testpair{ | |
| // RFC 4648 examples | |
| {"", ""}, | |
| {"f", "MY======"}, | |
| {"fo", "MZXQ===="}, | |
| {"foo", "MZXW6==="}, | |
| {"foob", "MZXW6YQ="}, | |
| {"fooba", "MZXW6YTB"}, | |
| {"foobar", "MZXW6YTBOI======"}, | |
| // Wikipedia examples, converted to base32 | |
| {"sure.", "ON2XEZJO"}, | |
| {"sure", "ON2XEZI="}, | |
| {"sur", "ON2XE==="}, | |
| {"su", "ON2Q===="}, | |
| {"leasure.", "NRSWC43VOJSS4==="}, | |
| {"easure.", "MVQXG5LSMUXA===="}, | |
| {"asure.", "MFZXK4TFFY======"}, | |
| {"sure.", "ON2XEZJO"}, | |
| } | |
| var bigtest = testpair{ | |
| "Twas brillig, and the slithy toves", | |
| "KR3WC4ZAMJZGS3DMNFTSYIDBNZSCA5DIMUQHG3DJORUHSIDUN53GK4Y=", | |
| } | |
| func testEqual(t *testing.T, msg string, args ...interface{}) bool { | |
| if args[len(args)-2] != args[len(args)-1] { | |
| t.Errorf(msg, args...) | |
| return false | |
| } | |
| return true | |
| } | |
| func TestEncode(t *testing.T) { | |
| for _, p := range pairs { | |
| got := StdEncoding.EncodeToString([]byte(p.decoded)) | |
| testEqual(t, "Encode(%q) = %q, want %q", p.decoded, got, p.encoded) | |
| } | |
| } | |
| func TestEncoder(t *testing.T) { | |
| for _, p := range pairs { | |
| bb := &bytes.Buffer{} | |
| encoder := NewEncoder(StdEncoding, bb) | |
| encoder.Write([]byte(p.decoded)) | |
| encoder.Close() | |
| testEqual(t, "Encode(%q) = %q, want %q", p.decoded, bb.String(), p.encoded) | |
| } | |
| } | |
| func TestEncoderBuffering(t *testing.T) { | |
| input := []byte(bigtest.decoded) | |
| for bs := 1; bs <= 12; bs++ { | |
| bb := &bytes.Buffer{} | |
| encoder := NewEncoder(StdEncoding, bb) | |
| for pos := 0; pos < len(input); pos += bs { | |
| end := pos + bs | |
| if end > len(input) { | |
| end = len(input) | |
| } | |
| n, err := encoder.Write(input[pos:end]) | |
| testEqual(t, "Write(%q) gave error %v, want %v", input[pos:end], err, error(nil)) | |
| testEqual(t, "Write(%q) gave length %v, want %v", input[pos:end], n, end-pos) | |
| } | |
| err := encoder.Close() | |
| testEqual(t, "Close gave error %v, want %v", err, error(nil)) | |
| testEqual(t, "Encoding/%d of %q = %q, want %q", bs, bigtest.decoded, bb.String(), bigtest.encoded) | |
| } | |
| } | |
| func TestDecode(t *testing.T) { | |
| for _, p := range pairs { | |
| dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) | |
| count, end, err := StdEncoding.decode(dbuf, []byte(p.encoded)) | |
| testEqual(t, "Decode(%q) = error %v, want %v", p.encoded, err, error(nil)) | |
| testEqual(t, "Decode(%q) = length %v, want %v", p.encoded, count, len(p.decoded)) | |
| if len(p.encoded) > 0 { | |
| testEqual(t, "Decode(%q) = end %v, want %v", p.encoded, end, (p.encoded[len(p.encoded)-1] == '=')) | |
| } | |
| testEqual(t, "Decode(%q) = %q, want %q", p.encoded, | |
| string(dbuf[0:count]), | |
| p.decoded) | |
| dbuf, err = StdEncoding.DecodeString(p.encoded) | |
| testEqual(t, "DecodeString(%q) = error %v, want %v", p.encoded, err, error(nil)) | |
| testEqual(t, "DecodeString(%q) = %q, want %q", p.encoded, string(dbuf), p.decoded) | |
| } | |
| } | |
| func TestDecoder(t *testing.T) { | |
| for _, p := range pairs { | |
| decoder := NewDecoder(StdEncoding, strings.NewReader(p.encoded)) | |
| dbuf := make([]byte, StdEncoding.DecodedLen(len(p.encoded))) | |
| count, err := decoder.Read(dbuf) | |
| if err != nil && err != io.EOF { | |
| t.Fatal("Read failed", err) | |
| } | |
| testEqual(t, "Read from %q = length %v, want %v", p.encoded, count, len(p.decoded)) | |
| testEqual(t, "Decoding of %q = %q, want %q", p.encoded, string(dbuf[0:count]), p.decoded) | |
| if err != io.EOF { | |
| count, err = decoder.Read(dbuf) | |
| } | |
| testEqual(t, "Read from %q = %v, want %v", p.encoded, err, io.EOF) | |
| } | |
| } | |
| func TestDecoderBuffering(t *testing.T) { | |
| for bs := 1; bs <= 12; bs++ { | |
| decoder := NewDecoder(StdEncoding, strings.NewReader(bigtest.encoded)) | |
| buf := make([]byte, len(bigtest.decoded)+12) | |
| var total int | |
| for total = 0; total < len(bigtest.decoded); { | |
| n, err := decoder.Read(buf[total : total+bs]) | |
| testEqual(t, "Read from %q at pos %d = %d, %v, want _, %v", bigtest.encoded, total, n, err, error(nil)) | |
| total += n | |
| } | |
| testEqual(t, "Decoding/%d of %q = %q, want %q", bs, bigtest.encoded, string(buf[0:total]), bigtest.decoded) | |
| } | |
| } | |
| func TestDecodeCorrupt(t *testing.T) { | |
| testCases := []struct { | |
| input string | |
| offset int // -1 means no corruption. | |
| }{ | |
| {"", -1}, | |
| {"!!!!", 0}, | |
| {"x===", 0}, | |
| {"AA=A====", 2}, | |
| {"AAA=AAAA", 3}, | |
| {"MMMMMMMMM", 8}, | |
| {"MMMMMM", 0}, | |
| {"A=", 1}, | |
| {"AA=", 3}, | |
| {"AA==", 4}, | |
| {"AA===", 5}, | |
| {"AAAA=", 5}, | |
| {"AAAA==", 6}, | |
| {"AAAAA=", 6}, | |
| {"AAAAA==", 7}, | |
| {"A=======", 1}, | |
| {"AA======", -1}, | |
| {"AAA=====", 3}, | |
| {"AAAA====", -1}, | |
| {"AAAAA===", -1}, | |
| {"AAAAAA==", 6}, | |
| {"AAAAAAA=", -1}, | |
| {"AAAAAAAA", -1}, | |
| } | |
| for _, tc := range testCases { | |
| dbuf := make([]byte, StdEncoding.DecodedLen(len(tc.input))) | |
| _, err := StdEncoding.Decode(dbuf, []byte(tc.input)) | |
| if tc.offset == -1 { | |
| if err != nil { | |
| t.Error("Decoder wrongly detected corruption in", tc.input) | |
| } | |
| continue | |
| } | |
| switch err := err.(type) { | |
| case CorruptInputError: | |
| testEqual(t, "Corruption in %q at offset %v, want %v", tc.input, int(err), tc.offset) | |
| default: | |
| t.Error("Decoder failed to detect corruption in", tc) | |
| } | |
| } | |
| } | |
| func TestBig(t *testing.T) { | |
| n := 3*1000 + 1 | |
| raw := make([]byte, n) | |
| const alpha = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
| for i := 0; i < n; i++ { | |
| raw[i] = alpha[i%len(alpha)] | |
| } | |
| encoded := new(bytes.Buffer) | |
| w := NewEncoder(StdEncoding, encoded) | |
| nn, err := w.Write(raw) | |
| if nn != n || err != nil { | |
| t.Fatalf("Encoder.Write(raw) = %d, %v want %d, nil", nn, err, n) | |
| } | |
| err = w.Close() | |
| if err != nil { | |
| t.Fatalf("Encoder.Close() = %v want nil", err) | |
| } | |
| decoded, err := ioutil.ReadAll(NewDecoder(StdEncoding, encoded)) | |
| if err != nil { | |
| t.Fatalf("ioutil.ReadAll(NewDecoder(...)): %v", err) | |
| } | |
| if !bytes.Equal(raw, decoded) { | |
| var i int | |
| for i = 0; i < len(decoded) && i < len(raw); i++ { | |
| if decoded[i] != raw[i] { | |
| break | |
| } | |
| } | |
| t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i) | |
| } | |
| } | |
| func testStringEncoding(t *testing.T, expected string, examples []string) { | |
| for _, e := range examples { | |
| buf, err := StdEncoding.DecodeString(e) | |
| if err != nil { | |
| t.Errorf("Decode(%q) failed: %v", e, err) | |
| continue | |
| } | |
| if s := string(buf); s != expected { | |
| t.Errorf("Decode(%q) = %q, want %q", e, s, expected) | |
| } | |
| } | |
| } | |
| func TestNewLineCharacters(t *testing.T) { | |
| // Each of these should decode to the string "sure", without errors. | |
| examples := []string{ | |
| "ON2XEZI=", | |
| "ON2XEZI=\r", | |
| "ON2XEZI=\n", | |
| "ON2XEZI=\r\n", | |
| "ON2XEZ\r\nI=", | |
| "ON2X\rEZ\nI=", | |
| "ON2X\nEZ\rI=", | |
| "ON2XEZ\nI=", | |
| "ON2XEZI\n=", | |
| } | |
| testStringEncoding(t, "sure", examples) | |
| // Each of these should decode to the string "foobar", without errors. | |
| examples = []string{ | |
| "MZXW6YTBOI======", | |
| "MZXW6YTBOI=\r\n=====", | |
| } | |
| testStringEncoding(t, "foobar", examples) | |
| } | |
| func TestDecoderIssue4779(t *testing.T) { | |
| encoded := `JRXXEZLNEBUXA43VNUQGI33MN5ZCA43JOQQGC3LFOQWCAY3PNZZWKY3UMV2HK4 | |
| RAMFSGS4DJONUWG2LOM4QGK3DJOQWCA43FMQQGI3YKMVUXK43NN5SCA5DFNVYG64RANFXGG2LENFSH | |
| K3TUEB2XIIDMMFRG64TFEBSXIIDEN5WG64TFEBWWCZ3OMEQGC3DJOF2WCLRAKV2CAZLONFWQUYLEEB | |
| WWS3TJNUQHMZLONFQW2LBAOF2WS4ZANZXXG5DSOVSCAZLYMVZGG2LUMF2GS33OEB2WY3DBNVRW6IDM | |
| MFRG64TJOMQG42LTNEQHK5AKMFWGS4LVNFYCAZLYEBSWCIDDN5WW233EN4QGG33OONSXC5LBOQXCAR | |
| DVNFZSAYLVORSSA2LSOVZGKIDEN5WG64RANFXAU4TFOBZGK2DFNZSGK4TJOQQGS3RAOZXWY5LQORQX | |
| IZJAOZSWY2LUEBSXG43FEBRWS3DMOVWSAZDPNRXXEZJAMV2SAZTVM5UWC5BANZ2WY3DBBJYGC4TJMF | |
| 2HK4ROEBCXQY3FOB2GK5LSEBZWS3TUEBXWGY3BMVRWC5BAMN2XA2LEMF2GC5BANZXW4IDQOJXWSZDF | |
| NZ2CYIDTOVXHIIDJNYFGG5LMOBQSA4LVNEQG6ZTGNFRWSYJAMRSXGZLSOVXHIIDNN5WGY2LUEBQW42 | |
| LNEBUWIIDFON2CA3DBMJXXE5LNFY== | |
| ====` | |
| encodedShort := strings.Replace(encoded, "\n", "", -1) | |
| dec := NewDecoder(StdEncoding, strings.NewReader(encoded)) | |
| res1, err := ioutil.ReadAll(dec) | |
| if err != nil { | |
| t.Errorf("ReadAll failed: %v", err) | |
| } | |
| dec = NewDecoder(StdEncoding, strings.NewReader(encodedShort)) | |
| var res2 []byte | |
| res2, err = ioutil.ReadAll(dec) | |
| if err != nil { | |
| t.Errorf("ReadAll failed: %v", err) | |
| } | |
| if !bytes.Equal(res1, res2) { | |
| t.Error("Decoded results not equal") | |
| } | |
| } | |
| func BenchmarkEncodeToString(b *testing.B) { | |
| data := make([]byte, 8192) | |
| b.SetBytes(int64(len(data))) | |
| for i := 0; i < b.N; i++ { | |
| StdEncoding.EncodeToString(data) | |
| } | |
| } | |
| func BenchmarkDecodeString(b *testing.B) { | |
| data := StdEncoding.EncodeToString(make([]byte, 8192)) | |
| b.SetBytes(int64(len(data))) | |
| for i := 0; i < b.N; i++ { | |
| StdEncoding.DecodeString(data) | |
| } | |
| } |