gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package globallock
  4. import (
  5. "context"
  6. "os"
  7. "sync"
  8. "testing"
  9. "time"
  10. "github.com/go-redsync/redsync/v4"
  11. "github.com/stretchr/testify/assert"
  12. "github.com/stretchr/testify/require"
  13. )
  14. func TestLocker(t *testing.T) {
  15. t.Run("redis", func(t *testing.T) {
  16. url := "redis://127.0.0.1:6379/0"
  17. if os.Getenv("CI") == "" {
  18. // Make it possible to run tests against a local redis instance
  19. url = os.Getenv("TEST_REDIS_URL")
  20. if url == "" {
  21. t.Skip("TEST_REDIS_URL not set and not running in CI")
  22. return
  23. }
  24. }
  25. oldExpiry := redisLockExpiry
  26. redisLockExpiry = 5 * time.Second // make it shorter for testing
  27. defer func() {
  28. redisLockExpiry = oldExpiry
  29. }()
  30. locker := NewRedisLocker(url)
  31. testLocker(t, locker)
  32. testRedisLocker(t, locker.(*redisLocker))
  33. require.NoError(t, locker.(*redisLocker).Close())
  34. })
  35. t.Run("memory", func(t *testing.T) {
  36. locker := NewMemoryLocker()
  37. testLocker(t, locker)
  38. testMemoryLocker(t, locker.(*memoryLocker))
  39. })
  40. }
  41. func testLocker(t *testing.T, locker Locker) {
  42. t.Run("lock", func(t *testing.T) {
  43. parentCtx := t.Context()
  44. release, err := locker.Lock(parentCtx, "test")
  45. defer release()
  46. assert.NoError(t, err)
  47. func() {
  48. ctx, cancel := context.WithTimeout(t.Context(), time.Second)
  49. defer cancel()
  50. release, err := locker.Lock(ctx, "test")
  51. defer release()
  52. assert.Error(t, err)
  53. }()
  54. release()
  55. func() {
  56. release, err := locker.Lock(t.Context(), "test")
  57. defer release()
  58. assert.NoError(t, err)
  59. }()
  60. })
  61. t.Run("try lock", func(t *testing.T) {
  62. parentCtx := t.Context()
  63. ok, release, err := locker.TryLock(parentCtx, "test")
  64. defer release()
  65. assert.True(t, ok)
  66. assert.NoError(t, err)
  67. func() {
  68. ctx, cancel := context.WithTimeout(t.Context(), time.Second)
  69. defer cancel()
  70. ok, release, err := locker.TryLock(ctx, "test")
  71. defer release()
  72. assert.False(t, ok)
  73. assert.NoError(t, err)
  74. }()
  75. release()
  76. func() {
  77. ok, release, _ := locker.TryLock(t.Context(), "test")
  78. defer release()
  79. assert.True(t, ok)
  80. }()
  81. })
  82. t.Run("wait and acquired", func(t *testing.T) {
  83. ctx := t.Context()
  84. release, err := locker.Lock(ctx, "test")
  85. require.NoError(t, err)
  86. wg := &sync.WaitGroup{}
  87. wg.Go(func() {
  88. started := time.Now()
  89. release, err := locker.Lock(t.Context(), "test") // should be blocked for seconds
  90. defer release()
  91. assert.Greater(t, time.Since(started), time.Second)
  92. assert.NoError(t, err)
  93. })
  94. time.Sleep(2 * time.Second)
  95. release()
  96. wg.Wait()
  97. })
  98. t.Run("multiple release", func(t *testing.T) {
  99. ctx := t.Context()
  100. release1, err := locker.Lock(ctx, "test")
  101. require.NoError(t, err)
  102. release1()
  103. release2, err := locker.Lock(ctx, "test")
  104. defer release2()
  105. require.NoError(t, err)
  106. // Call release1 again,
  107. // it should not panic or block,
  108. // and it shouldn't affect the other lock
  109. release1()
  110. ok, release3, err := locker.TryLock(ctx, "test")
  111. defer release3()
  112. require.NoError(t, err)
  113. // It should be able to acquire the lock;
  114. // otherwise, it means the lock has been released by release1
  115. assert.False(t, ok)
  116. })
  117. }
  118. // testMemoryLocker does specific tests for memoryLocker
  119. func testMemoryLocker(t *testing.T, locker *memoryLocker) {
  120. // nothing to do
  121. }
  122. // testRedisLocker does specific tests for redisLocker
  123. func testRedisLocker(t *testing.T, locker *redisLocker) {
  124. defer func() {
  125. // This case should be tested at the end.
  126. // Otherwise, it will affect other tests.
  127. t.Run("close", func(t *testing.T) {
  128. assert.NoError(t, locker.Close())
  129. _, err := locker.Lock(t.Context(), "test")
  130. assert.Error(t, err)
  131. })
  132. }()
  133. t.Run("failed extend", func(t *testing.T) {
  134. release, err := locker.Lock(t.Context(), "test")
  135. defer release()
  136. require.NoError(t, err)
  137. // It simulates that there are some problems with extending like network issues or redis server down.
  138. v, ok := locker.mutexM.Load("test")
  139. require.True(t, ok)
  140. m := v.(*redsync.Mutex)
  141. _, _ = m.Unlock() // release it to make it impossible to extend
  142. // In current design, callers can't know the lock can't be extended.
  143. // Just keep this case to improve the test coverage.
  144. })
  145. }