367 lines
6.7 KiB
Go
367 lines
6.7 KiB
Go
package nbio
|
|
|
|
import (
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"math/rand"
|
|
"net"
|
|
"os"
|
|
"runtime"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
var addr = "127.0.0.1:8888"
|
|
var testfile = "test_tmp.file"
|
|
var gopher *Gopher
|
|
|
|
func init() {
|
|
if err := ioutil.WriteFile(testfile, make([]byte, 1024*100), 0600); err != nil {
|
|
log.Panicf("write file failed: %v", err)
|
|
}
|
|
|
|
addrs := []string{addr}
|
|
g := NewGopher(Config{
|
|
Network: "tcp",
|
|
Addrs: addrs,
|
|
})
|
|
|
|
g.OnOpen(func(c *Conn) {
|
|
c.SetReadDeadline(time.Now().Add(time.Second * 10))
|
|
})
|
|
g.OnData(func(c *Conn, data []byte) {
|
|
if len(data) == 8 && string(data) == "sendfile" {
|
|
fd, err := os.Open(testfile)
|
|
if err != nil {
|
|
log.Panicf("open file failed: %v", err)
|
|
}
|
|
|
|
if _, err = c.Sendfile(fd, 0); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if err := fd.Close(); err != nil {
|
|
log.Panicf("close file failed: %v", err)
|
|
}
|
|
} else {
|
|
c.Write(append([]byte{}, data...))
|
|
}
|
|
})
|
|
g.OnClose(func(c *Conn, err error) {})
|
|
|
|
err := g.Start()
|
|
if err != nil {
|
|
log.Panicf("Start failed: %v\n", err)
|
|
}
|
|
|
|
gopher = g
|
|
}
|
|
|
|
func TestEcho(t *testing.T) {
|
|
var done = make(chan int)
|
|
var clientNum = 2
|
|
var msgSize = 1024
|
|
var total int64 = 0
|
|
|
|
g := NewGopher(Config{})
|
|
err := g.Start()
|
|
if err != nil {
|
|
log.Panicf("Start failed: %v\n", err)
|
|
}
|
|
defer g.Stop()
|
|
|
|
g.OnOpen(func(c *Conn) {
|
|
c.SetSession(1)
|
|
if c.Session() != 1 {
|
|
log.Panicf("invalid session: %v", c.Session())
|
|
}
|
|
c.SetLinger(1, 0)
|
|
c.SetNoDelay(true)
|
|
c.SetKeepAlive(true)
|
|
c.SetKeepAlivePeriod(time.Second * 60)
|
|
c.SetDeadline(time.Now().Add(time.Second))
|
|
c.SetReadBuffer(1024 * 4)
|
|
c.SetWriteBuffer(1024 * 4)
|
|
log.Printf("connected, local addr: %v, remote addr: %v", c.LocalAddr(), c.RemoteAddr())
|
|
})
|
|
g.OnData(func(c *Conn, data []byte) {
|
|
recved := atomic.AddInt64(&total, int64(len(data)))
|
|
if len(data) > 0 && recved >= int64(clientNum*msgSize) {
|
|
close(done)
|
|
}
|
|
})
|
|
|
|
g.OnReadBufferAlloc(func(c *Conn) []byte {
|
|
return make([]byte, 1024)
|
|
})
|
|
g.OnReadBufferFree(func(c *Conn, b []byte) {
|
|
|
|
})
|
|
|
|
one := func(n int) {
|
|
c, err := Dial("tcp", addr)
|
|
if err != nil {
|
|
log.Panicf("Dial failed: %v", err)
|
|
}
|
|
g.AddConn(c)
|
|
if n%2 == 0 {
|
|
c.Writev([][]byte{make([]byte, msgSize)})
|
|
} else {
|
|
c.Write(make([]byte, msgSize))
|
|
}
|
|
}
|
|
|
|
for i := 0; i < clientNum; i++ {
|
|
if runtime.GOOS != "windows" {
|
|
one(i)
|
|
} else {
|
|
go one(i)
|
|
}
|
|
}
|
|
|
|
<-done
|
|
}
|
|
|
|
func TestSendfile(t *testing.T) {
|
|
conn, err := net.Dial("tcp", addr)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
buf := make([]byte, 1024*100)
|
|
|
|
for i := 0; i < 3; i++ {
|
|
if _, err := conn.Write([]byte("sendfile")); err != nil {
|
|
log.Panicf("write 'sendfile' failed: %v", err)
|
|
}
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
log.Panicf("read file failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTimeout(t *testing.T) {
|
|
g := NewGopher(Config{})
|
|
err := g.Start()
|
|
if err != nil {
|
|
log.Panicf("Start failed: %v\n", err)
|
|
}
|
|
defer g.Stop()
|
|
|
|
var done = make(chan int)
|
|
var begin time.Time
|
|
var timeout = time.Second
|
|
g.OnOpen(func(c *Conn) {
|
|
begin = time.Now()
|
|
c.SetReadDeadline(begin.Add(timeout))
|
|
})
|
|
g.OnClose(func(c *Conn, err error) {
|
|
to := time.Since(begin)
|
|
if to > timeout*2 {
|
|
log.Panicf("timeout: %v, want: %v", to, timeout)
|
|
}
|
|
close(done)
|
|
})
|
|
|
|
one := func() {
|
|
c, err := DialTimeout("tcp", addr, time.Second)
|
|
if err != nil {
|
|
log.Panicf("Dial failed: %v", err)
|
|
}
|
|
g.AddConn(c)
|
|
}
|
|
one()
|
|
|
|
<-done
|
|
}
|
|
|
|
func TestFuzz(t *testing.T) {
|
|
wg := sync.WaitGroup{}
|
|
for i := 0; i < 100; i++ {
|
|
wg.Add(1)
|
|
go func(idx int) {
|
|
defer wg.Done()
|
|
if idx%2 == 0 {
|
|
Dial("tcp4", addr)
|
|
} else {
|
|
Dial("tcp4", addr)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
readed := 0
|
|
wg2 := sync.WaitGroup{}
|
|
wg2.Add(1)
|
|
g := NewGopher(Config{NPoller: 1})
|
|
g.OnData(func(c *Conn, data []byte) {
|
|
readed += len(data)
|
|
if readed == 4 {
|
|
wg2.Done()
|
|
}
|
|
})
|
|
err := g.Start()
|
|
if err != nil {
|
|
log.Panicf("Start failed: %v", err)
|
|
}
|
|
|
|
c, err := Dial("tcp", addr)
|
|
if err == nil {
|
|
log.Printf("Dial tcp4: %v, %v, %v", c.LocalAddr(), c.RemoteAddr(), err)
|
|
g.AddConn(c)
|
|
c.SetWriteDeadline(time.Now().Add(time.Second))
|
|
c.Write([]byte{1})
|
|
|
|
time.Sleep(time.Second / 10)
|
|
|
|
bs := [][]byte{}
|
|
bs = append(bs, []byte{2})
|
|
bs = append(bs, []byte{3})
|
|
bs = append(bs, []byte{4})
|
|
c.Writev(bs)
|
|
|
|
time.Sleep(time.Second / 10)
|
|
|
|
c.Close()
|
|
c.Write([]byte{1})
|
|
} else {
|
|
log.Panicf("Dial tcp4: %v", err)
|
|
}
|
|
|
|
gErr := NewGopher(Config{
|
|
Network: "tcp4",
|
|
Addrs: []string{"127.0.0.1:8889", "127.0.0.1:8889"},
|
|
})
|
|
gErr.Start()
|
|
}
|
|
|
|
func TestHeapTimer(t *testing.T) {
|
|
g := NewGopher(Config{})
|
|
g.Start()
|
|
defer g.Stop()
|
|
|
|
timeout := time.Second / 10
|
|
|
|
testHeapTimerNormal(g, timeout)
|
|
testHeapTimerExecPanic(g, timeout)
|
|
testHeapTimerNormalExecMany(g, timeout)
|
|
testHeapTimerExecManyRandtime(g)
|
|
}
|
|
|
|
func testHeapTimerNormal(g *Gopher, timeout time.Duration) {
|
|
t1 := time.Now()
|
|
ch1 := make(chan int)
|
|
g.AfterFunc(timeout*5, func() {
|
|
close(ch1)
|
|
})
|
|
<-ch1
|
|
to1 := time.Since(t1)
|
|
if to1 < timeout*4 || to1 > timeout*10 {
|
|
log.Panicf("invalid to1: %v", to1)
|
|
}
|
|
|
|
t2 := time.Now()
|
|
ch2 := make(chan int)
|
|
it2 := g.afterFunc(timeout, func() {
|
|
close(ch2)
|
|
})
|
|
it2.Reset(timeout * 5)
|
|
<-ch2
|
|
to2 := time.Since(t2)
|
|
if to2 < timeout*4 || to2 > timeout*10 {
|
|
log.Panicf("invalid to2: %v", to2)
|
|
}
|
|
|
|
ch3 := make(chan int)
|
|
it3 := g.afterFunc(timeout, func() {
|
|
close(ch3)
|
|
})
|
|
it3.Stop()
|
|
<-g.After(timeout * 2)
|
|
select {
|
|
case <-ch3:
|
|
log.Panicf("stop failed")
|
|
default:
|
|
}
|
|
}
|
|
|
|
func testHeapTimerExecPanic(g *Gopher, timeout time.Duration) {
|
|
g.afterFunc(timeout, func() {
|
|
panic("test")
|
|
})
|
|
}
|
|
|
|
func testHeapTimerNormalExecMany(g *Gopher, timeout time.Duration) {
|
|
ch4 := make(chan int, 5)
|
|
for i := 0; i < 5; i++ {
|
|
n := i + 1
|
|
if n == 3 {
|
|
n = 5
|
|
} else if n == 5 {
|
|
n = 3
|
|
}
|
|
|
|
g.afterFunc(timeout*time.Duration(n), func() {
|
|
ch4 <- n
|
|
})
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
n := <-ch4
|
|
if n != i+1 {
|
|
log.Panicf("invalid n: %v, %v", i, n)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testHeapTimerExecManyRandtime(g *Gopher) {
|
|
its := make([]*htimer, 100)[0:0]
|
|
ch5 := make(chan int, 100)
|
|
for i := 0; i < 100; i++ {
|
|
n := 500 + rand.Int()%200
|
|
to := time.Duration(n) * time.Second / 1000
|
|
its = append(its, g.afterFunc(to, func() {
|
|
ch5 <- n
|
|
}))
|
|
}
|
|
if len(its) != 100 || g.timers.Len() != 100 {
|
|
log.Panicf("invalid timers length: %v, %v", len(its), g.timers.Len())
|
|
}
|
|
for i := 0; i < 50; i++ {
|
|
if its[0] == nil {
|
|
log.Panicf("invalid its[0]")
|
|
}
|
|
its[0].Stop()
|
|
its = its[1:]
|
|
}
|
|
if len(its) != 50 || g.timers.Len() != 50 {
|
|
log.Panicf("invalid timers length: %v, %v", len(its), g.timers.Len())
|
|
}
|
|
recved := 0
|
|
LOOP_RECV:
|
|
for {
|
|
select {
|
|
case <-ch5:
|
|
recved++
|
|
case <-time.After(time.Second):
|
|
break LOOP_RECV
|
|
}
|
|
}
|
|
if recved != 50 {
|
|
log.Panicf("invalid recved num: %v", recved)
|
|
}
|
|
|
|
it := &htimer{parent: g, index: -1}
|
|
it.Stop()
|
|
}
|
|
|
|
func TestStop(t *testing.T) {
|
|
gopher.Stop()
|
|
os.Remove(testfile)
|
|
}
|