lock_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  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 TestMemLSNonCanonicalRoot(t *testing.T) {
  147. now := time.Unix(0, 0)
  148. m := NewMemLS().(*memLS)
  149. token, err := m.Create(now, LockDetails{
  150. Root: "/foo/./bar//",
  151. Duration: 1 * time.Second,
  152. })
  153. if err != nil {
  154. t.Fatalf("Create: %v", err)
  155. }
  156. if err := m.consistent(); err != nil {
  157. t.Fatalf("Create: inconsistent state: %v", err)
  158. }
  159. if err := m.Unlock(now, token); err != nil {
  160. t.Fatalf("Unlock: %v", err)
  161. }
  162. if err := m.consistent(); err != nil {
  163. t.Fatalf("Unlock: inconsistent state: %v", err)
  164. }
  165. }
  166. func TestMemLSExpiry(t *testing.T) {
  167. m := NewMemLS().(*memLS)
  168. testCases := []string{
  169. "setNow 0",
  170. "create /a.5",
  171. "want /a.5",
  172. "create /c.6",
  173. "want /a.5 /c.6",
  174. "create /a/b.7",
  175. "want /a.5 /a/b.7 /c.6",
  176. "setNow 4",
  177. "want /a.5 /a/b.7 /c.6",
  178. "setNow 5",
  179. "want /a/b.7 /c.6",
  180. "setNow 6",
  181. "want /a/b.7",
  182. "setNow 7",
  183. "want ",
  184. "setNow 8",
  185. "want ",
  186. "create /a.12",
  187. "create /b.13",
  188. "create /c.15",
  189. "create /a/d.16",
  190. "want /a.12 /a/d.16 /b.13 /c.15",
  191. "refresh /a.14",
  192. "want /a.14 /a/d.16 /b.13 /c.15",
  193. "setNow 12",
  194. "want /a.14 /a/d.16 /b.13 /c.15",
  195. "setNow 13",
  196. "want /a.14 /a/d.16 /c.15",
  197. "setNow 14",
  198. "want /a/d.16 /c.15",
  199. "refresh /a/d.20",
  200. "refresh /c.20",
  201. "want /a/d.20 /c.20",
  202. "setNow 20",
  203. "want ",
  204. }
  205. tokens := map[string]string{}
  206. zTime := time.Unix(0, 0)
  207. now := zTime
  208. for i, tc := range testCases {
  209. j := strings.IndexByte(tc, ' ')
  210. if j < 0 {
  211. t.Fatalf("test case #%d %q: invalid command", i, tc)
  212. }
  213. op, arg := tc[:j], tc[j+1:]
  214. switch op {
  215. default:
  216. t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
  217. case "create", "refresh":
  218. parts := strings.Split(arg, ".")
  219. if len(parts) != 2 {
  220. t.Fatalf("test case #%d %q: invalid create", i, tc)
  221. }
  222. root := parts[0]
  223. d, err := strconv.Atoi(parts[1])
  224. if err != nil {
  225. t.Fatalf("test case #%d %q: invalid duration", i, tc)
  226. }
  227. dur := time.Unix(0, 0).Add(time.Duration(d) * time.Second).Sub(now)
  228. switch op {
  229. case "create":
  230. token, err := m.Create(now, LockDetails{
  231. Root: root,
  232. Duration: dur,
  233. ZeroDepth: true,
  234. })
  235. if err != nil {
  236. t.Fatalf("test case #%d %q: Create: %v", i, tc, err)
  237. }
  238. tokens[root] = token
  239. case "refresh":
  240. token := tokens[root]
  241. if token == "" {
  242. t.Fatalf("test case #%d %q: no token for %q", i, tc, root)
  243. }
  244. got, err := m.Refresh(now, token, dur)
  245. if err != nil {
  246. t.Fatalf("test case #%d %q: Refresh: %v", i, tc, err)
  247. }
  248. want := LockDetails{
  249. Root: root,
  250. Duration: dur,
  251. ZeroDepth: true,
  252. }
  253. if got != want {
  254. t.Fatalf("test case #%d %q:\ngot %v\nwant %v", i, tc, got, want)
  255. }
  256. }
  257. case "setNow":
  258. d, err := strconv.Atoi(arg)
  259. if err != nil {
  260. t.Fatalf("test case #%d %q: invalid duration", i, tc)
  261. }
  262. now = time.Unix(0, 0).Add(time.Duration(d) * time.Second)
  263. case "want":
  264. m.mu.Lock()
  265. m.collectExpiredNodes(now)
  266. got := make([]string, 0, len(m.byToken))
  267. for _, n := range m.byToken {
  268. got = append(got, fmt.Sprintf("%s.%d",
  269. n.details.Root, n.expiry.Sub(zTime)/time.Second))
  270. }
  271. m.mu.Unlock()
  272. sort.Strings(got)
  273. want := []string{}
  274. if arg != "" {
  275. want = strings.Split(arg, " ")
  276. }
  277. if !reflect.DeepEqual(got, want) {
  278. t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, want)
  279. }
  280. }
  281. if err := m.consistent(); err != nil {
  282. t.Fatalf("test case #%d %q: inconsistent state: %v", i, tc, err)
  283. }
  284. }
  285. }
  286. func TestMemLSCreateRefreshUnlock(t *testing.T) {
  287. now := time.Unix(0, 0)
  288. m := NewMemLS().(*memLS)
  289. rng := rand.New(rand.NewSource(0))
  290. tokens := map[string]string{}
  291. nCreate, nRefresh, nUnlock := 0, 0, 0
  292. const N = 2000
  293. for i := 0; i < N; i++ {
  294. name := lockTestNames[rng.Intn(len(lockTestNames))]
  295. duration := lockTestDurations[rng.Intn(len(lockTestDurations))]
  296. unlocked := false
  297. // If the name was already locked, there's a 50-50 chance that
  298. // we refresh or unlock it. Otherwise, we create a lock.
  299. token := tokens[name]
  300. if token != "" {
  301. if rng.Intn(2) == 0 {
  302. nRefresh++
  303. if _, err := m.Refresh(now, token, duration); err != nil {
  304. t.Fatalf("iteration #%d: Refresh %q: %v", i, name, err)
  305. }
  306. } else {
  307. unlocked = true
  308. nUnlock++
  309. if err := m.Unlock(now, token); err != nil {
  310. t.Fatalf("iteration #%d: Unlock %q: %v", i, name, err)
  311. }
  312. }
  313. } else {
  314. nCreate++
  315. var err error
  316. token, err = m.Create(now, LockDetails{
  317. Root: name,
  318. Duration: duration,
  319. ZeroDepth: lockTestZeroDepth(name),
  320. })
  321. if err != nil {
  322. t.Fatalf("iteration #%d: Create %q: %v", i, name, err)
  323. }
  324. }
  325. if duration == 0 || unlocked {
  326. // A zero-duration lock should expire immediately and is
  327. // effectively equivalent to being unlocked.
  328. tokens[name] = ""
  329. } else {
  330. tokens[name] = token
  331. }
  332. if err := m.consistent(); err != nil {
  333. t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
  334. }
  335. }
  336. if nCreate < N/10 {
  337. t.Fatalf("too few Create calls: got %d, want >= %d", nCreate, N/10)
  338. }
  339. if nRefresh < N/10 {
  340. t.Fatalf("too few Refresh calls: got %d, want >= %d", nRefresh, N/10)
  341. }
  342. if nUnlock < N/10 {
  343. t.Fatalf("too few Unlock calls: got %d, want >= %d", nUnlock, N/10)
  344. }
  345. }
  346. func (m *memLS) consistent() error {
  347. m.mu.Lock()
  348. defer m.mu.Unlock()
  349. // If m.byName is non-empty, then it must contain an entry for the root "/",
  350. // and its refCount should equal the number of locked nodes.
  351. if len(m.byName) > 0 {
  352. n := m.byName["/"]
  353. if n == nil {
  354. return fmt.Errorf(`non-empty m.byName does not contain the root "/"`)
  355. }
  356. if n.refCount != len(m.byToken) {
  357. return fmt.Errorf("root node refCount=%d, differs from len(m.byToken)=%d", n.refCount, len(m.byToken))
  358. }
  359. }
  360. for name, n := range m.byName {
  361. // The map keys should be consistent with the node's copy of the key.
  362. if n.details.Root != name {
  363. return fmt.Errorf("node name %q != byName map key %q", n.details.Root, name)
  364. }
  365. // A name must be clean, and start with a "/".
  366. if len(name) == 0 || name[0] != '/' {
  367. return fmt.Errorf(`node name %q does not start with "/"`, name)
  368. }
  369. if name != path.Clean(name) {
  370. return fmt.Errorf(`node name %q is not clean`, name)
  371. }
  372. // A node's refCount should be positive.
  373. if n.refCount <= 0 {
  374. return fmt.Errorf("non-positive refCount for node at name %q", name)
  375. }
  376. // A node's refCount should be the number of self-or-descendents that
  377. // are locked (i.e. have a non-empty token).
  378. var list []string
  379. for name0, n0 := range m.byName {
  380. // All of lockTestNames' name fragments are one byte long: '_', 'i' or 'z',
  381. // so strings.HasPrefix is equivalent to self-or-descendent name match.
  382. // We don't have to worry about "/foo/bar" being a false positive match
  383. // for "/foo/b".
  384. if strings.HasPrefix(name0, name) && n0.token != "" {
  385. list = append(list, name0)
  386. }
  387. }
  388. if n.refCount != len(list) {
  389. sort.Strings(list)
  390. return fmt.Errorf("node at name %q has refCount %d but locked self-or-descendents are %q (len=%d)",
  391. name, n.refCount, list, len(list))
  392. }
  393. // A node n is in m.byToken if it has a non-empty token.
  394. if n.token != "" {
  395. if _, ok := m.byToken[n.token]; !ok {
  396. return fmt.Errorf("node at name %q has token %q but not in m.byToken", name, n.token)
  397. }
  398. }
  399. // A node n is in m.byExpiry if it has a non-negative byExpiryIndex.
  400. if n.byExpiryIndex >= 0 {
  401. if n.byExpiryIndex >= len(m.byExpiry) {
  402. return fmt.Errorf("node at name %q has byExpiryIndex %d but m.byExpiry has length %d", name, n.byExpiryIndex, len(m.byExpiry))
  403. }
  404. if n != m.byExpiry[n.byExpiryIndex] {
  405. return fmt.Errorf("node at name %q has byExpiryIndex %d but that indexes a different node", name, n.byExpiryIndex)
  406. }
  407. }
  408. }
  409. for token, n := range m.byToken {
  410. // The map keys should be consistent with the node's copy of the key.
  411. if n.token != token {
  412. return fmt.Errorf("node token %q != byToken map key %q", n.token, token)
  413. }
  414. // Every node in m.byToken is in m.byName.
  415. if _, ok := m.byName[n.details.Root]; !ok {
  416. return fmt.Errorf("node at name %q in m.byToken but not in m.byName", n.details.Root)
  417. }
  418. }
  419. for i, n := range m.byExpiry {
  420. // The slice indices should be consistent with the node's copy of the index.
  421. if n.byExpiryIndex != i {
  422. return fmt.Errorf("node byExpiryIndex %d != byExpiry slice index %d", n.byExpiryIndex, i)
  423. }
  424. // Every node in m.byExpiry is in m.byName.
  425. if _, ok := m.byName[n.details.Root]; !ok {
  426. return fmt.Errorf("node at name %q in m.byExpiry but not in m.byName", n.details.Root)
  427. }
  428. }
  429. return nil
  430. }
  431. func TestParseTimeout(t *testing.T) {
  432. testCases := []struct {
  433. s string
  434. want time.Duration
  435. wantErr error
  436. }{{
  437. "",
  438. infiniteTimeout,
  439. nil,
  440. }, {
  441. "Infinite",
  442. infiniteTimeout,
  443. nil,
  444. }, {
  445. "Infinitesimal",
  446. 0,
  447. errInvalidTimeout,
  448. }, {
  449. "infinite",
  450. 0,
  451. errInvalidTimeout,
  452. }, {
  453. "Second-0",
  454. 0 * time.Second,
  455. nil,
  456. }, {
  457. "Second-123",
  458. 123 * time.Second,
  459. nil,
  460. }, {
  461. " Second-456 ",
  462. 456 * time.Second,
  463. nil,
  464. }, {
  465. "Second-4100000000",
  466. 4100000000 * time.Second,
  467. nil,
  468. }, {
  469. "junk",
  470. 0,
  471. errInvalidTimeout,
  472. }, {
  473. "Second-",
  474. 0,
  475. errInvalidTimeout,
  476. }, {
  477. "Second--1",
  478. 0,
  479. errInvalidTimeout,
  480. }, {
  481. "Second--123",
  482. 0,
  483. errInvalidTimeout,
  484. }, {
  485. "Second-+123",
  486. 0,
  487. errInvalidTimeout,
  488. }, {
  489. "Second-0x123",
  490. 0,
  491. errInvalidTimeout,
  492. }, {
  493. "second-123",
  494. 0,
  495. errInvalidTimeout,
  496. }, {
  497. "Second-4294967295",
  498. 4294967295 * time.Second,
  499. nil,
  500. }, {
  501. // Section 10.7 says that "The timeout value for TimeType "Second"
  502. // must not be greater than 2^32-1."
  503. "Second-4294967296",
  504. 0,
  505. errInvalidTimeout,
  506. }, {
  507. // This test case comes from section 9.10.9 of the spec. It says,
  508. //
  509. // "In this request, the client has specified that it desires an
  510. // infinite-length lock, if available, otherwise a timeout of 4.1
  511. // billion seconds, if available."
  512. //
  513. // The Go WebDAV package always supports infinite length locks,
  514. // and ignores the fallback after the comma.
  515. "Infinite, Second-4100000000",
  516. infiniteTimeout,
  517. nil,
  518. }}
  519. for _, tc := range testCases {
  520. got, gotErr := parseTimeout(tc.s)
  521. if got != tc.want || gotErr != tc.wantErr {
  522. t.Errorf("parsing %q:\ngot %v, %v\nwant %v, %v", tc.s, got, gotErr, tc.want, tc.wantErr)
  523. }
  524. }
  525. }