summaryrefslogtreecommitdiff
path: root/plugins/reload/tests
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/reload/tests')
-rw-r--r--plugins/reload/tests/config_test.go63
-rw-r--r--plugins/reload/tests/configs/.rr-reload-2.yaml44
-rw-r--r--plugins/reload/tests/configs/.rr-reload-3.yaml46
-rw-r--r--plugins/reload/tests/configs/.rr-reload-4.yaml46
-rw-r--r--plugins/reload/tests/configs/.rr-reload.yaml44
-rw-r--r--plugins/reload/tests/reload_plugin_test.go822
6 files changed, 1065 insertions, 0 deletions
diff --git a/plugins/reload/tests/config_test.go b/plugins/reload/tests/config_test.go
new file mode 100644
index 00000000..5bb64b6b
--- /dev/null
+++ b/plugins/reload/tests/config_test.go
@@ -0,0 +1,63 @@
+package tests
+
+import (
+ "testing"
+ "time"
+
+ "github.com/spiral/roadrunner/v2/plugins/reload"
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Config_Valid(t *testing.T) {
+ services := make(map[string]reload.ServiceConfig)
+ services["test"] = reload.ServiceConfig{
+ Recursive: false,
+ Patterns: nil,
+ Dirs: nil,
+ Ignore: nil,
+ }
+
+ cfg := &reload.Config{
+ Interval: time.Second,
+ Patterns: nil,
+ Services: services,
+ }
+ assert.NoError(t, cfg.Valid())
+}
+
+func Test_Fake_ServiceConfig(t *testing.T) {
+ services := make(map[string]reload.ServiceConfig)
+ cfg := &reload.Config{
+ Interval: time.Microsecond,
+ Patterns: nil,
+ Services: services,
+ }
+ assert.Error(t, cfg.Valid())
+}
+
+func Test_Interval(t *testing.T) {
+ services := make(map[string]reload.ServiceConfig)
+ services["test"] = reload.ServiceConfig{
+ Enabled: false,
+ Recursive: false,
+ Patterns: nil,
+ Dirs: nil,
+ Ignore: nil,
+ }
+
+ cfg := &reload.Config{
+ Interval: time.Millisecond, // should crash here
+ Patterns: nil,
+ Services: services,
+ }
+ assert.Error(t, cfg.Valid())
+}
+
+func Test_NoServiceConfig(t *testing.T) {
+ cfg := &reload.Config{
+ Interval: time.Second,
+ Patterns: nil,
+ Services: nil,
+ }
+ assert.Error(t, cfg.Valid())
+}
diff --git a/plugins/reload/tests/configs/.rr-reload-2.yaml b/plugins/reload/tests/configs/.rr-reload-2.yaml
new file mode 100644
index 00000000..5be3179b
--- /dev/null
+++ b/plugins/reload/tests/configs/.rr-reload-2.yaml
@@ -0,0 +1,44 @@
+server:
+ command: php ../../../tests/psr-worker-bench.php
+ user: ''
+ group: ''
+ env:
+ RR_HTTP: 'true'
+ relay: pipes
+ relayTimeout: 20s
+http:
+ debug: true
+ address: '127.0.0.1:27388'
+ maxRequestSize: 1024
+ middleware:
+ - ''
+ uploads:
+ forbid:
+ - .php
+ - .exe
+ - .bat
+ trustedSubnets:
+ - 10.0.0.0/8
+ - 127.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
+ - '::1/128'
+ - 'fc00::/7'
+ - 'fe80::/10'
+ pool:
+ numWorkers: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+logs:
+ mode: development
+ level: error
+reload:
+ interval: 2s
+ patterns:
+ - .txt
+ services:
+ http:
+ dirs:
+ - './unit_tests'
+ recursive: true
diff --git a/plugins/reload/tests/configs/.rr-reload-3.yaml b/plugins/reload/tests/configs/.rr-reload-3.yaml
new file mode 100644
index 00000000..b97ed667
--- /dev/null
+++ b/plugins/reload/tests/configs/.rr-reload-3.yaml
@@ -0,0 +1,46 @@
+server:
+ command: php ../../../tests/psr-worker-bench.php
+ user: ''
+ group: ''
+ env:
+ RR_HTTP: 'true'
+ relay: pipes
+ relayTimeout: 20s
+http:
+ debug: true
+ address: '127.0.0.1:37388'
+ maxRequestSize: 1024
+ middleware:
+ - ''
+ uploads:
+ forbid:
+ - .php
+ - .exe
+ - .bat
+ trustedSubnets:
+ - 10.0.0.0/8
+ - 127.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
+ - '::1/128'
+ - 'fc00::/7'
+ - 'fe80::/10'
+ pool:
+ numWorkers: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+logs:
+ mode: development
+ level: error
+reload:
+ interval: 2s
+ patterns:
+ - .txt
+ services:
+ http:
+ dirs:
+ - './unit_tests'
+ - './unit_tests_copied'
+ - './dir1'
+ recursive: true
diff --git a/plugins/reload/tests/configs/.rr-reload-4.yaml b/plugins/reload/tests/configs/.rr-reload-4.yaml
new file mode 100644
index 00000000..b664b836
--- /dev/null
+++ b/plugins/reload/tests/configs/.rr-reload-4.yaml
@@ -0,0 +1,46 @@
+server:
+ command: php ../../../tests/psr-worker-bench.php
+ user: ''
+ group: ''
+ env:
+ RR_HTTP: 'true'
+ relay: pipes
+ relayTimeout: 20s
+http:
+ debug: true
+ address: '127.0.0.1:22766'
+ maxRequestSize: 1024
+ middleware:
+ - ''
+ uploads:
+ forbid:
+ - .php
+ - .exe
+ - .bat
+ trustedSubnets:
+ - 10.0.0.0/8
+ - 127.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
+ - '::1/128'
+ - 'fc00::/7'
+ - 'fe80::/10'
+ pool:
+ numWorkers: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+logs:
+ mode: development
+ level: error
+reload:
+ interval: 2s
+ patterns:
+ - .aaa
+ services:
+ http:
+ dirs:
+ - './unit_tests'
+ - './unit_tests_copied'
+ - './dir1'
+ recursive: false
diff --git a/plugins/reload/tests/configs/.rr-reload.yaml b/plugins/reload/tests/configs/.rr-reload.yaml
new file mode 100644
index 00000000..5e223db3
--- /dev/null
+++ b/plugins/reload/tests/configs/.rr-reload.yaml
@@ -0,0 +1,44 @@
+server:
+ command: php ../../../tests/psr-worker-bench.php
+ user: ''
+ group: ''
+ env:
+ RR_HTTP: 'true'
+ relay: pipes
+ relayTimeout: 20s
+http:
+ debug: true
+ address: '127.0.0.1:22388'
+ maxRequestSize: 1024
+ middleware:
+ - ''
+ uploads:
+ forbid:
+ - .php
+ - .exe
+ - .bat
+ trustedSubnets:
+ - 10.0.0.0/8
+ - 127.0.0.0/8
+ - 172.16.0.0/12
+ - 192.168.0.0/16
+ - '::1/128'
+ - 'fc00::/7'
+ - 'fe80::/10'
+ pool:
+ numWorkers: 2
+ maxJobs: 0
+ allocateTimeout: 60s
+ destroyTimeout: 60s
+logs:
+ mode: development
+ level: error
+reload:
+ interval: 1s
+ patterns:
+ - .txt
+ services:
+ http:
+ dirs:
+ - './unit_tests'
+ recursive: true
diff --git a/plugins/reload/tests/reload_plugin_test.go b/plugins/reload/tests/reload_plugin_test.go
new file mode 100644
index 00000000..8fb9474f
--- /dev/null
+++ b/plugins/reload/tests/reload_plugin_test.go
@@ -0,0 +1,822 @@
+package tests
+
+import (
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net/http"
+ "os"
+ "os/signal"
+ "path/filepath"
+ "strconv"
+ "sync"
+ "syscall"
+ "testing"
+ "time"
+
+ "github.com/spiral/endure"
+ "github.com/spiral/errors"
+ "github.com/spiral/roadrunner/v2/plugins/config"
+ httpPlugin "github.com/spiral/roadrunner/v2/plugins/http"
+ "github.com/spiral/roadrunner/v2/plugins/logger"
+ "github.com/spiral/roadrunner/v2/plugins/reload"
+ "github.com/spiral/roadrunner/v2/plugins/resetter"
+ "github.com/spiral/roadrunner/v2/plugins/server"
+ "github.com/stretchr/testify/assert"
+)
+
+const testDir string = "unit_tests"
+const testCopyToDir string = "unit_tests_copied"
+const hugeNumberOfFiles uint = 5000
+
+func TestReloadInit(t *testing.T) {
+ cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel))
+ assert.NoError(t, err)
+
+ cfg := &config.Viper{
+ Path: "configs/.rr-reload.yaml",
+ Prefix: "rr",
+ }
+
+ // try to remove, skip error
+ assert.NoError(t, freeResources(testDir))
+ err = os.Mkdir(testDir, 0755)
+ assert.NoError(t, err)
+
+ defer func() {
+ assert.NoError(t, freeResources(testDir))
+ }()
+
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &reload.Plugin{},
+ &resetter.Plugin{},
+ )
+ assert.NoError(t, err)
+
+ err = cont.Init()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ch, err := cont.Serve()
+ assert.NoError(t, err)
+
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ tt := time.NewTimer(time.Second * 10)
+
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-ch:
+ assert.Fail(t, "error", e.Error.Error())
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ case <-sig:
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ case <-tt.C:
+ // timeout
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ }
+ }
+ }()
+
+ t.Run("ReloadTestInit", reloadTestInit)
+ reloadHTTPLiveAfterReset(t, "22388")
+
+ wg.Wait()
+}
+
+func reloadTestInit(t *testing.T) {
+ err := ioutil.WriteFile(filepath.Join(testDir, "file.txt"), //nolint:gosec
+ []byte{}, 0755)
+ assert.NoError(t, err)
+}
+
+func TestReloadHugeNumberOfFiles(t *testing.T) {
+ cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel))
+ assert.NoError(t, err)
+
+ cfg := &config.Viper{
+ Path: "configs/.rr-reload.yaml",
+ Prefix: "rr",
+ }
+
+ // try to remove, skip error
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ err = os.Mkdir(testDir, 0755)
+ assert.NoError(t, err)
+ err = os.Mkdir(testCopyToDir, 0755)
+ assert.NoError(t, err)
+
+ defer func() {
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ }()
+
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &reload.Plugin{},
+ &resetter.Plugin{},
+ )
+ assert.NoError(t, err)
+
+ err = cont.Init()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ch, err := cont.Serve()
+ assert.NoError(t, err)
+
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ tt := time.NewTimer(time.Second * 100)
+
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-ch:
+ assert.Fail(t, "error", e.Error.Error())
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ case <-sig:
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ case <-tt.C:
+ // timeout
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ }
+ }
+ }()
+
+ t.Run("ReloadTestHugeNumberOfFiles", reloadHugeNumberOfFiles)
+ ttt := time.Now()
+ t.Run("ReloadRandomlyChangeFile", randomlyChangeFile)
+ if time.Since(ttt).Seconds() > 80 {
+ t.Fatal("spend too much time on reloading")
+ }
+ reloadHTTPLiveAfterReset(t, "22388")
+
+ wg.Wait()
+}
+
+func randomlyChangeFile(t *testing.T) {
+ // we know, that directory contains 5000 files (0-4999)
+ // let's try to randomly change it
+ for i := 0; i < 100; i++ {
+ // rand sleep
+ rSleep := rand.Int63n(1000) // nolint:gosec
+ time.Sleep(time.Millisecond * time.Duration(rSleep))
+ rNum := rand.Int63n(int64(hugeNumberOfFiles)) // nolint:gosec
+ err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".txt"), []byte("Hello, Gophers!"), 0755) // nolint:gosec
+ assert.NoError(t, err)
+ }
+}
+
+func reloadHugeNumberOfFiles(t *testing.T) {
+ for i := uint(0); i < hugeNumberOfFiles; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt"))
+ }
+}
+
+// Should be events only about creating files with txt ext
+func TestReloadFilterFileExt(t *testing.T) {
+ cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel))
+ assert.NoError(t, err)
+
+ cfg := &config.Viper{
+ Path: "configs/.rr-reload-2.yaml",
+ Prefix: "rr",
+ }
+
+ // try to remove, skip error
+ assert.NoError(t, freeResources(testDir))
+ err = os.Mkdir(testDir, 0755)
+ assert.NoError(t, err)
+
+ defer func() {
+ assert.NoError(t, freeResources(testDir))
+ }()
+
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &reload.Plugin{},
+ &resetter.Plugin{},
+ )
+ assert.NoError(t, err)
+
+ err = cont.Init()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ch, err := cont.Serve()
+ assert.NoError(t, err)
+
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ tt := time.NewTimer(time.Second * 40)
+
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-ch:
+ assert.Fail(t, "error", e.Error.Error())
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ case <-sig:
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ case <-tt.C:
+ // timeout
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ }
+ }
+ }()
+
+ t.Run("ReloadMakeFiles", reloadMakeFiles)
+ ttt := time.Now()
+ t.Run("ReloadFilteredExt", reloadFilteredExt)
+ if time.Since(ttt).Seconds() > 20 {
+ t.Fatal("spend too much time on reloading")
+ }
+
+ reloadHTTPLiveAfterReset(t, "27388")
+
+ wg.Wait()
+}
+
+func reloadMakeFiles(t *testing.T) {
+ for i := uint(0); i < 100; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt"))
+ }
+ for i := uint(0); i < 100; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".abc"))
+ }
+ for i := uint(0); i < 100; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".def"))
+ }
+}
+
+func reloadFilteredExt(t *testing.T) {
+ // change files with abc extension
+ for i := 0; i < 10; i++ {
+ // rand sleep
+ rSleep := rand.Int63n(1000) // nolint:gosec
+ time.Sleep(time.Millisecond * time.Duration(rSleep))
+ rNum := rand.Int63n(int64(hugeNumberOfFiles)) // nolint:gosec
+ err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".abc"), []byte("Hello, Gophers!"), 0755) // nolint:gosec
+ assert.NoError(t, err)
+ }
+
+ // change files with def extension
+ for i := 0; i < 10; i++ {
+ // rand sleep
+ rSleep := rand.Int63n(1000) // nolint:gosec
+ time.Sleep(time.Millisecond * time.Duration(rSleep))
+ rNum := rand.Int63n(int64(hugeNumberOfFiles)) // nolint:gosec
+ err := ioutil.WriteFile(filepath.Join(testDir, "file_"+strconv.Itoa(int(rNum))+".def"), []byte("Hello, Gophers!"), 0755) // nolint:gosec
+ assert.NoError(t, err)
+ }
+}
+
+// Should be events only about creating files with txt ext
+func TestReloadCopy3k(t *testing.T) {
+ cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel))
+ assert.NoError(t, err)
+
+ cfg := &config.Viper{
+ Path: "configs/.rr-reload-3.yaml",
+ Prefix: "rr",
+ }
+
+ // try to remove, skip error
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ assert.NoError(t, freeResources("dir1"))
+ err = os.Mkdir(testDir, 0755)
+ assert.NoError(t, err)
+ err = os.Mkdir(testCopyToDir, 0755)
+ assert.NoError(t, err)
+ err = os.Mkdir("dir1", 0755)
+ assert.NoError(t, err)
+
+ defer func() {
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ assert.NoError(t, freeResources("dir1"))
+ }()
+
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &reload.Plugin{},
+ &resetter.Plugin{},
+ )
+ assert.NoError(t, err)
+
+ err = cont.Init()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ch, err := cont.Serve()
+ assert.NoError(t, err)
+
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ tt := time.NewTimer(time.Second * 180)
+
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-ch:
+ assert.Fail(t, "error", e.Error.Error())
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ case <-sig:
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ case <-tt.C:
+ // timeout
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ }
+ }
+ }()
+
+ // Scenario
+ // 1
+ // Create 3k files with txt, abc, def extensions
+ // Copy files to the unit_tests_copy dir
+ // 2
+ // Delete both dirs, recreate
+ // Create 3k files with txt, abc, def extensions
+ // Move files to the unit_tests_copy dir
+ // 3
+ // Recursive
+
+ t.Run("ReloadMake3kFiles", reloadMake3kFiles)
+ ttt := time.Now()
+ t.Run("ReloadCopyFiles", reloadCopyFiles)
+ if time.Since(ttt).Seconds() > 100 {
+ t.Fatal("spend too much time on copy")
+ }
+
+ t.Run("ReloadRecursiveDirsSupport", copyFilesRecursive)
+ t.Run("RandomChangesInRecursiveDirs", randomChangesInRecursiveDirs)
+ t.Run("RemoveFilesSupport", removeFilesSupport)
+ t.Run("ReloadMoveSupport", reloadMoveSupport)
+
+ reloadHTTPLiveAfterReset(t, "37388")
+
+ wg.Wait()
+}
+
+func reloadMoveSupport(t *testing.T) {
+ t.Run("MoveSupportCopy", copyFilesRecursive)
+ // move some files
+ for i := 0; i < 50; i++ {
+ // rand sleep
+ rSleep := rand.Int63n(1000) // nolint:gosec
+ time.Sleep(time.Millisecond * time.Duration(rSleep))
+ rNum := rand.Int63n(int64(200)) // nolint:gosec
+ rDir := rand.Int63n(9) // nolint:gosec
+ rExt := rand.Int63n(3) // nolint:gosec
+
+ ext := []string{
+ ".txt",
+ ".abc",
+ ".def",
+ }
+
+ // change files with def extension
+ dirs := []string{
+ "dir1",
+ "dir1/dir2",
+ "dir1/dir2/dir3",
+ "dir1/dir2/dir3/dir4",
+ "dir1/dir2/dir3/dir4/dir5",
+ "dir1/dir2/dir3/dir4/dir5/dir6",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10",
+ }
+
+ // move file
+ err := os.Rename(filepath.Join(dirs[rDir], "file_"+strconv.Itoa(int(rNum))+ext[rExt]), filepath.Join(dirs[rDir+1], "file_"+strconv.Itoa(int(rNum))+ext[rExt]))
+ assert.NoError(t, err)
+ }
+}
+
+func removeFilesSupport(t *testing.T) {
+ // remove some files
+ for i := 0; i < 50; i++ {
+ // rand sleep
+ rSleep := rand.Int63n(1000) // nolint:gosec
+ time.Sleep(time.Millisecond * time.Duration(rSleep))
+ rNum := rand.Int63n(int64(200)) // nolint:gosec
+ rDir := rand.Int63n(10) // nolint:gosec
+ rExt := rand.Int63n(3) // nolint:gosec
+
+ ext := []string{
+ ".txt",
+ ".abc",
+ ".def",
+ }
+
+ // change files with def extension
+ dirs := []string{
+ "dir1",
+ "dir1/dir2",
+ "dir1/dir2/dir3",
+ "dir1/dir2/dir3/dir4",
+ "dir1/dir2/dir3/dir4/dir5",
+ "dir1/dir2/dir3/dir4/dir5/dir6",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10",
+ }
+ err := os.Remove(filepath.Join(dirs[rDir], "file_"+strconv.Itoa(int(rNum))+ext[rExt]))
+ assert.NoError(t, err)
+ }
+}
+
+func randomChangesInRecursiveDirs(t *testing.T) {
+ // change files with def extension
+ dirs := []string{
+ "dir1",
+ "dir1/dir2",
+ "dir1/dir2/dir3",
+ "dir1/dir2/dir3/dir4",
+ "dir1/dir2/dir3/dir4/dir5",
+ "dir1/dir2/dir3/dir4/dir5/dir6",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9",
+ "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10",
+ }
+
+ ext := []string{
+ ".txt",
+ ".abc",
+ ".def",
+ }
+
+ filenames := []string{
+ "file_", // should be update
+ "foo_", // should be created
+ "bar_", // should be created
+ }
+ for i := 0; i < 50; i++ {
+ // rand sleep
+ rSleep := rand.Int63n(1000) // nolint:gosec
+ time.Sleep(time.Millisecond * time.Duration(rSleep))
+ rNum := rand.Int63n(int64(200)) // nolint:gosec
+ rDir := rand.Int63n(10) // nolint:gosec
+ rExt := rand.Int63n(3) // nolint:gosec
+ rName := rand.Int63n(3) // nolint:gosec
+
+ err := ioutil.WriteFile(filepath.Join(dirs[rDir], filenames[rName]+strconv.Itoa(int(rNum))+ext[rExt]), []byte("Hello, Gophers!"), 0755) // nolint:gosec
+ assert.NoError(t, err)
+ }
+}
+
+func copyFilesRecursive(t *testing.T) {
+ err := copyDir(testDir, "dir1")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9")
+ assert.NoError(t, err)
+ err = copyDir(testDir, "dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9/dir10")
+ assert.NoError(t, err)
+}
+
+func reloadCopyFiles(t *testing.T) {
+ err := copyDir(testDir, testCopyToDir)
+ assert.NoError(t, err)
+
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ err = os.Mkdir(testDir, 0755)
+ assert.NoError(t, err)
+ err = os.Mkdir(testCopyToDir, 0755)
+ assert.NoError(t, err)
+
+ // recreate files
+ for i := uint(0); i < 200; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt"))
+ }
+ for i := uint(0); i < 200; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".abc"))
+ }
+ for i := uint(0); i < 200; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".def"))
+ }
+
+ err = copyDir(testDir, testCopyToDir)
+ assert.NoError(t, err)
+}
+
+func reloadMake3kFiles(t *testing.T) {
+ for i := uint(0); i < 100; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".txt"))
+ }
+ for i := uint(0); i < 100; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".abc"))
+ }
+ for i := uint(0); i < 100; i++ {
+ assert.NoError(t, makeFile("file_"+strconv.Itoa(int(i))+".def"))
+ }
+}
+
+func TestReloadNoRecursion(t *testing.T) {
+ cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.ErrorLevel))
+ assert.NoError(t, err)
+
+ cfg := &config.Viper{
+ Path: "configs/.rr-reload-4.yaml",
+ Prefix: "rr",
+ }
+
+ // try to remove, skip error
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ assert.NoError(t, freeResources("dir1"))
+ err = os.Mkdir(testDir, 0755)
+ assert.NoError(t, err)
+
+ err = os.Mkdir("dir1", 0755)
+ assert.NoError(t, err)
+
+ err = os.Mkdir(testCopyToDir, 0755)
+ assert.NoError(t, err)
+
+ defer func() {
+ assert.NoError(t, freeResources(testDir))
+ assert.NoError(t, freeResources(testCopyToDir))
+ assert.NoError(t, freeResources("dir1"))
+ }()
+
+ err = cont.RegisterAll(
+ cfg,
+ &logger.ZapLogger{},
+ &server.Plugin{},
+ &httpPlugin.Plugin{},
+ &reload.Plugin{},
+ &resetter.Plugin{},
+ )
+ assert.NoError(t, err)
+
+ err = cont.Init()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ch, err := cont.Serve()
+ assert.NoError(t, err)
+
+ sig := make(chan os.Signal, 1)
+ signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+
+ wg := &sync.WaitGroup{}
+ wg.Add(1)
+
+ tt := time.NewTimer(time.Second * 30)
+
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-ch:
+ assert.Fail(t, "error", e.Error.Error())
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ case <-sig:
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ case <-tt.C:
+ // timeout
+ err = cont.Stop()
+ if err != nil {
+ assert.FailNow(t, "error", err.Error())
+ }
+ return
+ }
+ }
+ }()
+ t.Run("ReloadMakeFiles", reloadMakeFiles) // make files in the testDir
+ t.Run("ReloadCopyFilesRecursive", reloadCopyFiles)
+
+ reloadHTTPLiveAfterReset(t, "22766")
+
+ wg.Wait()
+}
+
+// ========================================================================
+
+func reloadHTTPLiveAfterReset(t *testing.T, port string) {
+ req, err := http.NewRequest("GET", "http://localhost:"+port, nil)
+ assert.NoError(t, err)
+
+ r, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ b, err := ioutil.ReadAll(r.Body)
+ assert.NoError(t, err)
+
+ assert.Equal(t, 200, r.StatusCode)
+ assert.Equal(t, "hello world", string(b))
+
+ err = r.Body.Close()
+ assert.NoError(t, err)
+}
+
+func freeResources(path string) error {
+ return os.RemoveAll(path)
+}
+
+func makeFile(filename string) error {
+ return ioutil.WriteFile(filepath.Join(testDir, filename), []byte{}, 0755) //nolint:gosec
+}
+
+func copyDir(src string, dst string) error {
+ src = filepath.Clean(src)
+ dst = filepath.Clean(dst)
+
+ si, err := os.Stat(src)
+ if err != nil {
+ return err
+ }
+ if !si.IsDir() {
+ return errors.E(errors.Str("source is not a directory"))
+ }
+
+ _, err = os.Stat(dst)
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+
+ err = os.MkdirAll(dst, si.Mode())
+ if err != nil {
+ return err
+ }
+
+ entries, err := ioutil.ReadDir(src)
+ if err != nil {
+ return err
+ }
+
+ for _, entry := range entries {
+ srcPath := filepath.Join(src, entry.Name())
+ dstPath := filepath.Join(dst, entry.Name())
+
+ if entry.IsDir() {
+ err = copyDir(srcPath, dstPath)
+ if err != nil {
+ return err
+ }
+ } else {
+ // Skip symlinks.
+ if entry.Mode()&os.ModeSymlink != 0 {
+ continue
+ }
+
+ err = copyFile(srcPath, dstPath)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func copyFile(src, dst string) error {
+ in, err := os.Open(src)
+ if err != nil {
+ return errors.E(err)
+ }
+ defer func() {
+ _ = in.Close()
+ }()
+
+ out, err := os.Create(dst)
+ if err != nil {
+ return errors.E(err)
+ }
+ defer func() {
+ _ = out.Close()
+ }()
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return errors.E(err)
+ }
+
+ err = out.Sync()
+ if err != nil {
+ return errors.E(err)
+ }
+
+ si, err := os.Stat(src)
+ if err != nil {
+ return errors.E(err)
+ }
+ err = os.Chmod(dst, si.Mode())
+ if err != nil {
+ return errors.E(err)
+ }
+ return nil
+}