tunny_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*
  2. Copyright (c) 2014 Ashley Jeffs
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. package tunny
  20. import (
  21. "sync"
  22. "testing"
  23. "time"
  24. )
  25. /*--------------------------------------------------------------------------------------------------
  26. */
  27. func TestBasicJob(t *testing.T) {
  28. pool, err := CreatePool(1, func(in interface{}) interface{} {
  29. intVal := in.(int)
  30. return intVal * 2
  31. }).Open()
  32. if err != nil {
  33. t.Errorf("Failed to create pool: %v", err)
  34. return
  35. }
  36. defer pool.Close()
  37. for i := 0; i < 1; i++ {
  38. ret, err := pool.SendWork(10)
  39. if err != nil {
  40. t.Errorf("Failed to send work: %v", err)
  41. return
  42. }
  43. retInt := ret.(int)
  44. if ret != 20 {
  45. t.Errorf("Wrong return value: %v != %v", 20, retInt)
  46. }
  47. }
  48. }
  49. func TestParallelJobs(t *testing.T) {
  50. nWorkers := 10
  51. jobGroup := sync.WaitGroup{}
  52. testGroup := sync.WaitGroup{}
  53. pool, err := CreatePool(nWorkers, func(in interface{}) interface{} {
  54. jobGroup.Done()
  55. jobGroup.Wait()
  56. intVal := in.(int)
  57. return intVal * 2
  58. }).Open()
  59. if err != nil {
  60. t.Errorf("Failed to create pool: %v", err)
  61. return
  62. }
  63. defer pool.Close()
  64. for j := 0; j < 1; j++ {
  65. jobGroup.Add(nWorkers)
  66. testGroup.Add(nWorkers)
  67. for i := 0; i < nWorkers; i++ {
  68. go func() {
  69. ret, err := pool.SendWork(10)
  70. if err != nil {
  71. t.Errorf("Failed to send work: %v", err)
  72. return
  73. }
  74. retInt := ret.(int)
  75. if ret != 20 {
  76. t.Errorf("Wrong return value: %v != %v", 20, retInt)
  77. }
  78. testGroup.Done()
  79. }()
  80. }
  81. testGroup.Wait()
  82. }
  83. }
  84. /*--------------------------------------------------------------------------------------------------
  85. */
  86. // Basic worker implementation
  87. type dummyWorker struct {
  88. ready bool
  89. t *testing.T
  90. }
  91. func (d *dummyWorker) TunnyJob(in interface{}) interface{} {
  92. if !d.ready {
  93. d.t.Errorf("TunnyJob called without polling TunnyReady")
  94. }
  95. d.ready = false
  96. return in
  97. }
  98. func (d *dummyWorker) TunnyReady() bool {
  99. d.ready = true
  100. return d.ready
  101. }
  102. // Test the pool with a basic worker implementation
  103. func TestDummyWorker(t *testing.T) {
  104. pool, err := CreateCustomPool([]TunnyWorker{&dummyWorker{t: t}}).Open()
  105. if err != nil {
  106. t.Errorf("Failed to create pool: %v", err)
  107. return
  108. }
  109. defer pool.Close()
  110. for i := 0; i < 100; i++ {
  111. if result, err := pool.SendWork(12); err != nil {
  112. t.Errorf("Failed to send work: %v", err)
  113. } else if resInt, ok := result.(int); !ok || resInt != 12 {
  114. t.Errorf("Unexpected result from job: %v != %v", 12, result)
  115. }
  116. }
  117. }
  118. // Extended worker implementation
  119. type dummyExtWorker struct {
  120. dummyWorker
  121. initialized bool
  122. }
  123. func (d *dummyExtWorker) TunnyJob(in interface{}) interface{} {
  124. if !d.initialized {
  125. d.t.Errorf("TunnyJob called without calling TunnyInitialize")
  126. }
  127. return d.dummyWorker.TunnyJob(in)
  128. }
  129. func (d *dummyExtWorker) TunnyInitialize() {
  130. d.initialized = true
  131. }
  132. func (d *dummyExtWorker) TunnyTerminate() {
  133. if !d.initialized {
  134. d.t.Errorf("TunnyTerminate called without calling TunnyInitialize")
  135. }
  136. d.initialized = false
  137. }
  138. // Test the pool with an extended worker implementation
  139. func TestDummyExtWorker(t *testing.T) {
  140. pool, err := CreateCustomPool(
  141. []TunnyWorker{
  142. &dummyExtWorker{
  143. dummyWorker: dummyWorker{t: t},
  144. },
  145. }).Open()
  146. if err != nil {
  147. t.Errorf("Failed to create pool: %v", err)
  148. return
  149. }
  150. defer pool.Close()
  151. for i := 0; i < 100; i++ {
  152. if result, err := pool.SendWork(12); err != nil {
  153. t.Errorf("Failed to send work: %v", err)
  154. } else if resInt, ok := result.(int); !ok || resInt != 12 {
  155. t.Errorf("Unexpected result from job: %v != %v", 12, result)
  156. }
  157. }
  158. }
  159. // Extended and interruptible worker implementation
  160. type dummyExtIntWorker struct {
  161. dummyExtWorker
  162. jobLock *sync.Mutex
  163. }
  164. func (d *dummyExtIntWorker) TunnyJob(in interface{}) interface{} {
  165. d.jobLock.Lock()
  166. d.jobLock.Unlock()
  167. return d.dummyExtWorker.TunnyJob(in)
  168. }
  169. func (d *dummyExtIntWorker) TunnyReady() bool {
  170. d.jobLock.Lock()
  171. return d.dummyExtWorker.TunnyReady()
  172. }
  173. func (d *dummyExtIntWorker) TunnyInterrupt() {
  174. d.jobLock.Unlock()
  175. }
  176. // Test the pool with an extended and interruptible worker implementation
  177. func TestDummyExtIntWorker(t *testing.T) {
  178. pool, err := CreateCustomPool(
  179. []TunnyWorker{
  180. &dummyExtIntWorker{
  181. dummyExtWorker: dummyExtWorker{
  182. dummyWorker: dummyWorker{t: t},
  183. },
  184. jobLock: &sync.Mutex{},
  185. },
  186. }).Open()
  187. if err != nil {
  188. t.Errorf("Failed to create pool: %v", err)
  189. return
  190. }
  191. defer pool.Close()
  192. for i := 0; i < 100; i++ {
  193. if _, err := pool.SendWorkTimed(1, nil); err == nil {
  194. t.Errorf("Expected timeout from dummyExtIntWorker.")
  195. }
  196. }
  197. }
  198. func TestNumWorkers(t *testing.T) {
  199. numWorkers := 10
  200. pool, err := CreatePoolGeneric(numWorkers).Open()
  201. if err != nil {
  202. t.Errorf("Failed to create pool: %v", err)
  203. return
  204. }
  205. defer pool.Close()
  206. actual := pool.NumWorkers()
  207. if actual != numWorkers {
  208. t.Errorf("Expected to get %d workers, but got %d", numWorkers, actual)
  209. }
  210. }
  211. var waitHalfSecond = func() {
  212. time.Sleep(500 * time.Millisecond)
  213. }
  214. func TestNumPendingReportsAllWorkersWithNoWork(t *testing.T) {
  215. numWorkers := 10
  216. pool, err := CreatePoolGeneric(numWorkers).Open()
  217. if err != nil {
  218. t.Errorf("Failed to create pool: %v", err)
  219. return
  220. }
  221. defer pool.Close()
  222. actual := pool.NumPendingAsyncJobs()
  223. if actual != 0 {
  224. t.Errorf("Expected to get 0 pending jobs when pool is quiet, but got %d", actual)
  225. }
  226. }
  227. func TestNumPendingReportsNotAllWorkersWhenSomeBusy(t *testing.T) {
  228. numWorkers := 10
  229. pool, err := CreatePoolGeneric(numWorkers).Open()
  230. if err != nil {
  231. t.Errorf("Failed to create pool: %v", err)
  232. return
  233. }
  234. defer pool.Close()
  235. pool.SendWorkAsync(waitHalfSecond, nil)
  236. actual := pool.NumPendingAsyncJobs()
  237. expected := int32(1)
  238. if actual != expected {
  239. t.Errorf("Expected to get %d pending jobs when pool has work, but got %d", expected, actual)
  240. }
  241. }
  242. /*--------------------------------------------------------------------------------------------------
  243. */