lock_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  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. infiniteTimeout, // infiniteTimeout 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 an 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: infiniteTimeout,
  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=%t: 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 TestMemLSLookup(t *testing.T) {
  147. now := time.Unix(0, 0)
  148. m := NewMemLS().(*memLS)
  149. badToken := m.nextToken()
  150. t.Logf("badToken=%q", badToken)
  151. for _, name := range lockTestNames {
  152. token, err := m.Create(now, LockDetails{
  153. Root: name,
  154. Duration: infiniteTimeout,
  155. ZeroDepth: lockTestZeroDepth(name),
  156. })
  157. if err != nil {
  158. t.Fatalf("creating lock for %q: %v", name, err)
  159. }
  160. t.Logf("%-15q -> node=%p token=%q", name, m.byName[name], token)
  161. }
  162. baseNames := append([]string{"/a", "/b/c"}, lockTestNames...)
  163. for _, baseName := range baseNames {
  164. for _, suffix := range []string{"", "/0", "/1/2/3"} {
  165. name := baseName + suffix
  166. goodToken := ""
  167. base := m.byName[baseName]
  168. if base != nil && (suffix == "" || !lockTestZeroDepth(baseName)) {
  169. goodToken = base.token
  170. }
  171. for _, token := range []string{badToken, goodToken} {
  172. if token == "" {
  173. continue
  174. }
  175. got := m.lookup(name, Condition{Token: token})
  176. want := base
  177. if token == badToken {
  178. want = nil
  179. }
  180. if got != want {
  181. t.Errorf("name=%-20qtoken=%q (bad=%t): got %p, want %p",
  182. name, token, token == badToken, got, want)
  183. }
  184. }
  185. }
  186. }
  187. }
  188. func TestMemLSConfirm(t *testing.T) {
  189. now := time.Unix(0, 0)
  190. m := NewMemLS().(*memLS)
  191. alice, err := m.Create(now, LockDetails{
  192. Root: "/alice",
  193. Duration: infiniteTimeout,
  194. ZeroDepth: false,
  195. })
  196. if err != nil {
  197. t.Fatalf("Create: %v", err)
  198. }
  199. tweedle, err := m.Create(now, LockDetails{
  200. Root: "/tweedle",
  201. Duration: infiniteTimeout,
  202. ZeroDepth: false,
  203. })
  204. if err != nil {
  205. t.Fatalf("Create: %v", err)
  206. }
  207. if err := m.consistent(); err != nil {
  208. t.Fatalf("Create: inconsistent state: %v", err)
  209. }
  210. // Test a mismatch between name and condition.
  211. _, err = m.Confirm(now, "/tweedle/dee", "", Condition{Token: alice})
  212. if err != ErrConfirmationFailed {
  213. t.Fatalf("Confirm (mismatch): got %v, want ErrConfirmationFailed", err)
  214. }
  215. if err := m.consistent(); err != nil {
  216. t.Fatalf("Confirm (mismatch): inconsistent state: %v", err)
  217. }
  218. // Test two names (that fall under the same lock) in the one Confirm call.
  219. release, err := m.Confirm(now, "/tweedle/dee", "/tweedle/dum", Condition{Token: tweedle})
  220. if err != nil {
  221. t.Fatalf("Confirm (twins): %v", err)
  222. }
  223. if err := m.consistent(); err != nil {
  224. t.Fatalf("Confirm (twins): inconsistent state: %v", err)
  225. }
  226. release()
  227. if err := m.consistent(); err != nil {
  228. t.Fatalf("release (twins): inconsistent state: %v", err)
  229. }
  230. // Test the same two names in overlapping Confirm / release calls.
  231. releaseDee, err := m.Confirm(now, "/tweedle/dee", "", Condition{Token: tweedle})
  232. if err != nil {
  233. t.Fatalf("Confirm (sequence #0): %v", err)
  234. }
  235. if err := m.consistent(); err != nil {
  236. t.Fatalf("Confirm (sequence #0): inconsistent state: %v", err)
  237. }
  238. _, err = m.Confirm(now, "/tweedle/dum", "", Condition{Token: tweedle})
  239. if err != ErrConfirmationFailed {
  240. t.Fatalf("Confirm (sequence #1): got %v, want ErrConfirmationFailed", err)
  241. }
  242. if err := m.consistent(); err != nil {
  243. t.Fatalf("Confirm (sequence #1): inconsistent state: %v", err)
  244. }
  245. releaseDee()
  246. if err := m.consistent(); err != nil {
  247. t.Fatalf("release (sequence #2): inconsistent state: %v", err)
  248. }
  249. releaseDum, err := m.Confirm(now, "/tweedle/dum", "", Condition{Token: tweedle})
  250. if err != nil {
  251. t.Fatalf("Confirm (sequence #3): %v", err)
  252. }
  253. if err := m.consistent(); err != nil {
  254. t.Fatalf("Confirm (sequence #3): inconsistent state: %v", err)
  255. }
  256. // Test that you can't unlock a held lock.
  257. err = m.Unlock(now, tweedle)
  258. if err != ErrLocked {
  259. t.Fatalf("Unlock (sequence #4): got %v, want ErrLocked", err)
  260. }
  261. releaseDum()
  262. if err := m.consistent(); err != nil {
  263. t.Fatalf("release (sequence #5): inconsistent state: %v", err)
  264. }
  265. err = m.Unlock(now, tweedle)
  266. if err != nil {
  267. t.Fatalf("Unlock (sequence #6): %v", err)
  268. }
  269. if err := m.consistent(); err != nil {
  270. t.Fatalf("Unlock (sequence #6): inconsistent state: %v", err)
  271. }
  272. }
  273. func TestMemLSNonCanonicalRoot(t *testing.T) {
  274. now := time.Unix(0, 0)
  275. m := NewMemLS().(*memLS)
  276. token, err := m.Create(now, LockDetails{
  277. Root: "/foo/./bar//",
  278. Duration: 1 * time.Second,
  279. })
  280. if err != nil {
  281. t.Fatalf("Create: %v", err)
  282. }
  283. if err := m.consistent(); err != nil {
  284. t.Fatalf("Create: inconsistent state: %v", err)
  285. }
  286. if err := m.Unlock(now, token); err != nil {
  287. t.Fatalf("Unlock: %v", err)
  288. }
  289. if err := m.consistent(); err != nil {
  290. t.Fatalf("Unlock: inconsistent state: %v", err)
  291. }
  292. }
  293. func TestMemLSExpiry(t *testing.T) {
  294. m := NewMemLS().(*memLS)
  295. testCases := []string{
  296. "setNow 0",
  297. "create /a.5",
  298. "want /a.5",
  299. "create /c.6",
  300. "want /a.5 /c.6",
  301. "create /a/b.7",
  302. "want /a.5 /a/b.7 /c.6",
  303. "setNow 4",
  304. "want /a.5 /a/b.7 /c.6",
  305. "setNow 5",
  306. "want /a/b.7 /c.6",
  307. "setNow 6",
  308. "want /a/b.7",
  309. "setNow 7",
  310. "want ",
  311. "setNow 8",
  312. "want ",
  313. "create /a.12",
  314. "create /b.13",
  315. "create /c.15",
  316. "create /a/d.16",
  317. "want /a.12 /a/d.16 /b.13 /c.15",
  318. "refresh /a.14",
  319. "want /a.14 /a/d.16 /b.13 /c.15",
  320. "setNow 12",
  321. "want /a.14 /a/d.16 /b.13 /c.15",
  322. "setNow 13",
  323. "want /a.14 /a/d.16 /c.15",
  324. "setNow 14",
  325. "want /a/d.16 /c.15",
  326. "refresh /a/d.20",
  327. "refresh /c.20",
  328. "want /a/d.20 /c.20",
  329. "setNow 20",
  330. "want ",
  331. }
  332. tokens := map[string]string{}
  333. zTime := time.Unix(0, 0)
  334. now := zTime
  335. for i, tc := range testCases {
  336. j := strings.IndexByte(tc, ' ')
  337. if j < 0 {
  338. t.Fatalf("test case #%d %q: invalid command", i, tc)
  339. }
  340. op, arg := tc[:j], tc[j+1:]
  341. switch op {
  342. default:
  343. t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
  344. case "create", "refresh":
  345. parts := strings.Split(arg, ".")
  346. if len(parts) != 2 {
  347. t.Fatalf("test case #%d %q: invalid create", i, tc)
  348. }
  349. root := parts[0]
  350. d, err := strconv.Atoi(parts[1])
  351. if err != nil {
  352. t.Fatalf("test case #%d %q: invalid duration", i, tc)
  353. }
  354. dur := time.Unix(0, 0).Add(time.Duration(d) * time.Second).Sub(now)
  355. switch op {
  356. case "create":
  357. token, err := m.Create(now, LockDetails{
  358. Root: root,
  359. Duration: dur,
  360. ZeroDepth: true,
  361. })
  362. if err != nil {
  363. t.Fatalf("test case #%d %q: Create: %v", i, tc, err)
  364. }
  365. tokens[root] = token
  366. case "refresh":
  367. token := tokens[root]
  368. if token == "" {
  369. t.Fatalf("test case #%d %q: no token for %q", i, tc, root)
  370. }
  371. got, err := m.Refresh(now, token, dur)
  372. if err != nil {
  373. t.Fatalf("test case #%d %q: Refresh: %v", i, tc, err)
  374. }
  375. want := LockDetails{
  376. Root: root,
  377. Duration: dur,
  378. ZeroDepth: true,
  379. }
  380. if got != want {
  381. t.Fatalf("test case #%d %q:\ngot %v\nwant %v", i, tc, got, want)
  382. }
  383. }
  384. case "setNow":
  385. d, err := strconv.Atoi(arg)
  386. if err != nil {
  387. t.Fatalf("test case #%d %q: invalid duration", i, tc)
  388. }
  389. now = time.Unix(0, 0).Add(time.Duration(d) * time.Second)
  390. case "want":
  391. m.mu.Lock()
  392. m.collectExpiredNodes(now)
  393. got := make([]string, 0, len(m.byToken))
  394. for _, n := range m.byToken {
  395. got = append(got, fmt.Sprintf("%s.%d",
  396. n.details.Root, n.expiry.Sub(zTime)/time.Second))
  397. }
  398. m.mu.Unlock()
  399. sort.Strings(got)
  400. want := []string{}
  401. if arg != "" {
  402. want = strings.Split(arg, " ")
  403. }
  404. if !reflect.DeepEqual(got, want) {
  405. t.Fatalf("test case #%d %q:\ngot %q\nwant %q", i, tc, got, want)
  406. }
  407. }
  408. if err := m.consistent(); err != nil {
  409. t.Fatalf("test case #%d %q: inconsistent state: %v", i, tc, err)
  410. }
  411. }
  412. }
  413. func TestMemLS(t *testing.T) {
  414. now := time.Unix(0, 0)
  415. m := NewMemLS().(*memLS)
  416. rng := rand.New(rand.NewSource(0))
  417. tokens := map[string]string{}
  418. nConfirm, nCreate, nRefresh, nUnlock := 0, 0, 0, 0
  419. const N = 2000
  420. for i := 0; i < N; i++ {
  421. name := lockTestNames[rng.Intn(len(lockTestNames))]
  422. duration := lockTestDurations[rng.Intn(len(lockTestDurations))]
  423. confirmed, unlocked := false, false
  424. // If the name was already locked, we randomly confirm/release, refresh
  425. // or unlock it. Otherwise, we create a lock.
  426. token := tokens[name]
  427. if token != "" {
  428. switch rng.Intn(3) {
  429. case 0:
  430. confirmed = true
  431. nConfirm++
  432. release, err := m.Confirm(now, name, "", Condition{Token: token})
  433. if err != nil {
  434. t.Fatalf("iteration #%d: Confirm %q: %v", i, name, err)
  435. }
  436. if err := m.consistent(); err != nil {
  437. t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
  438. }
  439. release()
  440. case 1:
  441. nRefresh++
  442. if _, err := m.Refresh(now, token, duration); err != nil {
  443. t.Fatalf("iteration #%d: Refresh %q: %v", i, name, err)
  444. }
  445. case 2:
  446. unlocked = true
  447. nUnlock++
  448. if err := m.Unlock(now, token); err != nil {
  449. t.Fatalf("iteration #%d: Unlock %q: %v", i, name, err)
  450. }
  451. }
  452. } else {
  453. nCreate++
  454. var err error
  455. token, err = m.Create(now, LockDetails{
  456. Root: name,
  457. Duration: duration,
  458. ZeroDepth: lockTestZeroDepth(name),
  459. })
  460. if err != nil {
  461. t.Fatalf("iteration #%d: Create %q: %v", i, name, err)
  462. }
  463. }
  464. if !confirmed {
  465. if duration == 0 || unlocked {
  466. // A zero-duration lock should expire immediately and is
  467. // effectively equivalent to being unlocked.
  468. tokens[name] = ""
  469. } else {
  470. tokens[name] = token
  471. }
  472. }
  473. if err := m.consistent(); err != nil {
  474. t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
  475. }
  476. }
  477. if nConfirm < N/10 {
  478. t.Fatalf("too few Confirm calls: got %d, want >= %d", nConfirm, N/10)
  479. }
  480. if nCreate < N/10 {
  481. t.Fatalf("too few Create calls: got %d, want >= %d", nCreate, N/10)
  482. }
  483. if nRefresh < N/10 {
  484. t.Fatalf("too few Refresh calls: got %d, want >= %d", nRefresh, N/10)
  485. }
  486. if nUnlock < N/10 {
  487. t.Fatalf("too few Unlock calls: got %d, want >= %d", nUnlock, N/10)
  488. }
  489. }
  490. func (m *memLS) consistent() error {
  491. m.mu.Lock()
  492. defer m.mu.Unlock()
  493. // If m.byName is non-empty, then it must contain an entry for the root "/",
  494. // and its refCount should equal the number of locked nodes.
  495. if len(m.byName) > 0 {
  496. n := m.byName["/"]
  497. if n == nil {
  498. return fmt.Errorf(`non-empty m.byName does not contain the root "/"`)
  499. }
  500. if n.refCount != len(m.byToken) {
  501. return fmt.Errorf("root node refCount=%d, differs from len(m.byToken)=%d", n.refCount, len(m.byToken))
  502. }
  503. }
  504. for name, n := range m.byName {
  505. // The map keys should be consistent with the node's copy of the key.
  506. if n.details.Root != name {
  507. return fmt.Errorf("node name %q != byName map key %q", n.details.Root, name)
  508. }
  509. // A name must be clean, and start with a "/".
  510. if len(name) == 0 || name[0] != '/' {
  511. return fmt.Errorf(`node name %q does not start with "/"`, name)
  512. }
  513. if name != path.Clean(name) {
  514. return fmt.Errorf(`node name %q is not clean`, name)
  515. }
  516. // A node's refCount should be positive.
  517. if n.refCount <= 0 {
  518. return fmt.Errorf("non-positive refCount for node at name %q", name)
  519. }
  520. // A node's refCount should be the number of self-or-descendents that
  521. // are locked (i.e. have a non-empty token).
  522. var list []string
  523. for name0, n0 := range m.byName {
  524. // All of lockTestNames' name fragments are one byte long: '_', 'i' or 'z',
  525. // so strings.HasPrefix is equivalent to self-or-descendent name match.
  526. // We don't have to worry about "/foo/bar" being a false positive match
  527. // for "/foo/b".
  528. if strings.HasPrefix(name0, name) && n0.token != "" {
  529. list = append(list, name0)
  530. }
  531. }
  532. if n.refCount != len(list) {
  533. sort.Strings(list)
  534. return fmt.Errorf("node at name %q has refCount %d but locked self-or-descendents are %q (len=%d)",
  535. name, n.refCount, list, len(list))
  536. }
  537. // A node n is in m.byToken if it has a non-empty token.
  538. if n.token != "" {
  539. if _, ok := m.byToken[n.token]; !ok {
  540. return fmt.Errorf("node at name %q has token %q but not in m.byToken", name, n.token)
  541. }
  542. }
  543. // A node n is in m.byExpiry if it has a non-negative byExpiryIndex.
  544. if n.byExpiryIndex >= 0 {
  545. if n.byExpiryIndex >= len(m.byExpiry) {
  546. return fmt.Errorf("node at name %q has byExpiryIndex %d but m.byExpiry has length %d", name, n.byExpiryIndex, len(m.byExpiry))
  547. }
  548. if n != m.byExpiry[n.byExpiryIndex] {
  549. return fmt.Errorf("node at name %q has byExpiryIndex %d but that indexes a different node", name, n.byExpiryIndex)
  550. }
  551. }
  552. }
  553. for token, n := range m.byToken {
  554. // The map keys should be consistent with the node's copy of the key.
  555. if n.token != token {
  556. return fmt.Errorf("node token %q != byToken map key %q", n.token, token)
  557. }
  558. // Every node in m.byToken is in m.byName.
  559. if _, ok := m.byName[n.details.Root]; !ok {
  560. return fmt.Errorf("node at name %q in m.byToken but not in m.byName", n.details.Root)
  561. }
  562. }
  563. for i, n := range m.byExpiry {
  564. // The slice indices should be consistent with the node's copy of the index.
  565. if n.byExpiryIndex != i {
  566. return fmt.Errorf("node byExpiryIndex %d != byExpiry slice index %d", n.byExpiryIndex, i)
  567. }
  568. // Every node in m.byExpiry is in m.byName.
  569. if _, ok := m.byName[n.details.Root]; !ok {
  570. return fmt.Errorf("node at name %q in m.byExpiry but not in m.byName", n.details.Root)
  571. }
  572. // No node in m.byExpiry should be held.
  573. if n.held {
  574. return fmt.Errorf("node at name %q in m.byExpiry is held", n.details.Root)
  575. }
  576. }
  577. return nil
  578. }
  579. func TestParseTimeout(t *testing.T) {
  580. testCases := []struct {
  581. s string
  582. want time.Duration
  583. wantErr error
  584. }{{
  585. "",
  586. infiniteTimeout,
  587. nil,
  588. }, {
  589. "Infinite",
  590. infiniteTimeout,
  591. nil,
  592. }, {
  593. "Infinitesimal",
  594. 0,
  595. errInvalidTimeout,
  596. }, {
  597. "infinite",
  598. 0,
  599. errInvalidTimeout,
  600. }, {
  601. "Second-0",
  602. 0 * time.Second,
  603. nil,
  604. }, {
  605. "Second-123",
  606. 123 * time.Second,
  607. nil,
  608. }, {
  609. " Second-456 ",
  610. 456 * time.Second,
  611. nil,
  612. }, {
  613. "Second-4100000000",
  614. 4100000000 * time.Second,
  615. nil,
  616. }, {
  617. "junk",
  618. 0,
  619. errInvalidTimeout,
  620. }, {
  621. "Second-",
  622. 0,
  623. errInvalidTimeout,
  624. }, {
  625. "Second--1",
  626. 0,
  627. errInvalidTimeout,
  628. }, {
  629. "Second--123",
  630. 0,
  631. errInvalidTimeout,
  632. }, {
  633. "Second-+123",
  634. 0,
  635. errInvalidTimeout,
  636. }, {
  637. "Second-0x123",
  638. 0,
  639. errInvalidTimeout,
  640. }, {
  641. "second-123",
  642. 0,
  643. errInvalidTimeout,
  644. }, {
  645. "Second-4294967295",
  646. 4294967295 * time.Second,
  647. nil,
  648. }, {
  649. // Section 10.7 says that "The timeout value for TimeType "Second"
  650. // must not be greater than 2^32-1."
  651. "Second-4294967296",
  652. 0,
  653. errInvalidTimeout,
  654. }, {
  655. // This test case comes from section 9.10.9 of the spec. It says,
  656. //
  657. // "In this request, the client has specified that it desires an
  658. // infinite-length lock, if available, otherwise a timeout of 4.1
  659. // billion seconds, if available."
  660. //
  661. // The Go WebDAV package always supports infinite length locks,
  662. // and ignores the fallback after the comma.
  663. "Infinite, Second-4100000000",
  664. infiniteTimeout,
  665. nil,
  666. }}
  667. for _, tc := range testCases {
  668. got, gotErr := parseTimeout(tc.s)
  669. if got != tc.want || gotErr != tc.wantErr {
  670. t.Errorf("parsing %q:\ngot %v, %v\nwant %v, %v", tc.s, got, gotErr, tc.want, tc.wantErr)
  671. }
  672. }
  673. }