lock_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package webdav
  5. import (
  6. "fmt"
  7. "math/rand"
  8. "path"
  9. "reflect"
  10. "sort"
  11. "strings"
  12. "testing"
  13. "time"
  14. )
  15. func TestWalkToRoot(t *testing.T) {
  16. testCases := []struct {
  17. name string
  18. want []string
  19. }{{
  20. "/a/b/c/d",
  21. []string{
  22. "/a/b/c/d",
  23. "/a/b/c",
  24. "/a/b",
  25. "/a",
  26. "/",
  27. },
  28. }, {
  29. "/a",
  30. []string{
  31. "/a",
  32. "/",
  33. },
  34. }, {
  35. "/",
  36. []string{
  37. "/",
  38. },
  39. }}
  40. for _, tc := range testCases {
  41. var got []string
  42. if !walkToRoot(tc.name, func(name0 string, first bool) bool {
  43. if first != (len(got) == 0) {
  44. t.Errorf("name=%q: first=%t but len(got)==%d", tc.name, first, len(got))
  45. return false
  46. }
  47. got = append(got, name0)
  48. return true
  49. }) {
  50. continue
  51. }
  52. if !reflect.DeepEqual(got, tc.want) {
  53. t.Errorf("name=%q:\ngot %q\nwant %q", tc.name, got, tc.want)
  54. }
  55. }
  56. }
  57. // lockTestNames are the names of a set of mutually compatible locks. For each
  58. // name fragment:
  59. // - _ means no explicit lock.
  60. // - i means a infinite-depth lock,
  61. // - z means a zero-depth lock,
  62. var lockTestNames = []string{
  63. "/_/_/_/_/z",
  64. "/_/_/i",
  65. "/_/z",
  66. "/_/z/i",
  67. "/_/z/z",
  68. "/_/z/_/i",
  69. "/_/z/_/z",
  70. "/i",
  71. "/z",
  72. "/z/_/i",
  73. "/z/_/z",
  74. }
  75. func lockTestDepth(name string) int {
  76. switch name[len(name)-1] {
  77. case 'i':
  78. return -1
  79. case 'z':
  80. return 0
  81. }
  82. panic(fmt.Sprintf("lock name %q did not end with 'i' or 'z'", name))
  83. }
  84. func TestMemLSCanCreate(t *testing.T) {
  85. now := time.Unix(0, 0)
  86. m := NewMemLS().(*memLS)
  87. for _, name := range lockTestNames {
  88. _, err := m.Create(now, LockDetails{
  89. Depth: lockTestDepth(name),
  90. Duration: -1,
  91. Root: name,
  92. })
  93. if err != nil {
  94. t.Fatalf("creating lock for %q: %v", name, err)
  95. }
  96. }
  97. wantCanCreate := func(name string, depth int) bool {
  98. for _, n := range lockTestNames {
  99. switch {
  100. case n == name:
  101. // An existing lock has the same name as the proposed lock.
  102. return false
  103. case strings.HasPrefix(n, name):
  104. // An existing lock would be a child of the proposed lock,
  105. // which conflicts if the proposed lock has infinite depth.
  106. if depth < 0 {
  107. return false
  108. }
  109. case strings.HasPrefix(name, n):
  110. // An existing lock would be an ancestor of the proposed lock,
  111. // which conflicts if the ancestor has infinite depth.
  112. if n[len(n)-1] == 'i' {
  113. return false
  114. }
  115. }
  116. }
  117. return true
  118. }
  119. var check func(int, string)
  120. check = func(recursion int, name string) {
  121. for _, depth := range []int{-1, 0} {
  122. got := m.canCreate(name, depth)
  123. want := wantCanCreate(name, depth)
  124. if got != want {
  125. t.Errorf("canCreate name=%q depth=%d: got %t, want %t", name, depth, got, want)
  126. }
  127. }
  128. if recursion == 6 {
  129. return
  130. }
  131. if name != "/" {
  132. name += "/"
  133. }
  134. for _, c := range "_iz" {
  135. check(recursion+1, name+string(c))
  136. }
  137. }
  138. check(0, "/")
  139. }
  140. func TestMemLSCreateUnlock(t *testing.T) {
  141. now := time.Unix(0, 0)
  142. m := NewMemLS().(*memLS)
  143. rng := rand.New(rand.NewSource(0))
  144. tokens := map[string]string{}
  145. for i := 0; i < 1000; i++ {
  146. name := lockTestNames[rng.Intn(len(lockTestNames))]
  147. if token := tokens[name]; token != "" {
  148. if err := m.Unlock(now, token); err != nil {
  149. t.Fatalf("iteration #%d: unlock %q: %v", i, name, err)
  150. }
  151. tokens[name] = ""
  152. } else {
  153. token, err := m.Create(now, LockDetails{
  154. Depth: lockTestDepth(name),
  155. Duration: -1,
  156. Root: name,
  157. })
  158. if err != nil {
  159. t.Fatalf("iteration #%d: create %q: %v", i, name, err)
  160. }
  161. tokens[name] = token
  162. }
  163. if err := m.consistent(); err != nil {
  164. t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
  165. }
  166. }
  167. }
  168. func (m *memLS) consistent() error {
  169. m.mu.Lock()
  170. defer m.mu.Unlock()
  171. // If m.byName is non-empty, then it must contain an entry for the root "/",
  172. // and its refCount should equal the number of locked nodes.
  173. if len(m.byName) > 0 {
  174. n := m.byName["/"]
  175. if n == nil {
  176. return fmt.Errorf(`non-empty m.byName does not contain the root "/"`)
  177. }
  178. if n.refCount != len(m.byToken) {
  179. return fmt.Errorf("root node refCount=%d, differs from len(m.byToken)=%d", n.refCount, len(m.byToken))
  180. }
  181. }
  182. for name, n := range m.byName {
  183. // The map keys should be consistent with the node's copy of the key.
  184. if n.details.Root != name {
  185. return fmt.Errorf("node name %q != byName map key %q", n.details.Root, name)
  186. }
  187. // A name must be clean, and start with a "/".
  188. if len(name) == 0 || name[0] != '/' {
  189. return fmt.Errorf(`node name %q does not start with "/"`, name)
  190. }
  191. if name != path.Clean(name) {
  192. return fmt.Errorf(`node name %q is not clean`, name)
  193. }
  194. // A node's refCount should be positive.
  195. if n.refCount <= 0 {
  196. return fmt.Errorf("non-positive refCount for node at name %q", name)
  197. }
  198. // A node's refCount should be the number of self-or-descendents that
  199. // are locked (i.e. have a non-empty token).
  200. var list []string
  201. for name0, n0 := range m.byName {
  202. // All of lockTestNames' name fragments are one byte long: '_', 'i' or 'z',
  203. // so strings.HasPrefix is equivalent to self-or-descendent name match.
  204. // We don't have to worry about "/foo/bar" being a false positive match
  205. // for "/foo/b".
  206. if strings.HasPrefix(name0, name) && n0.token != "" {
  207. list = append(list, name0)
  208. }
  209. }
  210. if n.refCount != len(list) {
  211. sort.Strings(list)
  212. return fmt.Errorf("node at name %q has refCount %d but locked self-or-descendents are %q (len=%d)",
  213. name, n.refCount, list, len(list))
  214. }
  215. // A node n is in m.byToken if it has a non-empty token.
  216. if n.token != "" {
  217. if _, ok := m.byToken[n.token]; !ok {
  218. return fmt.Errorf("node at name %q has token %q but not in m.byToken", name, n.token)
  219. }
  220. }
  221. }
  222. for token, n := range m.byToken {
  223. // The map keys should be consistent with the node's copy of the key.
  224. if n.token != token {
  225. return fmt.Errorf("node token %q != byToken map key %q", n.token, token)
  226. }
  227. // Every node in m.byToken is in m.byName.
  228. if _, ok := m.byName[n.details.Root]; !ok {
  229. return fmt.Errorf("node at name %q in m.byToken but not in m.byName", n.details.Root)
  230. }
  231. }
  232. return nil
  233. }