forked from mirror/redcon
210 lines
4.4 KiB
Go
210 lines
4.4 KiB
Go
|
package redcon
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"log"
|
||
|
"math/rand"
|
||
|
"testing"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// TestRandomCommands fills a bunch of random commands and test various
|
||
|
// ways that the reader may receive data.
|
||
|
func TestRandomCommands(t *testing.T) {
|
||
|
rand.Seed(time.Now().UnixNano())
|
||
|
|
||
|
// build random commands.
|
||
|
gcmds := make([][]string, 10000)
|
||
|
for i := 0; i < len(gcmds); i++ {
|
||
|
args := make([]string, (rand.Int()%50)+1) // 1-50 args
|
||
|
for j := 0; j < len(args); j++ {
|
||
|
n := rand.Int() % 10
|
||
|
if j == 0 {
|
||
|
n++
|
||
|
}
|
||
|
arg := make([]byte, n)
|
||
|
for k := 0; k < len(arg); k++ {
|
||
|
arg[k] = byte(rand.Int() % 0xFF)
|
||
|
}
|
||
|
args[j] = string(arg)
|
||
|
}
|
||
|
gcmds[i] = args
|
||
|
}
|
||
|
// create a list of a buffers
|
||
|
var bufs []string
|
||
|
|
||
|
// pipe valid RESP commands
|
||
|
for i := 0; i < len(gcmds); i++ {
|
||
|
args := gcmds[i]
|
||
|
msg := fmt.Sprintf("*%d\r\n", len(args))
|
||
|
for j := 0; j < len(args); j++ {
|
||
|
msg += fmt.Sprintf("$%d\r\n%s\r\n", len(args[j]), args[j])
|
||
|
}
|
||
|
bufs = append(bufs, msg)
|
||
|
}
|
||
|
bufs = append(bufs, "RESET THE INDEX\r\n")
|
||
|
|
||
|
// pipe valid plain commands
|
||
|
for i := 0; i < len(gcmds); i++ {
|
||
|
args := gcmds[i]
|
||
|
var msg string
|
||
|
for j := 0; j < len(args); j++ {
|
||
|
quotes := false
|
||
|
var narg []byte
|
||
|
arg := args[j]
|
||
|
if len(arg) == 0 {
|
||
|
quotes = true
|
||
|
}
|
||
|
for k := 0; k < len(arg); k++ {
|
||
|
switch arg[k] {
|
||
|
default:
|
||
|
narg = append(narg, arg[k])
|
||
|
case ' ':
|
||
|
quotes = true
|
||
|
narg = append(narg, arg[k])
|
||
|
case '\\', '"', '*':
|
||
|
quotes = true
|
||
|
narg = append(narg, '\\', arg[k])
|
||
|
case '\r':
|
||
|
quotes = true
|
||
|
narg = append(narg, '\\', 'r')
|
||
|
case '\n':
|
||
|
quotes = true
|
||
|
narg = append(narg, '\\', 'n')
|
||
|
}
|
||
|
}
|
||
|
msg += " "
|
||
|
if quotes {
|
||
|
msg += "\""
|
||
|
}
|
||
|
msg += string(narg)
|
||
|
if quotes {
|
||
|
msg += "\""
|
||
|
}
|
||
|
}
|
||
|
if msg != "" {
|
||
|
msg = msg[1:]
|
||
|
}
|
||
|
msg += "\r\n"
|
||
|
bufs = append(bufs, msg)
|
||
|
}
|
||
|
bufs = append(bufs, "RESET THE INDEX\r\n")
|
||
|
|
||
|
// pipe valid RESP commands in broken chunks
|
||
|
lmsg := ""
|
||
|
for i := 0; i < len(gcmds); i++ {
|
||
|
args := gcmds[i]
|
||
|
msg := fmt.Sprintf("*%d\r\n", len(args))
|
||
|
for j := 0; j < len(args); j++ {
|
||
|
msg += fmt.Sprintf("$%d\r\n%s\r\n", len(args[j]), args[j])
|
||
|
}
|
||
|
msg = lmsg + msg
|
||
|
if len(msg) > 0 {
|
||
|
lmsg = msg[len(msg)/2:]
|
||
|
msg = msg[:len(msg)/2]
|
||
|
}
|
||
|
bufs = append(bufs, msg)
|
||
|
}
|
||
|
bufs = append(bufs, lmsg)
|
||
|
bufs = append(bufs, "RESET THE INDEX\r\n")
|
||
|
|
||
|
// pipe valid RESP commands in large broken chunks
|
||
|
lmsg = ""
|
||
|
for i := 0; i < len(gcmds); i++ {
|
||
|
args := gcmds[i]
|
||
|
msg := fmt.Sprintf("*%d\r\n", len(args))
|
||
|
for j := 0; j < len(args); j++ {
|
||
|
msg += fmt.Sprintf("$%d\r\n%s\r\n", len(args[j]), args[j])
|
||
|
}
|
||
|
if len(lmsg) < 1500 {
|
||
|
lmsg += msg
|
||
|
continue
|
||
|
}
|
||
|
msg = lmsg + msg
|
||
|
if len(msg) > 0 {
|
||
|
lmsg = msg[len(msg)/2:]
|
||
|
msg = msg[:len(msg)/2]
|
||
|
}
|
||
|
bufs = append(bufs, msg)
|
||
|
}
|
||
|
bufs = append(bufs, lmsg)
|
||
|
bufs = append(bufs, "RESET THE INDEX\r\n")
|
||
|
|
||
|
// Pipe the buffers in a background routine
|
||
|
rd, wr := io.Pipe()
|
||
|
go func() {
|
||
|
defer wr.Close()
|
||
|
for _, msg := range bufs {
|
||
|
io.WriteString(wr, msg)
|
||
|
}
|
||
|
}()
|
||
|
defer rd.Close()
|
||
|
cnt := 0
|
||
|
idx := 0
|
||
|
start := time.Now()
|
||
|
r := newReader(rd)
|
||
|
for {
|
||
|
cmds, err := r.ReadCommands()
|
||
|
if err != nil {
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
for _, cmd := range cmds {
|
||
|
if len(cmd) == 3 && cmd[0] == "RESET" && cmd[1] == "THE" && cmd[2] == "INDEX" {
|
||
|
if idx != len(gcmds) {
|
||
|
t.Fatalf("did not process all commands")
|
||
|
}
|
||
|
idx = 0
|
||
|
break
|
||
|
}
|
||
|
if len(cmd) != len(gcmds[idx]) {
|
||
|
t.Fatalf("len not equal for index %d -- %d != %d", idx, len(cmd), len(gcmds[idx]))
|
||
|
}
|
||
|
for i := 0; i < len(cmd); i++ {
|
||
|
if cmd[i] != gcmds[idx][i] {
|
||
|
t.Fatalf("not equal for index %d/%d", idx, i)
|
||
|
}
|
||
|
}
|
||
|
idx++
|
||
|
cnt++
|
||
|
}
|
||
|
}
|
||
|
if false {
|
||
|
dur := time.Now().Sub(start)
|
||
|
fmt.Printf("%d commands in %s - %.0f ops/sec\n", cnt, dur, float64(cnt)/(float64(dur)/float64(time.Second)))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
func TestServer(t *testing.T) {
|
||
|
err := ListenAndServe(":11111",
|
||
|
func(conn Conn, cmds [][]string) {
|
||
|
for _, cmd := range cmds {
|
||
|
switch strings.ToLower(cmd[0]) {
|
||
|
default:
|
||
|
conn.WriteError("ERR unknown command '" + cmd[0] + "'")
|
||
|
case "ping":
|
||
|
conn.WriteString("PONG")
|
||
|
case "quit":
|
||
|
conn.WriteString("OK")
|
||
|
conn.Close()
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
func(conn Conn) bool {
|
||
|
log.Printf("accept: %s", conn.RemoteAddr())
|
||
|
return true
|
||
|
},
|
||
|
func(conn Conn, err error) {
|
||
|
log.Printf("closed: %s [%v]", conn.RemoteAddr(), err)
|
||
|
},
|
||
|
)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}
|
||
|
*/
|