Skip to content
This repository has been archived by the owner on Apr 2, 2021. It is now read-only.

Commit

Permalink
add resp3 protocol part 4(protocol-string and render-string) (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
chyroc authored and kasvith committed Aug 27, 2018
1 parent 91f7cd8 commit 6585325
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 59 deletions.
52 changes: 48 additions & 4 deletions internal/protcl/resp3.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,55 @@ type Resp3 struct {
Elems []*Resp3
}

func (r *Resp3) String() string {
return r.string("")
// RenderString convert resp3 to show-message on client
func (r *Resp3) RenderString() string {
return r.renderString("")
}

func (r *Resp3) string(pre string) string {
// ProtocolString convert resp3 to protocol raw string
func (r *Resp3) ProtocolString() string {
buf := new(strings.Builder)
buf.WriteByte(r.Type)
r.protocolString(buf)
return buf.String()
}

func (r *Resp3) protocolString(buf *strings.Builder) {
switch r.Type {
case RepSimpleString, Resp3SimpleError:
buf.WriteString(r.Str)
case Resp3BlobString, Resp3BolbError:
buf.WriteString(strconv.Itoa(len(r.Str)))
buf.WriteByte('\n')
buf.WriteString(r.Str)
case Resp3Number:
buf.WriteString(strconv.Itoa(r.Integer))
case Resp3Double:
buf.WriteString(strconv.FormatFloat(r.Double, 'f', -1, 64))
case Resp3BigNumber:
buf.WriteString(r.BigInt.String())
case Resp3Null:
case Resp3Boolean:
if r.Boolean {
buf.WriteByte('t')
} else {
buf.WriteByte('f')
}
case Resp3Array, Resp3Set:
buf.WriteString(strconv.Itoa(len(r.Elems)))
buf.WriteByte('\n')

for _, v := range r.Elems {
buf.WriteByte(v.Type)
v.protocolString(buf)
}
return
}

buf.WriteByte('\n')
}

func (r *Resp3) renderString(pre string) string {
switch r.Type {
case RepSimpleString, Resp3BlobString:
return fmt.Sprintf("%s%q", pre, r.Str)
Expand Down Expand Up @@ -72,7 +116,7 @@ func (r *Resp3) string(pre string) string {
}
for _, elem := range r.Elems {
str.WriteString("\n")
str.WriteString(elem.string(pre + "\t"))
str.WriteString(elem.renderString(pre + "\t"))
}
return str.String()
}
Expand Down
190 changes: 135 additions & 55 deletions internal/protcl/resp3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package protcl

import (
"bufio"
"math/big"
"strings"
"testing"

Expand All @@ -14,7 +15,7 @@ func testResp3Parser(t *testing.T, input, expect string) {
parser := NewResp3Parser(bufio.NewReader(strings.NewReader(input)))
result, err := parser.Parse()
assert.Nil(err)
assert.Equal(expect, result.String())
assert.Equal(expect, result.RenderString())
}

func testResp3Error(t *testing.T, input, e string) {
Expand All @@ -27,59 +28,138 @@ func testResp3Error(t *testing.T, input, e string) {
assert.Equal(e, err.Error())
}

type TestResp3 struct {
resp3 *Resp3
protocol string
render string
err string
}

func TestResp3Parser(t *testing.T) {
// simple string
testResp3Error(t, "+string", `EOF`)
testResp3Parser(t, "+string\n", `"string"`)

// blob string
testResp3Parser(t, "$0\n\n", `""`)
testResp3Error(t, "$1\n\n", `unexpected line end`)
testResp3Error(t, "$1\naa\n", `unexpected line end`)
testResp3Parser(t, "$10\n1234567890\n", `"1234567890"`)

// simple error
testResp3Parser(t, "-error\n", `(error) error`)
testResp3Parser(t, "-Err error\n", `(error) Err error`)

// blob error
testResp3Parser(t, "!3\nerr\n", `(error) err`)
testResp3Parser(t, "!17\nErr this is error\n", `(error) Err this is error`)

// number
testResp3Parser(t, ":-1\n", `(integer) -1`)
testResp3Parser(t, ":0\n", `(integer) 0`)
testResp3Parser(t, ":100\n", `(integer) 100`)

// double
testResp3Parser(t, ",-1\n", `(double) -1`)
testResp3Parser(t, ",0\n", `(double) 0`)
testResp3Parser(t, ",10\n", `(double) 10`)
testResp3Parser(t, ",.1\n", `(double) 0.1`)
testResp3Parser(t, ",1.23\n", `(double) 1.23`)
testResp3Parser(t, ",1.\n", `(double) 1`)
testResp3Error(t, ",invalid\n", `convert invalid to double fail, because of strconv.ParseFloat: parsing "invalid": invalid syntax`)

// big number
testResp3Parser(t, "(3492890328409238509324850943850943825024385\n", `(big number) 3492890328409238509324850943850943825024385`)
testResp3Error(t, "(invalid string\n", `convert invalid string to Big Number fail`)

// null
testResp3Parser(t, "_\n", "(null)")

// boolean
testResp3Parser(t, "#t\n", `(boolean) true`)
testResp3Parser(t, "#f\n", `(boolean) false`)
testResp3Error(t, "#x\n", `unexpect string: t/f`)
testResp3Error(t, "#\n", `unexpected line end`)

// array
testResp3Error(t, "*3\n:1\n:2\n", "EOF")
testResp3Parser(t, "*3\n:1\n:2\n:3\n", "(array)\n\t(integer) 1\n\t(integer) 2\n\t(integer) 3")
testResp3Parser(t, "*2\n*3\n:1\n$5\nhello\n:2\n#f\n", "(array)\n\t(array)\n\t\t(integer) 1\n\t\t\"hello\"\n\t\t(integer) 2\n\t(boolean) false")

// set
testResp3Error(t, "~3\n:1\n:2\n", "EOF")
testResp3Parser(t, "~3\n:1\n:2\n:3\n", "(set)\n\t(integer) 1\n\t(integer) 2\n\t(integer) 3")
testResp3Parser(t, "~2\n*3\n:1\n$5\nhello\n:2\n#f\n", "(set)\n\t(array)\n\t\t(integer) 1\n\t\t\"hello\"\n\t\t(integer) 2\n\t(boolean) false")
assert := testifyAssert.New(t)

bigNumber, _ := big.NewInt(0).SetString("3492890328409238509324850943850943825024385", 10)
testCases := []TestResp3{
// simple renderString
{protocol: "+renderString", err: "EOF"},
{resp3: &Resp3{Type: Resp3SimpleString, Str: ""}, protocol: "+\n", render: `""`},
{resp3: &Resp3{Type: Resp3SimpleString, Str: "hello"}, protocol: "+hello\n", render: `"hello"`},
{resp3: &Resp3{Type: Resp3SimpleString, Str: "hello world"}, protocol: "+hello world\n", render: `"hello world"`},

// blob renderString
{protocol: "$1\n\n", err: "unexpected line end"},
{protocol: "$1\naa\n", err: "unexpected line end"},
{resp3: &Resp3{Type: Resp3BlobString, Str: ""}, protocol: "$0\n\n", render: `""`},
{resp3: &Resp3{Type: Resp3BlobString, Str: "hello"}, protocol: "$5\nhello\n", render: `"hello"`},
{resp3: &Resp3{Type: Resp3BlobString, Str: "hello\nworld"}, protocol: "$11\nhello\nworld\n", render: "\"hello\\nworld\""},

// simple error
{protocol: "-renderString", err: "EOF"},
{resp3: &Resp3{Type: Resp3SimpleError, Str: ""}, protocol: "-\n", render: `(error) `},
{resp3: &Resp3{Type: Resp3SimpleError, Str: "hello"}, protocol: "-hello\n", render: `(error) hello`},
{resp3: &Resp3{Type: Resp3SimpleError, Str: "hello world"}, protocol: "-hello world\n", render: `(error) hello world`},

// blob error
{protocol: "!1\n\n", err: "unexpected line end"},
{protocol: "!1\naa\n", err: "unexpected line end"},
{resp3: &Resp3{Type: Resp3BolbError, Str: ""}, protocol: "!0\n\n", render: `(error) `},
{resp3: &Resp3{Type: Resp3BolbError, Str: "hello"}, protocol: "!5\nhello\n", render: `(error) hello`},
{resp3: &Resp3{Type: Resp3BolbError, Str: "hello\nworld"}, protocol: "!11\nhello\nworld\n", render: "(error) hello\nworld"},

// number
{protocol: ":invalid", err: "EOF"},
{protocol: ":invalid\n", err: "ERR: error casting invalid to int"},
{resp3: &Resp3{Type: Resp3Number, Integer: -1}, protocol: ":-1\n", render: `(integer) -1`},
{resp3: &Resp3{Type: Resp3Number, Integer: 0}, protocol: ":0\n", render: `(integer) 0`},
{resp3: &Resp3{Type: Resp3Number, Integer: 100}, protocol: ":100\n", render: `(integer) 100`},

// double
{protocol: ",invalid", err: "EOF"},
{protocol: ",invalid\n", err: "convert invalid to double fail, because of strconv.ParseFloat: parsing \"invalid\": invalid syntax"},
{resp3: &Resp3{Type: Resp3Double, Double: -1}, protocol: ",-1\n", render: "(double) -1"},
{resp3: &Resp3{Type: Resp3Double, Double: 0}, protocol: ",0\n", render: "(double) 0"},
{resp3: &Resp3{Type: Resp3Double, Double: 10}, protocol: ",10\n", render: "(double) 10"},
{resp3: &Resp3{Type: Resp3Double, Double: 1.23}, protocol: ",1.23\n", render: "(double) 1.23"},
{protocol: ",.1\n", render: "(double) 0.1"},
{protocol: ",1.\n", render: "(double) 1"},

// big number
{protocol: "(invalid", err: "EOF"},
{protocol: "(invalid\n", err: "convert invalid to Big Number fail"},
{resp3: &Resp3{Type: Resp3BigNumber, BigInt: bigNumber}, protocol: "(3492890328409238509324850943850943825024385\n", render: "(big number) 3492890328409238509324850943850943825024385"},

// null
{protocol: "_invalid", err: "unexpect string: <LF>"},
{protocol: "_invalid\n", err: "unexpect string: <LF>"},
{resp3: &Resp3{Type: Resp3Null}, protocol: "_\n", render: "(null)"},

// boolean
{protocol: "#", err: "EOF"},
{protocol: "#\n", err: "unexpected line end"},
{protocol: "#x\n", err: "unexpect string: t/f"},
{protocol: "#invalid", err: "unexpected line end"},
{protocol: "#invalid\n", err: "unexpected line end"},
{resp3: &Resp3{Type: Resp3Boolean, Boolean: true}, protocol: "#t\n", render: "(boolean) true"},
{resp3: &Resp3{Type: Resp3Boolean, Boolean: false}, protocol: "#f\n", render: "(boolean) false"},

// array
{protocol: "*", err: "EOF"},
{protocol: "*\n", err: "ERR: error casting to int"},
{protocol: "*invalid", err: "EOF"},
{protocol: "*invalid\n", err: "ERR: error casting invalid to int"},
{protocol: "*1\n\n", err: "unknown protocol type: \n"},
{protocol: "*1\ninvalid\n", err: "unknown protocol type: i"},
{protocol: "*3\n:1\n:2\n", err: "EOF"},
{resp3: &Resp3{Type: Resp3Array, Elems: []*Resp3{
{Type: Resp3Number, Integer: 1},
{Type: Resp3Number, Integer: 2},
{Type: Resp3Number, Integer: 3},
}}, protocol: "*3\n:1\n:2\n:3\n", render: "(array)\n\t(integer) 1\n\t(integer) 2\n\t(integer) 3"},
{resp3: &Resp3{Type: Resp3Array, Elems: []*Resp3{
{Type: Resp3Array, Elems: []*Resp3{
{Type: Resp3Number, Integer: 1},
{Type: Resp3BlobString, Str: "hello"},
{Type: Resp3Number, Integer: 2},
}},
{Type: Resp3Boolean, Boolean: false},
}}, protocol: "*2\n*3\n:1\n$5\nhello\n:2\n#f\n", render: "(array)\n\t(array)\n\t\t(integer) 1\n\t\t\"hello\"\n\t\t(integer) 2\n\t(boolean) false"},

// set
{protocol: "~", err: "EOF"},
{protocol: "~\n", err: "ERR: error casting to int"},
{protocol: "~invalid", err: "EOF"},
{protocol: "~invalid\n", err: "ERR: error casting invalid to int"},
{protocol: "~1\n\n", err: "unknown protocol type: \n"},
{protocol: "~1\ninvalid\n", err: "unknown protocol type: i"},
{protocol: "~3\n:1\n:2\n", err: "EOF"},
{resp3: &Resp3{Type: Resp3Set, Elems: []*Resp3{
{Type: Resp3Number, Integer: 1},
{Type: Resp3Number, Integer: 2},
{Type: Resp3Number, Integer: 3},
}}, protocol: "~3\n:1\n:2\n:3\n", render: "(set)\n\t(integer) 1\n\t(integer) 2\n\t(integer) 3"},
{resp3: &Resp3{Type: Resp3Set, Elems: []*Resp3{
{Type: Resp3Array, Elems: []*Resp3{
{Type: Resp3Number, Integer: 1},
{Type: Resp3BlobString, Str: "hello"},
{Type: Resp3Number, Integer: 2},
}},
{Type: Resp3Boolean, Boolean: false},
}}, protocol: "~2\n*3\n:1\n$5\nhello\n:2\n#f\n", render: "(set)\n\t(array)\n\t\t(integer) 1\n\t\t\"hello\"\n\t\t(integer) 2\n\t(boolean) false"},
}

for _, testCase := range testCases {
if testCase.resp3 == nil {
if testCase.render != "" {
// protocol renderString -> render renderString
testResp3Parser(t, testCase.protocol, testCase.render)
} else {
// err test
testResp3Error(t, testCase.protocol, testCase.err)
}
} else {
// resp3 -> protocol renderString -> render renderString
assert.Equal(testCase.protocol, testCase.resp3.ProtocolString())
testResp3Parser(t, testCase.protocol, testCase.render)
}
}
}

0 comments on commit 6585325

Please sign in to comment.