Merge branch 'issue/add_test'
This commit is contained in:
20
pop3.go
20
pop3.go
@@ -113,7 +113,7 @@ func (c *Client) ListAll() ([]MessageInfo, error) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ResponseError(fmt.Sprintf("LISTコマンドのレスポンスが不正です。: %s", err.Error()))
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return list, nil
|
return list, nil
|
||||||
@@ -144,7 +144,7 @@ func (c *Client) Uidl(number int) (int, string, error) {
|
|||||||
val, uid, err = c.convertNumberAndUid(msg)
|
val, uid, err = c.convertNumberAndUid(msg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, "", ResponseError(fmt.Sprintf("UIDLコマンドのレスポンスが不正です。: %s", err.Error()))
|
return 0, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return val, uid, nil
|
return val, uid, nil
|
||||||
@@ -168,7 +168,7 @@ func (c *Client) UidlAll() ([]MessageInfo, error) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ResponseError(fmt.Sprintf("UIDLコマンドのレスポンスが不正です。: %s", err.Error()))
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return list, nil
|
return list, nil
|
||||||
@@ -232,7 +232,7 @@ func (c *Client) cmdStatOrList(name, format string, args ...interface{}) (int, u
|
|||||||
s := strings.Split(msg, " ")
|
s := strings.Split(msg, " ")
|
||||||
|
|
||||||
if len(s) < 2 {
|
if len(s) < 2 {
|
||||||
return 0, 0, ResponseError(fmt.Sprintf("%sコマンドのレスポンスが不正です。: %s", name, msg))
|
return 0, 0, ResponseError(fmt.Sprintf("invalid response format: %s", msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
var val int
|
var val int
|
||||||
@@ -241,7 +241,7 @@ func (c *Client) cmdStatOrList(name, format string, args ...interface{}) (int, u
|
|||||||
val, size, err = c.convertNumberAndSize(msg)
|
val, size, err = c.convertNumberAndSize(msg)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, ResponseError(fmt.Sprintf("%sコマンドのレスポンスが不正です。: %s", name, err.Error()))
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return val, size, nil
|
return val, size, nil
|
||||||
@@ -293,18 +293,18 @@ func (c *Client) convertNumberAndSize(line string) (int, uint64, error) {
|
|||||||
s := strings.Split(line, " ")
|
s := strings.Split(line, " ")
|
||||||
|
|
||||||
if len(s) < 2 {
|
if len(s) < 2 {
|
||||||
return 0, 0, errors.New(fmt.Sprintf("分割後の配列数が2未満です。: %s", line))
|
return 0, 0, errors.New(fmt.Sprintf("the length of the array is less than 2: %s", line))
|
||||||
}
|
}
|
||||||
|
|
||||||
var val int
|
var val int
|
||||||
var size uint64
|
var size uint64
|
||||||
|
|
||||||
if val, err = strconv.Atoi(s[0]); err != nil {
|
if val, err = strconv.Atoi(s[0]); err != nil {
|
||||||
return 0, 0, errors.New(fmt.Sprintf("配列要素[0]をint型に変換できません。: %s", line))
|
return 0, 0, errors.New(fmt.Sprintf("can not convert element[0] to int type: %s", line))
|
||||||
}
|
}
|
||||||
|
|
||||||
if size, err = strconv.ParseUint(s[1], 10, 64); err != nil {
|
if size, err = strconv.ParseUint(s[1], 10, 64); err != nil {
|
||||||
return 0, 0, errors.New(fmt.Sprintf("配列要素[1]をuint64型に変換できません。: %s", line))
|
return 0, 0, errors.New(fmt.Sprintf("can not convert element[1] to uint64 type: %s", line))
|
||||||
}
|
}
|
||||||
|
|
||||||
return val, size, nil
|
return val, size, nil
|
||||||
@@ -316,13 +316,13 @@ func (c *Client) convertNumberAndUid(line string) (int, string, error) {
|
|||||||
s := strings.Split(line, " ")
|
s := strings.Split(line, " ")
|
||||||
|
|
||||||
if len(s) < 2 {
|
if len(s) < 2 {
|
||||||
return 0, "", errors.New(fmt.Sprintf("分割後の配列数が2未満です。: %s", line))
|
return 0, "", errors.New(fmt.Sprintf("the length of the array is less than 2: %s", line))
|
||||||
}
|
}
|
||||||
|
|
||||||
var val int
|
var val int
|
||||||
|
|
||||||
if val, err = strconv.Atoi(s[0]); err != nil {
|
if val, err = strconv.Atoi(s[0]); err != nil {
|
||||||
return 0, "", errors.New(fmt.Sprintf("配列要素[0]をint型に変換できません。: %s", line))
|
return 0, "", errors.New(fmt.Sprintf("can not convert element[0] to int type: %s", line))
|
||||||
}
|
}
|
||||||
|
|
||||||
return val, s[1], nil
|
return val, s[1], nil
|
||||||
|
|||||||
302
pop3_test.go
Normal file
302
pop3_test.go
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
package pop3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSingleLineResponse(t *testing.T) {
|
||||||
|
if len(helloServer) != len(helloClient) {
|
||||||
|
t.Fatalf("Hello server and client size mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(helloServer); i++ {
|
||||||
|
execute(t, helloServer[i], helloClient[i], func(t *testing.T, c *Client) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch i {
|
||||||
|
case 0:
|
||||||
|
err = c.User("testuser")
|
||||||
|
case 1:
|
||||||
|
err = c.Pass("testpassword")
|
||||||
|
case 2:
|
||||||
|
var count int
|
||||||
|
var size uint64
|
||||||
|
count, size, err = c.Stat()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if count != 6 {
|
||||||
|
t.Errorf("Stat count:\nexpected:\n[%d]\nactual:\n[%d]\n", 6, count)
|
||||||
|
}
|
||||||
|
if size != 78680 {
|
||||||
|
t.Errorf("Stat size:\nexpected:\n[%d]\nactual:\n[%d]\n", 78680, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
//_, err = c.Retr(1)
|
||||||
|
case 4:
|
||||||
|
var number int
|
||||||
|
var size uint64
|
||||||
|
number, size, err = c.List(1)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if number != 1 {
|
||||||
|
t.Errorf("List number:\nexpected:\n[%d]\nactual:\n[%d]\n", 1, number)
|
||||||
|
}
|
||||||
|
if size != 4404 {
|
||||||
|
t.Errorf("List size:\nexpected:\n[%d]\nactual:\n[%d]\n", 4404, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
//_, err = c.ListAll()
|
||||||
|
case 6:
|
||||||
|
var number int
|
||||||
|
var uid string
|
||||||
|
number, uid, err = c.Uidl(1)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if number != 1 {
|
||||||
|
t.Errorf("Uidl number:\nexpected:\n[%d]\nactual:\n[%d]\n", 1, number)
|
||||||
|
}
|
||||||
|
if uid != "DJzjbtr5hb2Lefq5Ass6eMjtEBV" {
|
||||||
|
t.Errorf("Uidl uid:\nexpected:\n[%s]\nactual:\n[%s]\n", "DJzjbtr5hb2Lefq5Ass6eMjtEBV", uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 7:
|
||||||
|
//_, err = c.UidlAll()
|
||||||
|
case 8:
|
||||||
|
err = c.Dele(1)
|
||||||
|
case 9:
|
||||||
|
err = c.Noop()
|
||||||
|
case 10:
|
||||||
|
err = c.Rset()
|
||||||
|
case 11:
|
||||||
|
err = c.Quit()
|
||||||
|
default:
|
||||||
|
t.Fatalf("Unhandled command")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Command %d failed: %v", i, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetr(t *testing.T) {
|
||||||
|
execute(t, retrServer, retrClient, func(t *testing.T, c *Client) {
|
||||||
|
var err error
|
||||||
|
var content string
|
||||||
|
|
||||||
|
content, err = c.Retr(1)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Command failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `Date: Sat, 15 Feb 2014 13:49:28 +0900 (JST)
|
||||||
|
From: <from@example.com>
|
||||||
|
Subject: test mail
|
||||||
|
To: to@example.com
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
This is a test mail.`
|
||||||
|
|
||||||
|
expected = strings.Replace(expected, "\n", "\r\n", -1)
|
||||||
|
|
||||||
|
if content != expected {
|
||||||
|
t.Fatalf("excepted:\n[%s]\nactual:\n[%s]\n", expected, content)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListAll(t *testing.T) {
|
||||||
|
execute(t, listAllServer, listAllClient, func(t *testing.T, c *Client) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
exceptedMis := []MessageInfo{
|
||||||
|
MessageInfo{Number: 1, Size: 4404},
|
||||||
|
MessageInfo{Number: 2, Size: 3921},
|
||||||
|
MessageInfo{Number: 3, Size: 6646},
|
||||||
|
MessageInfo{Number: 4, Size: 55344},
|
||||||
|
MessageInfo{Number: 5, Size: 5376},
|
||||||
|
MessageInfo{Number: 6, Size: 2989},
|
||||||
|
}
|
||||||
|
|
||||||
|
var actualMis []MessageInfo
|
||||||
|
|
||||||
|
actualMis, err = c.ListAll()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Command failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, mi := range actualMis {
|
||||||
|
if mi.Number != exceptedMis[i].Number {
|
||||||
|
t.Fatalf("%d: Number\nexcepted:\n[%d]\nactual:\n[%d]\n", i, exceptedMis[i].Number, mi.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mi.Size != exceptedMis[i].Size {
|
||||||
|
t.Fatalf("%d: Size\nexcepted:\n[%d]\nactual:\n[%d]\n", i, exceptedMis[i].Size, mi.Size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUidlAll(t *testing.T) {
|
||||||
|
execute(t, uidlAllServer, uidlAllClient, func(t *testing.T, c *Client) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
exceptedMis := []MessageInfo{
|
||||||
|
MessageInfo{Number: 1, Uid: "DJzjbtr5hb2Lefq5Ass6eMjtEBV"},
|
||||||
|
MessageInfo{Number: 2, Uid: "IeHR1LtUfd5MI1gz1sYOU4TG1rx"},
|
||||||
|
MessageInfo{Number: 3, Uid: "Gjx3lbdNAnIMCJCuQxL05DFCqyy"},
|
||||||
|
MessageInfo{Number: 4, Uid: "DANJyGBstEYirvbBVFXSh3CGXAg"},
|
||||||
|
MessageInfo{Number: 5, Uid: "uhNSJrPJBlcacoEbX9aXMUKC90n"},
|
||||||
|
MessageInfo{Number: 6, Uid: "fZ88gZjY2NdYOQA6aij8dxCCifC"},
|
||||||
|
}
|
||||||
|
|
||||||
|
var actualMis []MessageInfo
|
||||||
|
|
||||||
|
actualMis, err = c.UidlAll()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Command failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, mi := range actualMis {
|
||||||
|
if mi.Number != exceptedMis[i].Number {
|
||||||
|
t.Fatalf("%d: Number\nexcepted:\n[%d]\nactual:\n[%d]\n", i, exceptedMis[i].Number, mi.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mi.Uid != exceptedMis[i].Uid {
|
||||||
|
t.Fatalf("%d: Uid\nexcepted:\n[%s]\nactual:\n[%s]\n", i, exceptedMis[i].Uid, mi.Uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute(t *testing.T, sServer, sClient string, processFn processFunc) {
|
||||||
|
server := strings.Join(strings.Split(baseHelloServer+sServer, "\n"), "\r\n")
|
||||||
|
client := strings.Join(strings.Split(baseHelloClient+sClient, "\n"), "\r\n")
|
||||||
|
|
||||||
|
var cmdbuf bytes.Buffer
|
||||||
|
|
||||||
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
||||||
|
|
||||||
|
var fake faker
|
||||||
|
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
||||||
|
|
||||||
|
c, err := NewClient(fake)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewClient: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
processFn(t, c)
|
||||||
|
|
||||||
|
bcmdbuf.Flush()
|
||||||
|
|
||||||
|
actualcmds := cmdbuf.String()
|
||||||
|
|
||||||
|
if client != actualcmds {
|
||||||
|
t.Errorf("Got:\n[%s]\nExpected:\n[%s]", actualcmds, client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type processFunc func(t *testing.T, c *Client)
|
||||||
|
|
||||||
|
var baseHelloServer = `+OK hello from popgate(2.35.25)
|
||||||
|
`
|
||||||
|
|
||||||
|
var helloServer = []string{
|
||||||
|
"+OK password required.\n",
|
||||||
|
"+OK maildrop ready, 6 messages (78680 octets)\n",
|
||||||
|
"+OK 6 78680\n",
|
||||||
|
"+OK 4404 octets\n",
|
||||||
|
"+OK 1 4404\n",
|
||||||
|
"+OK 6 messages (78680 octets)\n",
|
||||||
|
"+OK 1 DJzjbtr5hb2Lefq5Ass6eMjtEBV\n",
|
||||||
|
"+OK 6 messages (78680 octets)\n",
|
||||||
|
"+OK message 1 marked deleted\n",
|
||||||
|
"+OK \n",
|
||||||
|
"+OK maildrop has 0 messages (0 octets)\n",
|
||||||
|
"+OK server signing off.\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseHelloClient = ``
|
||||||
|
|
||||||
|
var helloClient = []string{
|
||||||
|
"USER testuser\n",
|
||||||
|
"PASS testpassword\n",
|
||||||
|
"STAT\n",
|
||||||
|
"", //"RETR 1\n",
|
||||||
|
"LIST 1\n",
|
||||||
|
"", //"LIST\n",
|
||||||
|
"UIDL 1\n",
|
||||||
|
"", //"UIDL\n",
|
||||||
|
"DELE 1\n",
|
||||||
|
"NOOP\n",
|
||||||
|
"RSET\n",
|
||||||
|
"QUIT\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
var retrServer = `+OK 3970 octets
|
||||||
|
Date: Sat, 15 Feb 2014 13:49:28 +0900 (JST)
|
||||||
|
From: <from@example.com>
|
||||||
|
Subject: test mail
|
||||||
|
To: to@example.com
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=utf-8
|
||||||
|
Content-Transfer-Encoding: quoted-printable
|
||||||
|
|
||||||
|
This is a test mail.
|
||||||
|
.
|
||||||
|
`
|
||||||
|
|
||||||
|
var retrClient = `RETR 1
|
||||||
|
`
|
||||||
|
|
||||||
|
var listAllServer = `+OK 6 messages (78680 octets)
|
||||||
|
1 4404
|
||||||
|
2 3921
|
||||||
|
3 6646
|
||||||
|
4 55344
|
||||||
|
5 5376
|
||||||
|
6 2989
|
||||||
|
.
|
||||||
|
`
|
||||||
|
var listAllClient = `LIST
|
||||||
|
`
|
||||||
|
|
||||||
|
var uidlAllServer = `+OK 6 messages (78680 octets)
|
||||||
|
1 DJzjbtr5hb2Lefq5Ass6eMjtEBV
|
||||||
|
2 IeHR1LtUfd5MI1gz1sYOU4TG1rx
|
||||||
|
3 Gjx3lbdNAnIMCJCuQxL05DFCqyy
|
||||||
|
4 DANJyGBstEYirvbBVFXSh3CGXAg
|
||||||
|
5 uhNSJrPJBlcacoEbX9aXMUKC90n
|
||||||
|
6 fZ88gZjY2NdYOQA6aij8dxCCifC
|
||||||
|
.
|
||||||
|
`
|
||||||
|
var uidlAllClient = `UIDL
|
||||||
|
`
|
||||||
|
|
||||||
|
type faker struct {
|
||||||
|
io.ReadWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f faker) Close() error { return nil }
|
||||||
|
func (f faker) LocalAddr() net.Addr { return nil }
|
||||||
|
func (f faker) RemoteAddr() net.Addr { return nil }
|
||||||
|
func (f faker) SetDeadline(time.Time) error { return nil }
|
||||||
|
func (f faker) SetReadDeadline(time.Time) error { return nil }
|
||||||
|
func (f faker) SetWriteDeadline(time.Time) error { return nil }
|
||||||
@@ -73,7 +73,7 @@ func (r *Reader) ReadToPeriod() (string, error) {
|
|||||||
lines, err := r.ReadLines()
|
lines, err := r.ReadLines()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(lines, "\r\n"), nil
|
return strings.Join(lines, "\r\n"), nil
|
||||||
|
|||||||
95
pop3proto_test.go
Normal file
95
pop3proto_test.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package pop3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadLine(t *testing.T) {
|
||||||
|
// This function calls textproto.Reader.ReadLine only.
|
||||||
|
// so no need for test.
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadLines(t *testing.T) {
|
||||||
|
r := reader("line1\nline2\n.\n")
|
||||||
|
|
||||||
|
s, err := r.ReadLines()
|
||||||
|
|
||||||
|
if len(s) != 2 || s[0] != "line1" || s[1] != "line2" || err != nil {
|
||||||
|
t.Fatalf("%v, %v", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err = r.ReadLines()
|
||||||
|
|
||||||
|
if s != nil || err != io.EOF {
|
||||||
|
t.Fatalf("EOF: %s, %v", s, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadToPeriod(t *testing.T) {
|
||||||
|
r := reader("line1\nline2\n.\n")
|
||||||
|
|
||||||
|
s, err := r.ReadToPeriod()
|
||||||
|
|
||||||
|
expected := strings.Replace(`line1
|
||||||
|
line2`, "\n", "\r\n", -1)
|
||||||
|
|
||||||
|
if s != expected || err != nil {
|
||||||
|
t.Fatalf("%v, %v", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err = r.ReadToPeriod()
|
||||||
|
|
||||||
|
if s != "" || err != io.EOF {
|
||||||
|
t.Fatalf("EOF: %s, %v", s, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadResponse(t *testing.T) {
|
||||||
|
var r *Reader
|
||||||
|
var s string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
r = reader("+OK message\n")
|
||||||
|
s, err = r.ReadResponse()
|
||||||
|
if s != "message" || err != nil {
|
||||||
|
t.Fatalf("%v, %v", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r = reader("-ERR message\n")
|
||||||
|
s, err = r.ReadResponse()
|
||||||
|
if s != "" || err == nil || err.Error() != "message" {
|
||||||
|
t.Fatalf("%v, %v", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r = reader("message\n")
|
||||||
|
s, err = r.ReadResponse()
|
||||||
|
if s != "" || err == nil || err.Error() != "レスポンスのフォーマットが不正です。: message" {
|
||||||
|
t.Fatalf("%v, %v", s, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r = reader("* message\n")
|
||||||
|
s, err = r.ReadResponse()
|
||||||
|
if s != "" || err == nil || err.Error() != "レスポンスの内容が不明です。: * message" {
|
||||||
|
t.Fatalf("%v, %v", s, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteLine(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
w := NewWriter(bufio.NewWriter(&buf))
|
||||||
|
|
||||||
|
err := w.WriteLine("foo %d", 123)
|
||||||
|
|
||||||
|
if s := buf.String(); s != "foo 123\r\n" || err != nil {
|
||||||
|
t.Fatalf("s=%q; err=%s", s, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func reader(s string) *Reader {
|
||||||
|
return NewReader(bufio.NewReader(strings.NewReader(s)))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user