lock_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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. "strconv"
  12. "strings"
  13. "testing"
  14. "time"
  15. )
  16. func TestWalkToRoot(t *testing.T) {
  17. testCases := []struct {
  18. name string
  19. want []string
  20. }{{
  21. "/a/b/c/d",
  22. []string{
  23. "/a/b/c/d",
  24. "/a/b/c",
  25. "/a/b",
  26. "/a",
  27. "/",
  28. },
  29. }, {
  30. "/a",
  31. []string{
  32. "/a",
  33. "/",
  34. },
  35. }, {
  36. "/",
  37. []string{
  38. "/",
  39. },
  40. }}
  41. for _, tc := range testCases {
  42. var got []string
  43. if !walkToRoot(tc.name, func(name0 string, first bool) bool {
  44. if first != (len(got) == 0) {
  45. t.Errorf("name=%q: first=%t but len(got)==%d", tc.name, first, len(got))
  46. return false
  47. }
  48. got = append(got, name0)
  49. return true
  50. }) {
  51. continue
  52. }
  53. if !reflect.DeepEqual(got, tc.want) {
  54. t.Errorf("name=%q:\ngot %q\nwant %q", tc.name, got, tc.want)
  55. }
  56. }
  57. }
  58. var lockTestDurations = []time.Duration{
  59. -1, // A negative duration means to never expire.
  60. 0, // A zero duration means to expire immediately.
  61. 100 * time.Hour, // A very large duration will not expire in these tests.
  62. }
  63. // lockTestNames are the names of a set of mutually compatible locks. For each
  64. // name fragment:
  65. // - _ means no explicit lock.
  66. // - i means a infinite-depth lock,
  67. // - z means a zero-depth lock,
  68. var lockTestNames = []string{
  69. "/_/_/_/_/z",
  70. "/_/_/i",
  71. "/_/z",
  72. "/_/z/i",
  73. "/_/z/z",
  74. "/_/z/_/i",
  75. "/_/z/_/z",
  76. "/i",
  77. "/z",
  78. "/z/_/i",
  79. "/z/_/z",
  80. }
  81. func lockTestZeroDepth(name string) bool {
  82. switch name[len(name)-1] {
  83. case 'i':
  84. return false
  85. case 'z':
  86. return true
  87. }
  88. panic(fmt.Sprintf("lock name %q did not end with 'i' or 'z'", name))
  89. }
  90. func TestMemLSCanCreate(t *testing.T) {
  91. now := time.Unix(0, 0)
  92. m := NewMemLS().(*memLS)
  93. for _, name := range lockTestNames {
  94. _, err := m.Create(now, LockDetails{
  95. Root: name,
  96. Duration: -1,
  97. ZeroDepth: lockTestZeroDepth(name),
  98. })
  99. if err != nil {
  100. t.Fatalf("creating lock for %q: %v", name, err)
  101. }
  102. }
  103. wantCanCreate := func(name string, zeroDepth bool) bool {
  104. for _, n := range lockTestNames {
  105. switch {
  106. case n == name:
  107. // An existing lock has the same name as the proposed lock.
  108. return false
  109. case strings.HasPrefix(n, name):
  110. // An existing lock would be a child of the proposed lock,
  111. // which conflicts if the proposed lock has infinite depth.
  112. if !zeroDepth {
  113. return false
  114. }
  115. case strings.HasPrefix(name, n):
  116. // An existing lock would be an ancestor of the proposed lock,
  117. // which conflicts if the ancestor has infinite depth.
  118. if n[len(n)-1] == 'i' {
  119. return false
  120. }
  121. }
  122. }
  123. return true
  124. }
  125. var check func(int, string)
  126. check = func(recursion int, name string) {
  127. for _, zeroDepth := range []bool{false, true} {
  128. got := m.canCreate(name, zeroDepth)
  129. want := wantCanCreate(name, zeroDepth)
  130. if got != want {
  131. t.Errorf("canCreate name=%q zeroDepth=%d: got %t, want %t", name, zeroDepth, got, want)
  132. }
  133. }
  134. if recursion == 6 {
  135. return
  136. }
  137. if name != "/" {
  138. name += "/"
  139. }
  140. for _, c := range "_iz" {
  141. check(recursion+1, name+string(c))
  142. }
  143. }
  144. check(0, "/")
  145. }
  146. func TestMemLSExpiry(t *testing.T) {
  147. m := NewMemLS().(*memLS)
  148. testCases := []string{
  149. "setNow 0",
  150. "create /a.5",
  151. "want /a.5",
  152. "create /c.6",
  153. "want /a.5 /c.6",
  154. "create /a/b.7",
  155. "want /a.5 /a/b.7 /c.6",
  156. "setNow 4",
  157. "want /a.5 /a/b.7 /c.6",
  158. "setNow 5",
  159. "want /a/b.7 /c.6",
  160. "setNow 6",
  161. "want /a/b.7",
  162. "setNow 7",
  163. "want ",
  164. "setNow 8",
  165. "want ",
  166. "create /a.12",
  167. "create /b.13",
  168. "create /c.15",
  169. "create /a/d.16",
  170. "want /a.12 /a/d.16 /b.13 /c.15",
  171. "refresh /a.14",
  172. "want /a.14 /a/d.16 /b.13 /c.15",
  173. "setNow 12",
  174. "want /a.14 /a/d.16 /b.13 /c.15",
  175. "setNow 13",
  176. "want /a.14 /a/d.16 /c.15",
  177. "setNow 14",
  178. "want /a/d.16 /c.15",
  179. "refresh /a/d.20",
  180. "refresh /c.20",
  181. "want /a/d.20 /c.20",
  182. "setNow 20",
  183. "want ",
  184. }
  185. tokens := map[string]string{}
  186. zTime := time.Unix(0, 0)
  187. now := zTime
  188. for i, tc := range testCases {
  189. j := strings.IndexByte(tc, ' ')
  190. if j < 0 {
  191. t.Fatalf("test case #%d %q: invalid command", i, tc)
  192. }
  193. op, arg := tc[:j], tc[j+1:]
  194. switch op {
  195. default:
  196. t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
  197. case "create", "refresh":
  198. parts := strings.Split(arg, ".")
  199. if len(parts) != 2 {
  200. t.Fatalf("test case #%d %q: invalid create", i, tc)
  201. }
  202. root := parts[0]
  203. d, err := strconv.Atoi(parts[1])
  204. if err != nil {
  205. t.Fatalf("test case #%d %q: invalid duration", i, tc)
  206. }
  207. dur := time.Unix(0, 0).Add(time.Duration(d) * time.Second).Sub(now)
  208. switch op {
  209. case "create":
  210. token, err := m.Create(now, LockDetails{
  211. Root: root,
  212. Duration: dur,
  213. ZeroDepth: true,
  214. })
  215. if err != nil {
  216. t.Fatalf("test case #%d %q: Create: %v", i, tc, err)
  217. }
  218. tokens[root] = token
  219. case "refresh":
  220. token := tokens[root]
  221. if token == "" {
  222. t.Fatalf("test case #%d %q: no token for %q", i, tc, root)
  223. }
  224. got, err := m.Refresh(now, token, dur)
  225. if err != nil {
  226. t.Fatalf("test case #%d %q: Refresh: %v", i, tc, err)
  227. }
  228. want := LockDetails{
  229. Root: root,
  230. Duration: dur,
  231. ZeroDepth: true,
  232. }
  233. if got != want {
  234. t.Fatalf("test case #%d %q:\ngot %v\nwant %v", i, tc, got, want)
  235. }
  236. }
  237. case "setNow":
  238. d, err := strconv.Atoi(arg)
  239. if err != nil {
  240. t.Fatalf("test case #%d %q: invalid duration", i, tc)
  241. }
  242. now = time.Unix(0, 0).Add(time.Duration(d) * time.Second)
  243. case "want":
  244. m.mu.Lock()
  245. m.collectExpiredNodes(now)
  246. got := make([]string, 0, len(m.byToken))
  247. for _, n := range m.byToken {
  248. got = append(got, fmt.Sprintf("%s.%d",
  249. n.details.Root, n.expiry.Sub(zTime)/time.Second))
  250. }
  251. m.mu.Unlock()
  252. sort.Strings(got)
  253. want := []string{}
  254. if arg != "" {
  255. want = strings.Split(arg, " ")
  256. }
  257. if !reflect.DeepEqual(got, want) {
  258. t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, want)
  259. }
  260. }
  261. if err := m.consistent(); err != nil {
  262. t.Fatalf("test case #%d %q: inconsistent state: %v", i, tc, err)
  263. }
  264. }
  265. }
  266. func TestMemLSCreateRefreshUnlock(t *testing.T) {
  267. now := time.Unix(0, 0)
  268. m := NewMemLS().(*memLS)
  269. rng := rand.New(rand.NewSource(0))
  270. tokens := map[string]string{}
  271. nCreate, nRefresh, nUnlock := 0, 0, 0
  272. const N = 2000
  273. for i := 0; i < N; i++ {
  274. name := lockTestNames[rng.Intn(len(lockTestNames))]
  275. duration := lockTestDurations[rng.Intn(len(lockTestDurations))]
  276. unlocked := false
  277. // If the name was already locked, there's a 50-50 chance that
  278. // we refresh or unlock it. Otherwise, we create a lock.
  279. token := tokens[name]
  280. if token != "" {
  281. if rng.Intn(2) == 0 {
  282. nRefresh++
  283. if _, err := m.Refresh(now, token, duration); err != nil {
  284. t.Fatalf("iteration #%d: Refresh %q: %v", i, name, err)
  285. }
  286. } else {
  287. unlocked = true
  288. nUnlock++
  289. if err := m.Unlock(now, token); err != nil {
  290. t.Fatalf("iteration #%d: Unlock %q: %v", i, name, err)
  291. }
  292. }
  293. } else {
  294. nCreate++
  295. var err error
  296. token, err = m.Create(now, LockDetails{
  297. Root: name,
  298. Duration: duration,
  299. ZeroDepth: lockTestZeroDepth(name),
  300. })
  301. if err != nil {
  302. t.Fatalf("iteration #%d: Create %q: %v", i, name, err)
  303. }
  304. }
  305. if duration == 0 || unlocked {
  306. // A zero-duration lock should expire immediately and is
  307. // effectively equivalent to being unlocked.
  308. tokens[name] = ""
  309. } else {
  310. tokens[name] = token
  311. }
  312. if err := m.consistent(); err != nil {
  313. t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
  314. }
  315. }
  316. if nCreate < N/10 {
  317. t.Fatalf("too few Create calls: got %d, want >= %d", nCreate, N/10)
  318. }
  319. if nRefresh < N/10 {
  320. t.Fatalf("too few Refresh calls: got %d, want >= %d", nRefresh, N/10)
  321. }
  322. if nUnlock < N/10 {
  323. t.Fatalf("too few Unlock calls: got %d, want >= %d", nUnlock, N/10)
  324. }
  325. }
  326. func (m *memLS) consistent() error {
  327. m.mu.Lock()
  328. defer m.mu.Unlock()
  329. // If m.byName is non-empty, then it must contain an entry for the root "/",
  330. // and its refCount should equal the number of locked nodes.
  331. if len(m.byName) > 0 {
  332. n := m.byName["/"]
  333. if n == nil {
  334. return fmt.Errorf(`non-empty m.byName does not contain the root "/"`)
  335. }
  336. if n.refCount != len(m.byToken) {
  337. return fmt.Errorf("root node refCount=%d, differs from len(m.byToken)=%d", n.refCount, len(m.byToken))
  338. }
  339. }
  340. for name, n := range m.byName {
  341. // The map keys should be consistent with the node's copy of the key.
  342. if n.details.Root != name {
  343. return fmt.Errorf("node name %q != byName map key %q", n.details.Root, name)
  344. }
  345. // A name must be clean, and start with a "/".
  346. if len(name) == 0 || name[0] != '/' {
  347. return fmt.Errorf(`node name %q does not start with "/"`, name)
  348. }
  349. if name != path.Clean(name) {
  350. return fmt.Errorf(`node name %q is not clean`, name)
  351. }
  352. // A node's refCount should be positive.
  353. if n.refCount <= 0 {
  354. return fmt.Errorf("non-positive refCount for node at name %q", name)
  355. }
  356. // A node's refCount should be the number of self-or-descendents that
  357. // are locked (i.e. have a non-empty token).
  358. var list []string
  359. for name0, n0 := range m.byName {
  360. // All of lockTestNames' name fragments are one byte long: '_', 'i' or 'z',
  361. // so strings.HasPrefix is equivalent to self-or-descendent name match.
  362. // We don't have to worry about "/foo/bar" being a false positive match
  363. // for "/foo/b".
  364. if strings.HasPrefix(name0, name) && n0.token != "" {
  365. list = append(list, name0)
  366. }
  367. }
  368. if n.refCount != len(list) {
  369. sort.Strings(list)
  370. return fmt.Errorf("node at name %q has refCount %d but locked self-or-descendents are %q (len=%d)",
  371. name, n.refCount, list, len(list))
  372. }
  373. // A node n is in m.byToken if it has a non-empty token.
  374. if n.token != "" {
  375. if _, ok := m.byToken[n.token]; !ok {
  376. return fmt.Errorf("node at name %q has token %q but not in m.byToken", name, n.token)
  377. }
  378. }
  379. // A node n is in m.byExpiry if it has a non-negative byExpiryIndex.
  380. if n.byExpiryIndex >= 0 {
  381. if n.byExpiryIndex >= len(m.byExpiry) {
  382. return fmt.Errorf("node at name %q has byExpiryIndex %d but m.byExpiry has length %d", name, n.byExpiryIndex, len(m.byExpiry))
  383. }
  384. if n != m.byExpiry[n.byExpiryIndex] {
  385. return fmt.Errorf("node at name %q has byExpiryIndex %d but that indexes a different node", name, n.byExpiryIndex)
  386. }
  387. }
  388. }
  389. for token, n := range m.byToken {
  390. // The map keys should be consistent with the node's copy of the key.
  391. if n.token != token {
  392. return fmt.Errorf("node token %q != byToken map key %q", n.token, token)
  393. }
  394. // Every node in m.byToken is in m.byName.
  395. if _, ok := m.byName[n.details.Root]; !ok {
  396. return fmt.Errorf("node at name %q in m.byToken but not in m.byName", n.details.Root)
  397. }
  398. }
  399. for i, n := range m.byExpiry {
  400. // The slice indices should be consistent with the node's copy of the index.
  401. if n.byExpiryIndex != i {
  402. return fmt.Errorf("node byExpiryIndex %d != byExpiry slice index %d", n.byExpiryIndex, i)
  403. }
  404. // Every node in m.byExpiry is in m.byName.
  405. if _, ok := m.byName[n.details.Root]; !ok {
  406. return fmt.Errorf("node at name %q in m.byExpiry but not in m.byName", n.details.Root)
  407. }
  408. }
  409. return nil
  410. }
  411. func TestParseTimeout(t *testing.T) {
  412. testCases := []struct {
  413. s string
  414. want time.Duration
  415. wantErr error
  416. }{{
  417. "",
  418. infiniteTimeout,
  419. nil,
  420. }, {
  421. "Infinite",
  422. infiniteTimeout,
  423. nil,
  424. }, {
  425. "Infinitesimal",
  426. 0,
  427. errInvalidTimeout,
  428. }, {
  429. "infinite",
  430. 0,
  431. errInvalidTimeout,
  432. }, {
  433. "Second-0",
  434. 0 * time.Second,
  435. nil,
  436. }, {
  437. "Second-123",
  438. 123 * time.Second,
  439. nil,
  440. }, {
  441. " Second-456 ",
  442. 456 * time.Second,
  443. nil,
  444. }, {
  445. "Second-4100000000",
  446. 4100000000 * time.Second,
  447. nil,
  448. }, {
  449. "junk",
  450. 0,
  451. errInvalidTimeout,
  452. }, {
  453. "Second-",
  454. 0,
  455. errInvalidTimeout,
  456. }, {
  457. "Second--1",
  458. 0,
  459. errInvalidTimeout,
  460. }, {
  461. "Second--123",
  462. 0,
  463. errInvalidTimeout,
  464. }, {
  465. "Second-+123",
  466. 0,
  467. errInvalidTimeout,
  468. }, {
  469. "Second-0x123",
  470. 0,
  471. errInvalidTimeout,
  472. }, {
  473. "second-123",
  474. 0,
  475. errInvalidTimeout,
  476. }, {
  477. "Second-4294967295",
  478. 4294967295 * time.Second,
  479. nil,
  480. }, {
  481. // Section 10.7 says that "The timeout value for TimeType "Second"
  482. // must not be greater than 2^32-1."
  483. "Second-4294967296",
  484. 0,
  485. errInvalidTimeout,
  486. }, {
  487. // This test case comes from section 9.10.9 of the spec. It says,
  488. //
  489. // "In this request, the client has specified that it desires an
  490. // infinite-length lock, if available, otherwise a timeout of 4.1
  491. // billion seconds, if available."
  492. //
  493. // The Go WebDAV package always supports infinite length locks,
  494. // and ignores the fallback after the comma.
  495. "Infinite, Second-4100000000",
  496. infiniteTimeout,
  497. nil,
  498. }}
  499. for _, tc := range testCases {
  500. got, gotErr := parseTimeout(tc.s)
  501. if got != tc.want || gotErr != tc.wantErr {
  502. t.Errorf("parsing %q:\ngot %v, %v\nwant %v, %v", tc.s, got, gotErr, tc.want, tc.wantErr)
  503. }
  504. }
  505. }