2021-12-01 15:43:13 +00:00

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)
}