summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/config_test.go40
-rw-r--r--ext/errors_test.go11
-rw-r--r--ext/pipe_factory_test.go133
-rw-r--r--ext/protocol_test.go46
-rw-r--r--ext/socket_factory_test.go427
5 files changed, 657 insertions, 0 deletions
diff --git a/ext/config_test.go b/ext/config_test.go
new file mode 100644
index 00000000..fbdde223
--- /dev/null
+++ b/ext/config_test.go
@@ -0,0 +1,40 @@
+package ext
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+ "time"
+)
+
+func Test_NumWorkers(t *testing.T) {
+ cfg := Config{
+ AllocateTimeout: time.Second,
+ DestroyTimeout: time.Second * 10,
+ }
+ err := cfg.Valid()
+
+ assert.NotNil(t, err)
+ assert.Equal(t, "cfg.NumWorkers must be set", err.Error())
+}
+
+func Test_AllocateTimeout(t *testing.T) {
+ cfg := Config{
+ NumWorkers: 10,
+ DestroyTimeout: time.Second * 10,
+ }
+ err := cfg.Valid()
+
+ assert.NotNil(t, err)
+ assert.Equal(t, "cfg.AllocateTimeout must be set", err.Error())
+}
+
+func Test_DestroyTimeout(t *testing.T) {
+ cfg := Config{
+ NumWorkers: 10,
+ AllocateTimeout: time.Second,
+ }
+ err := cfg.Valid()
+
+ assert.NotNil(t, err)
+ assert.Equal(t, "cfg.DestroyTimeout must be set", err.Error())
+}
diff --git a/ext/errors_test.go b/ext/errors_test.go
new file mode 100644
index 00000000..7c9d7a5b
--- /dev/null
+++ b/ext/errors_test.go
@@ -0,0 +1,11 @@
+package ext
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func Test_JobError_Error(t *testing.T) {
+ e := JobError([]byte("error"))
+ assert.Equal(t, "error", e.Error())
+}
diff --git a/ext/pipe_factory_test.go b/ext/pipe_factory_test.go
new file mode 100644
index 00000000..434c31b0
--- /dev/null
+++ b/ext/pipe_factory_test.go
@@ -0,0 +1,133 @@
+package ext
+
+import (
+ "github.com/stretchr/testify/assert"
+ "os/exec"
+ "testing"
+)
+
+func Test_Pipe_Start(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+
+ w, err := NewPipeFactory().SpawnWorker(cmd)
+ assert.NoError(t, err)
+ assert.NotNil(t, w)
+
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+
+ w.Stop()
+}
+
+func Test_Pipe_StartError(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+ cmd.Start()
+
+ w, err := NewPipeFactory().SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Pipe_PipeError(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+ cmd.StdinPipe()
+
+ w, err := NewPipeFactory().SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Pipe_PipeError2(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+ cmd.StdoutPipe()
+
+ w, err := NewPipeFactory().SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Pipe_Failboot(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/failboot.php")
+ w, err := NewPipeFactory().SpawnWorker(cmd)
+
+ assert.Nil(t, w)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "failboot")
+}
+
+func Test_Pipe_Invalid(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/invalid.php")
+
+ w, err := NewPipeFactory().SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Pipe_Echo(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+
+ w, _ := NewPipeFactory().SpawnWorker(cmd)
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+ defer w.Stop()
+
+ res, err := w.Exec(&Payload{Body: []byte("hello")})
+
+ assert.NoError(t, err)
+ assert.NotNil(t, res)
+ assert.NotNil(t, res.Body)
+ assert.Nil(t, res.Context)
+
+ assert.Equal(t, "hello", res.String())
+}
+
+func Test_Pipe_Broken(t *testing.T) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "broken", "pipes")
+
+ w, _ := NewPipeFactory().SpawnWorker(cmd)
+ go func() {
+ err := w.Wait()
+
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "undefined_function()")
+ }()
+ defer w.Stop()
+
+ res, err := w.Exec(&Payload{Body: []byte("hello")})
+
+ assert.Error(t, err)
+ assert.Nil(t, res)
+}
+
+func Benchmark_Pipe_SpawnWorker_Stop(b *testing.B) {
+ f := NewPipeFactory()
+ for n := 0; n < b.N; n++ {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+ w, _ := f.SpawnWorker(cmd)
+ go func() {
+ if w.Wait() != nil {
+ b.Fail()
+ }
+ }()
+
+ w.Stop()
+ }
+}
+
+func Benchmark_Pipe_Worker_ExecEcho(b *testing.B) {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+
+ w, _ := NewPipeFactory().SpawnWorker(cmd)
+ go func() {
+ w.Wait()
+ }()
+ defer w.Stop()
+
+ for n := 0; n < b.N; n++ {
+ if _, err := w.Exec(&Payload{Body: []byte("hello")}); err != nil {
+ b.Fail()
+ }
+ }
+}
diff --git a/ext/protocol_test.go b/ext/protocol_test.go
new file mode 100644
index 00000000..f6410ef5
--- /dev/null
+++ b/ext/protocol_test.go
@@ -0,0 +1,46 @@
+package ext
+
+import (
+ "github.com/pkg/errors"
+ "github.com/spiral/goridge"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+type relayMock struct {
+ error bool
+ payload string
+}
+
+func (r *relayMock) Send(data []byte, flags byte) (err error) {
+ if r.error {
+ return errors.New("send error")
+ }
+
+ return nil
+}
+
+func (r *relayMock) Receive() (data []byte, p goridge.Prefix, err error) {
+ return []byte(r.payload), goridge.NewPrefix().WithFlag(goridge.PayloadControl), nil
+}
+
+func (r *relayMock) Close() error {
+ return nil
+}
+
+func Test_Protocol_Errors(t *testing.T) {
+ err := sendPayload(&relayMock{}, make(chan int))
+ assert.Error(t, err)
+}
+
+func Test_Protocol_FetchPID(t *testing.T) {
+ pid, err := fetchPID(&relayMock{error: false, payload: "{\"pid\":100}"})
+ assert.NoError(t, err)
+ assert.Equal(t, 100, pid)
+
+ _, err = fetchPID(&relayMock{error: true, payload: "{\"pid\":100}"})
+ assert.Error(t, err)
+
+ _, err = fetchPID(&relayMock{error: false, payload: "{\"pid:100"})
+ assert.Error(t, err)
+}
diff --git a/ext/socket_factory_test.go b/ext/socket_factory_test.go
new file mode 100644
index 00000000..2d6108bc
--- /dev/null
+++ b/ext/socket_factory_test.go
@@ -0,0 +1,427 @@
+package ext
+
+import (
+ "github.com/stretchr/testify/assert"
+ "net"
+ "os/exec"
+ "runtime"
+ "testing"
+ "time"
+)
+
+func Test_Tcp_Start(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "tcp")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.NoError(t, err)
+ assert.NotNil(t, w)
+
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+
+ w.Stop()
+}
+
+func Test_Tcp_StartCloseFactory(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "tcp")
+
+ f := NewSocketFactory(ls, time.Minute)
+ defer f.Close()
+
+ w, err := f.SpawnWorker(cmd)
+ assert.NoError(t, err)
+ assert.NotNil(t, w)
+
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+
+ w.Stop()
+}
+
+func Test_Tcp_StartError(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "pipes")
+ cmd.Start()
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Tcp_Failboot(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/failboot.php")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.Nil(t, w)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "failboot")
+}
+
+func Test_Tcp_Timeout(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/slow-client.php", "echo", "tcp", "200", "0")
+
+ w, err := NewSocketFactory(ls, time.Millisecond*100).SpawnWorker(cmd)
+ assert.Nil(t, w)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "relay timeout")
+}
+
+func Test_Tcp_Invalid(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/invalid.php")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Tcp_Broken(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "broken", "tcp")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ go func() {
+ err := w.Wait()
+
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "undefined_function()")
+ }()
+ defer w.Stop()
+
+ res, err := w.Exec(&Payload{Body: []byte("hello")})
+
+ assert.Error(t, err)
+ assert.Nil(t, res)
+}
+
+func Test_Tcp_Echo(t *testing.T) {
+ time.Sleep(time.Millisecond * 10) // to ensure free socket
+
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if assert.NoError(t, err) {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "tcp")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+ defer w.Stop()
+
+ res, err := w.Exec(&Payload{Body: []byte("hello")})
+
+ assert.NoError(t, err)
+ assert.NotNil(t, res)
+ assert.NotNil(t, res.Body)
+ assert.Nil(t, res.Context)
+
+ assert.Equal(t, "hello", res.String())
+}
+
+func Test_Unix_Start(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "unix")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.NoError(t, err)
+ assert.NotNil(t, w)
+
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+
+ w.Stop()
+}
+
+func Test_Unix_Failboot(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/failboot.php")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.Nil(t, w)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "failboot")
+}
+
+func Test_Unix_Timeout(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/slow-client.php", "echo", "unix", "200", "0")
+
+ w, err := NewSocketFactory(ls, time.Millisecond*100).SpawnWorker(cmd)
+ assert.Nil(t, w)
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "relay timeout")
+}
+
+func Test_Unix_Invalid(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/invalid.php")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ assert.Error(t, err)
+ assert.Nil(t, w)
+}
+
+func Test_Unix_Broken(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "broken", "unix")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ go func() {
+ err := w.Wait()
+
+ assert.Error(t, err)
+ assert.Contains(t, err.Error(), "undefined_function()")
+ }()
+ defer w.Stop()
+
+ res, err := w.Exec(&Payload{Body: []byte("hello")})
+
+ assert.Error(t, err)
+ assert.Nil(t, res)
+}
+
+func Test_Unix_Echo(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ t.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "unix")
+
+ w, err := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ go func() {
+ assert.NoError(t, w.Wait())
+ }()
+ defer w.Stop()
+
+ res, err := w.Exec(&Payload{Body: []byte("hello")})
+
+ assert.NoError(t, err)
+ assert.NotNil(t, res)
+ assert.NotNil(t, res.Body)
+ assert.Nil(t, res.Context)
+
+ assert.Equal(t, "hello", res.String())
+}
+
+func Benchmark_Tcp_SpawnWorker_Stop(b *testing.B) {
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ b.Skip("socket is busy")
+ }
+
+ f := NewSocketFactory(ls, time.Minute)
+ for n := 0; n < b.N; n++ {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "tcp")
+
+ w, _ := f.SpawnWorker(cmd)
+ go func() {
+ if w.Wait() != nil {
+ b.Fail()
+ }
+ }()
+
+ w.Stop()
+ }
+}
+
+func Benchmark_Tcp_Worker_ExecEcho(b *testing.B) {
+ ls, err := net.Listen("tcp", "localhost:9007")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ b.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "tcp")
+
+ w, _ := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ go func() {
+ w.Wait()
+ }()
+ defer w.Stop()
+
+ for n := 0; n < b.N; n++ {
+ if _, err := w.Exec(&Payload{Body: []byte("hello")}); err != nil {
+ b.Fail()
+ }
+ }
+}
+
+func Benchmark_Unix_SpawnWorker_Stop(b *testing.B) {
+ if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
+ b.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ b.Skip("socket is busy")
+ }
+
+ f := NewSocketFactory(ls, time.Minute)
+ for n := 0; n < b.N; n++ {
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "unix")
+
+ w, _ := f.SpawnWorker(cmd)
+ go func() {
+ if w.Wait() != nil {
+ b.Fail()
+ }
+ }()
+
+ w.Stop()
+ }
+}
+
+func Benchmark_Unix_Worker_ExecEcho(b *testing.B) {
+ if runtime.GOOS == "windows" || runtime.GOOS == "darwin" {
+ b.Skip("not supported on " + runtime.GOOS)
+ }
+
+ ls, err := net.Listen("unix", "sock.unix")
+ if err == nil {
+ defer ls.Close()
+ } else {
+ b.Skip("socket is busy")
+ }
+
+ cmd := exec.Command("php", "php-src/tests/client.php", "echo", "unix")
+
+ w, _ := NewSocketFactory(ls, time.Minute).SpawnWorker(cmd)
+ go func() {
+ w.Wait()
+ }()
+ defer w.Stop()
+
+ for n := 0; n < b.N; n++ {
+ if _, err := w.Exec(&Payload{Body: []byte("hello")}); err != nil {
+ b.Fail()
+ }
+ }
+}