OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / webdav / lock_test.go
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
5 package webdav
6
7 import (
8         "fmt"
9         "math/rand"
10         "path"
11         "reflect"
12         "sort"
13         "strconv"
14         "strings"
15         "testing"
16         "time"
17 )
18
19 func TestWalkToRoot(t *testing.T) {
20         testCases := []struct {
21                 name string
22                 want []string
23         }{{
24                 "/a/b/c/d",
25                 []string{
26                         "/a/b/c/d",
27                         "/a/b/c",
28                         "/a/b",
29                         "/a",
30                         "/",
31                 },
32         }, {
33                 "/a",
34                 []string{
35                         "/a",
36                         "/",
37                 },
38         }, {
39                 "/",
40                 []string{
41                         "/",
42                 },
43         }}
44
45         for _, tc := range testCases {
46                 var got []string
47                 if !walkToRoot(tc.name, func(name0 string, first bool) bool {
48                         if first != (len(got) == 0) {
49                                 t.Errorf("name=%q: first=%t but len(got)==%d", tc.name, first, len(got))
50                                 return false
51                         }
52                         got = append(got, name0)
53                         return true
54                 }) {
55                         continue
56                 }
57                 if !reflect.DeepEqual(got, tc.want) {
58                         t.Errorf("name=%q:\ngot  %q\nwant %q", tc.name, got, tc.want)
59                 }
60         }
61 }
62
63 var lockTestDurations = []time.Duration{
64         infiniteTimeout, // infiniteTimeout means to never expire.
65         0,               // A zero duration means to expire immediately.
66         100 * time.Hour, // A very large duration will not expire in these tests.
67 }
68
69 // lockTestNames are the names of a set of mutually compatible locks. For each
70 // name fragment:
71 //      - _ means no explicit lock.
72 //      - i means a infinite-depth lock,
73 //      - z means a zero-depth lock,
74 var lockTestNames = []string{
75         "/_/_/_/_/z",
76         "/_/_/i",
77         "/_/z",
78         "/_/z/i",
79         "/_/z/z",
80         "/_/z/_/i",
81         "/_/z/_/z",
82         "/i",
83         "/z",
84         "/z/_/i",
85         "/z/_/z",
86 }
87
88 func lockTestZeroDepth(name string) bool {
89         switch name[len(name)-1] {
90         case 'i':
91                 return false
92         case 'z':
93                 return true
94         }
95         panic(fmt.Sprintf("lock name %q did not end with 'i' or 'z'", name))
96 }
97
98 func TestMemLSCanCreate(t *testing.T) {
99         now := time.Unix(0, 0)
100         m := NewMemLS().(*memLS)
101
102         for _, name := range lockTestNames {
103                 _, err := m.Create(now, LockDetails{
104                         Root:      name,
105                         Duration:  infiniteTimeout,
106                         ZeroDepth: lockTestZeroDepth(name),
107                 })
108                 if err != nil {
109                         t.Fatalf("creating lock for %q: %v", name, err)
110                 }
111         }
112
113         wantCanCreate := func(name string, zeroDepth bool) bool {
114                 for _, n := range lockTestNames {
115                         switch {
116                         case n == name:
117                                 // An existing lock has the same name as the proposed lock.
118                                 return false
119                         case strings.HasPrefix(n, name):
120                                 // An existing lock would be a child of the proposed lock,
121                                 // which conflicts if the proposed lock has infinite depth.
122                                 if !zeroDepth {
123                                         return false
124                                 }
125                         case strings.HasPrefix(name, n):
126                                 // An existing lock would be an ancestor of the proposed lock,
127                                 // which conflicts if the ancestor has infinite depth.
128                                 if n[len(n)-1] == 'i' {
129                                         return false
130                                 }
131                         }
132                 }
133                 return true
134         }
135
136         var check func(int, string)
137         check = func(recursion int, name string) {
138                 for _, zeroDepth := range []bool{false, true} {
139                         got := m.canCreate(name, zeroDepth)
140                         want := wantCanCreate(name, zeroDepth)
141                         if got != want {
142                                 t.Errorf("canCreate name=%q zeroDepth=%t: got %t, want %t", name, zeroDepth, got, want)
143                         }
144                 }
145                 if recursion == 6 {
146                         return
147                 }
148                 if name != "/" {
149                         name += "/"
150                 }
151                 for _, c := range "_iz" {
152                         check(recursion+1, name+string(c))
153                 }
154         }
155         check(0, "/")
156 }
157
158 func TestMemLSLookup(t *testing.T) {
159         now := time.Unix(0, 0)
160         m := NewMemLS().(*memLS)
161
162         badToken := m.nextToken()
163         t.Logf("badToken=%q", badToken)
164
165         for _, name := range lockTestNames {
166                 token, err := m.Create(now, LockDetails{
167                         Root:      name,
168                         Duration:  infiniteTimeout,
169                         ZeroDepth: lockTestZeroDepth(name),
170                 })
171                 if err != nil {
172                         t.Fatalf("creating lock for %q: %v", name, err)
173                 }
174                 t.Logf("%-15q -> node=%p token=%q", name, m.byName[name], token)
175         }
176
177         baseNames := append([]string{"/a", "/b/c"}, lockTestNames...)
178         for _, baseName := range baseNames {
179                 for _, suffix := range []string{"", "/0", "/1/2/3"} {
180                         name := baseName + suffix
181
182                         goodToken := ""
183                         base := m.byName[baseName]
184                         if base != nil && (suffix == "" || !lockTestZeroDepth(baseName)) {
185                                 goodToken = base.token
186                         }
187
188                         for _, token := range []string{badToken, goodToken} {
189                                 if token == "" {
190                                         continue
191                                 }
192
193                                 got := m.lookup(name, Condition{Token: token})
194                                 want := base
195                                 if token == badToken {
196                                         want = nil
197                                 }
198                                 if got != want {
199                                         t.Errorf("name=%-20qtoken=%q (bad=%t): got %p, want %p",
200                                                 name, token, token == badToken, got, want)
201                                 }
202                         }
203                 }
204         }
205 }
206
207 func TestMemLSConfirm(t *testing.T) {
208         now := time.Unix(0, 0)
209         m := NewMemLS().(*memLS)
210         alice, err := m.Create(now, LockDetails{
211                 Root:      "/alice",
212                 Duration:  infiniteTimeout,
213                 ZeroDepth: false,
214         })
215         tweedle, err := m.Create(now, LockDetails{
216                 Root:      "/tweedle",
217                 Duration:  infiniteTimeout,
218                 ZeroDepth: false,
219         })
220         if err != nil {
221                 t.Fatalf("Create: %v", err)
222         }
223         if err := m.consistent(); err != nil {
224                 t.Fatalf("Create: inconsistent state: %v", err)
225         }
226
227         // Test a mismatch between name and condition.
228         _, err = m.Confirm(now, "/tweedle/dee", "", Condition{Token: alice})
229         if err != ErrConfirmationFailed {
230                 t.Fatalf("Confirm (mismatch): got %v, want ErrConfirmationFailed", err)
231         }
232         if err := m.consistent(); err != nil {
233                 t.Fatalf("Confirm (mismatch): inconsistent state: %v", err)
234         }
235
236         // Test two names (that fall under the same lock) in the one Confirm call.
237         release, err := m.Confirm(now, "/tweedle/dee", "/tweedle/dum", Condition{Token: tweedle})
238         if err != nil {
239                 t.Fatalf("Confirm (twins): %v", err)
240         }
241         if err := m.consistent(); err != nil {
242                 t.Fatalf("Confirm (twins): inconsistent state: %v", err)
243         }
244         release()
245         if err := m.consistent(); err != nil {
246                 t.Fatalf("release (twins): inconsistent state: %v", err)
247         }
248
249         // Test the same two names in overlapping Confirm / release calls.
250         releaseDee, err := m.Confirm(now, "/tweedle/dee", "", Condition{Token: tweedle})
251         if err != nil {
252                 t.Fatalf("Confirm (sequence #0): %v", err)
253         }
254         if err := m.consistent(); err != nil {
255                 t.Fatalf("Confirm (sequence #0): inconsistent state: %v", err)
256         }
257
258         _, err = m.Confirm(now, "/tweedle/dum", "", Condition{Token: tweedle})
259         if err != ErrConfirmationFailed {
260                 t.Fatalf("Confirm (sequence #1): got %v, want ErrConfirmationFailed", err)
261         }
262         if err := m.consistent(); err != nil {
263                 t.Fatalf("Confirm (sequence #1): inconsistent state: %v", err)
264         }
265
266         releaseDee()
267         if err := m.consistent(); err != nil {
268                 t.Fatalf("release (sequence #2): inconsistent state: %v", err)
269         }
270
271         releaseDum, err := m.Confirm(now, "/tweedle/dum", "", Condition{Token: tweedle})
272         if err != nil {
273                 t.Fatalf("Confirm (sequence #3): %v", err)
274         }
275         if err := m.consistent(); err != nil {
276                 t.Fatalf("Confirm (sequence #3): inconsistent state: %v", err)
277         }
278
279         // Test that you can't unlock a held lock.
280         err = m.Unlock(now, tweedle)
281         if err != ErrLocked {
282                 t.Fatalf("Unlock (sequence #4): got %v, want ErrLocked", err)
283         }
284
285         releaseDum()
286         if err := m.consistent(); err != nil {
287                 t.Fatalf("release (sequence #5): inconsistent state: %v", err)
288         }
289
290         err = m.Unlock(now, tweedle)
291         if err != nil {
292                 t.Fatalf("Unlock (sequence #6): %v", err)
293         }
294         if err := m.consistent(); err != nil {
295                 t.Fatalf("Unlock (sequence #6): inconsistent state: %v", err)
296         }
297 }
298
299 func TestMemLSNonCanonicalRoot(t *testing.T) {
300         now := time.Unix(0, 0)
301         m := NewMemLS().(*memLS)
302         token, err := m.Create(now, LockDetails{
303                 Root:     "/foo/./bar//",
304                 Duration: 1 * time.Second,
305         })
306         if err != nil {
307                 t.Fatalf("Create: %v", err)
308         }
309         if err := m.consistent(); err != nil {
310                 t.Fatalf("Create: inconsistent state: %v", err)
311         }
312         if err := m.Unlock(now, token); err != nil {
313                 t.Fatalf("Unlock: %v", err)
314         }
315         if err := m.consistent(); err != nil {
316                 t.Fatalf("Unlock: inconsistent state: %v", err)
317         }
318 }
319
320 func TestMemLSExpiry(t *testing.T) {
321         m := NewMemLS().(*memLS)
322         testCases := []string{
323                 "setNow 0",
324                 "create /a.5",
325                 "want /a.5",
326                 "create /c.6",
327                 "want /a.5 /c.6",
328                 "create /a/b.7",
329                 "want /a.5 /a/b.7 /c.6",
330                 "setNow 4",
331                 "want /a.5 /a/b.7 /c.6",
332                 "setNow 5",
333                 "want /a/b.7 /c.6",
334                 "setNow 6",
335                 "want /a/b.7",
336                 "setNow 7",
337                 "want ",
338                 "setNow 8",
339                 "want ",
340                 "create /a.12",
341                 "create /b.13",
342                 "create /c.15",
343                 "create /a/d.16",
344                 "want /a.12 /a/d.16 /b.13 /c.15",
345                 "refresh /a.14",
346                 "want /a.14 /a/d.16 /b.13 /c.15",
347                 "setNow 12",
348                 "want /a.14 /a/d.16 /b.13 /c.15",
349                 "setNow 13",
350                 "want /a.14 /a/d.16 /c.15",
351                 "setNow 14",
352                 "want /a/d.16 /c.15",
353                 "refresh /a/d.20",
354                 "refresh /c.20",
355                 "want /a/d.20 /c.20",
356                 "setNow 20",
357                 "want ",
358         }
359
360         tokens := map[string]string{}
361         zTime := time.Unix(0, 0)
362         now := zTime
363         for i, tc := range testCases {
364                 j := strings.IndexByte(tc, ' ')
365                 if j < 0 {
366                         t.Fatalf("test case #%d %q: invalid command", i, tc)
367                 }
368                 op, arg := tc[:j], tc[j+1:]
369                 switch op {
370                 default:
371                         t.Fatalf("test case #%d %q: invalid operation %q", i, tc, op)
372
373                 case "create", "refresh":
374                         parts := strings.Split(arg, ".")
375                         if len(parts) != 2 {
376                                 t.Fatalf("test case #%d %q: invalid create", i, tc)
377                         }
378                         root := parts[0]
379                         d, err := strconv.Atoi(parts[1])
380                         if err != nil {
381                                 t.Fatalf("test case #%d %q: invalid duration", i, tc)
382                         }
383                         dur := time.Unix(0, 0).Add(time.Duration(d) * time.Second).Sub(now)
384
385                         switch op {
386                         case "create":
387                                 token, err := m.Create(now, LockDetails{
388                                         Root:      root,
389                                         Duration:  dur,
390                                         ZeroDepth: true,
391                                 })
392                                 if err != nil {
393                                         t.Fatalf("test case #%d %q: Create: %v", i, tc, err)
394                                 }
395                                 tokens[root] = token
396
397                         case "refresh":
398                                 token := tokens[root]
399                                 if token == "" {
400                                         t.Fatalf("test case #%d %q: no token for %q", i, tc, root)
401                                 }
402                                 got, err := m.Refresh(now, token, dur)
403                                 if err != nil {
404                                         t.Fatalf("test case #%d %q: Refresh: %v", i, tc, err)
405                                 }
406                                 want := LockDetails{
407                                         Root:      root,
408                                         Duration:  dur,
409                                         ZeroDepth: true,
410                                 }
411                                 if got != want {
412                                         t.Fatalf("test case #%d %q:\ngot  %v\nwant %v", i, tc, got, want)
413                                 }
414                         }
415
416                 case "setNow":
417                         d, err := strconv.Atoi(arg)
418                         if err != nil {
419                                 t.Fatalf("test case #%d %q: invalid duration", i, tc)
420                         }
421                         now = time.Unix(0, 0).Add(time.Duration(d) * time.Second)
422
423                 case "want":
424                         m.mu.Lock()
425                         m.collectExpiredNodes(now)
426                         got := make([]string, 0, len(m.byToken))
427                         for _, n := range m.byToken {
428                                 got = append(got, fmt.Sprintf("%s.%d",
429                                         n.details.Root, n.expiry.Sub(zTime)/time.Second))
430                         }
431                         m.mu.Unlock()
432                         sort.Strings(got)
433                         want := []string{}
434                         if arg != "" {
435                                 want = strings.Split(arg, " ")
436                         }
437                         if !reflect.DeepEqual(got, want) {
438                                 t.Fatalf("test case #%d %q:\ngot  %q\nwant %q", i, tc, got, want)
439                         }
440                 }
441
442                 if err := m.consistent(); err != nil {
443                         t.Fatalf("test case #%d %q: inconsistent state: %v", i, tc, err)
444                 }
445         }
446 }
447
448 func TestMemLS(t *testing.T) {
449         now := time.Unix(0, 0)
450         m := NewMemLS().(*memLS)
451         rng := rand.New(rand.NewSource(0))
452         tokens := map[string]string{}
453         nConfirm, nCreate, nRefresh, nUnlock := 0, 0, 0, 0
454         const N = 2000
455
456         for i := 0; i < N; i++ {
457                 name := lockTestNames[rng.Intn(len(lockTestNames))]
458                 duration := lockTestDurations[rng.Intn(len(lockTestDurations))]
459                 confirmed, unlocked := false, false
460
461                 // If the name was already locked, we randomly confirm/release, refresh
462                 // or unlock it. Otherwise, we create a lock.
463                 token := tokens[name]
464                 if token != "" {
465                         switch rng.Intn(3) {
466                         case 0:
467                                 confirmed = true
468                                 nConfirm++
469                                 release, err := m.Confirm(now, name, "", Condition{Token: token})
470                                 if err != nil {
471                                         t.Fatalf("iteration #%d: Confirm %q: %v", i, name, err)
472                                 }
473                                 if err := m.consistent(); err != nil {
474                                         t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
475                                 }
476                                 release()
477
478                         case 1:
479                                 nRefresh++
480                                 if _, err := m.Refresh(now, token, duration); err != nil {
481                                         t.Fatalf("iteration #%d: Refresh %q: %v", i, name, err)
482                                 }
483
484                         case 2:
485                                 unlocked = true
486                                 nUnlock++
487                                 if err := m.Unlock(now, token); err != nil {
488                                         t.Fatalf("iteration #%d: Unlock %q: %v", i, name, err)
489                                 }
490                         }
491
492                 } else {
493                         nCreate++
494                         var err error
495                         token, err = m.Create(now, LockDetails{
496                                 Root:      name,
497                                 Duration:  duration,
498                                 ZeroDepth: lockTestZeroDepth(name),
499                         })
500                         if err != nil {
501                                 t.Fatalf("iteration #%d: Create %q: %v", i, name, err)
502                         }
503                 }
504
505                 if !confirmed {
506                         if duration == 0 || unlocked {
507                                 // A zero-duration lock should expire immediately and is
508                                 // effectively equivalent to being unlocked.
509                                 tokens[name] = ""
510                         } else {
511                                 tokens[name] = token
512                         }
513                 }
514
515                 if err := m.consistent(); err != nil {
516                         t.Fatalf("iteration #%d: inconsistent state: %v", i, err)
517                 }
518         }
519
520         if nConfirm < N/10 {
521                 t.Fatalf("too few Confirm calls: got %d, want >= %d", nConfirm, N/10)
522         }
523         if nCreate < N/10 {
524                 t.Fatalf("too few Create calls: got %d, want >= %d", nCreate, N/10)
525         }
526         if nRefresh < N/10 {
527                 t.Fatalf("too few Refresh calls: got %d, want >= %d", nRefresh, N/10)
528         }
529         if nUnlock < N/10 {
530                 t.Fatalf("too few Unlock calls: got %d, want >= %d", nUnlock, N/10)
531         }
532 }
533
534 func (m *memLS) consistent() error {
535         m.mu.Lock()
536         defer m.mu.Unlock()
537
538         // If m.byName is non-empty, then it must contain an entry for the root "/",
539         // and its refCount should equal the number of locked nodes.
540         if len(m.byName) > 0 {
541                 n := m.byName["/"]
542                 if n == nil {
543                         return fmt.Errorf(`non-empty m.byName does not contain the root "/"`)
544                 }
545                 if n.refCount != len(m.byToken) {
546                         return fmt.Errorf("root node refCount=%d, differs from len(m.byToken)=%d", n.refCount, len(m.byToken))
547                 }
548         }
549
550         for name, n := range m.byName {
551                 // The map keys should be consistent with the node's copy of the key.
552                 if n.details.Root != name {
553                         return fmt.Errorf("node name %q != byName map key %q", n.details.Root, name)
554                 }
555
556                 // A name must be clean, and start with a "/".
557                 if len(name) == 0 || name[0] != '/' {
558                         return fmt.Errorf(`node name %q does not start with "/"`, name)
559                 }
560                 if name != path.Clean(name) {
561                         return fmt.Errorf(`node name %q is not clean`, name)
562                 }
563
564                 // A node's refCount should be positive.
565                 if n.refCount <= 0 {
566                         return fmt.Errorf("non-positive refCount for node at name %q", name)
567                 }
568
569                 // A node's refCount should be the number of self-or-descendents that
570                 // are locked (i.e. have a non-empty token).
571                 var list []string
572                 for name0, n0 := range m.byName {
573                         // All of lockTestNames' name fragments are one byte long: '_', 'i' or 'z',
574                         // so strings.HasPrefix is equivalent to self-or-descendent name match.
575                         // We don't have to worry about "/foo/bar" being a false positive match
576                         // for "/foo/b".
577                         if strings.HasPrefix(name0, name) && n0.token != "" {
578                                 list = append(list, name0)
579                         }
580                 }
581                 if n.refCount != len(list) {
582                         sort.Strings(list)
583                         return fmt.Errorf("node at name %q has refCount %d but locked self-or-descendents are %q (len=%d)",
584                                 name, n.refCount, list, len(list))
585                 }
586
587                 // A node n is in m.byToken if it has a non-empty token.
588                 if n.token != "" {
589                         if _, ok := m.byToken[n.token]; !ok {
590                                 return fmt.Errorf("node at name %q has token %q but not in m.byToken", name, n.token)
591                         }
592                 }
593
594                 // A node n is in m.byExpiry if it has a non-negative byExpiryIndex.
595                 if n.byExpiryIndex >= 0 {
596                         if n.byExpiryIndex >= len(m.byExpiry) {
597                                 return fmt.Errorf("node at name %q has byExpiryIndex %d but m.byExpiry has length %d", name, n.byExpiryIndex, len(m.byExpiry))
598                         }
599                         if n != m.byExpiry[n.byExpiryIndex] {
600                                 return fmt.Errorf("node at name %q has byExpiryIndex %d but that indexes a different node", name, n.byExpiryIndex)
601                         }
602                 }
603         }
604
605         for token, n := range m.byToken {
606                 // The map keys should be consistent with the node's copy of the key.
607                 if n.token != token {
608                         return fmt.Errorf("node token %q != byToken map key %q", n.token, token)
609                 }
610
611                 // Every node in m.byToken is in m.byName.
612                 if _, ok := m.byName[n.details.Root]; !ok {
613                         return fmt.Errorf("node at name %q in m.byToken but not in m.byName", n.details.Root)
614                 }
615         }
616
617         for i, n := range m.byExpiry {
618                 // The slice indices should be consistent with the node's copy of the index.
619                 if n.byExpiryIndex != i {
620                         return fmt.Errorf("node byExpiryIndex %d != byExpiry slice index %d", n.byExpiryIndex, i)
621                 }
622
623                 // Every node in m.byExpiry is in m.byName.
624                 if _, ok := m.byName[n.details.Root]; !ok {
625                         return fmt.Errorf("node at name %q in m.byExpiry but not in m.byName", n.details.Root)
626                 }
627
628                 // No node in m.byExpiry should be held.
629                 if n.held {
630                         return fmt.Errorf("node at name %q in m.byExpiry is held", n.details.Root)
631                 }
632         }
633         return nil
634 }
635
636 func TestParseTimeout(t *testing.T) {
637         testCases := []struct {
638                 s       string
639                 want    time.Duration
640                 wantErr error
641         }{{
642                 "",
643                 infiniteTimeout,
644                 nil,
645         }, {
646                 "Infinite",
647                 infiniteTimeout,
648                 nil,
649         }, {
650                 "Infinitesimal",
651                 0,
652                 errInvalidTimeout,
653         }, {
654                 "infinite",
655                 0,
656                 errInvalidTimeout,
657         }, {
658                 "Second-0",
659                 0 * time.Second,
660                 nil,
661         }, {
662                 "Second-123",
663                 123 * time.Second,
664                 nil,
665         }, {
666                 "  Second-456    ",
667                 456 * time.Second,
668                 nil,
669         }, {
670                 "Second-4100000000",
671                 4100000000 * time.Second,
672                 nil,
673         }, {
674                 "junk",
675                 0,
676                 errInvalidTimeout,
677         }, {
678                 "Second-",
679                 0,
680                 errInvalidTimeout,
681         }, {
682                 "Second--1",
683                 0,
684                 errInvalidTimeout,
685         }, {
686                 "Second--123",
687                 0,
688                 errInvalidTimeout,
689         }, {
690                 "Second-+123",
691                 0,
692                 errInvalidTimeout,
693         }, {
694                 "Second-0x123",
695                 0,
696                 errInvalidTimeout,
697         }, {
698                 "second-123",
699                 0,
700                 errInvalidTimeout,
701         }, {
702                 "Second-4294967295",
703                 4294967295 * time.Second,
704                 nil,
705         }, {
706                 // Section 10.7 says that "The timeout value for TimeType "Second"
707                 // must not be greater than 2^32-1."
708                 "Second-4294967296",
709                 0,
710                 errInvalidTimeout,
711         }, {
712                 // This test case comes from section 9.10.9 of the spec. It says,
713                 //
714                 // "In this request, the client has specified that it desires an
715                 // infinite-length lock, if available, otherwise a timeout of 4.1
716                 // billion seconds, if available."
717                 //
718                 // The Go WebDAV package always supports infinite length locks,
719                 // and ignores the fallback after the comma.
720                 "Infinite, Second-4100000000",
721                 infiniteTimeout,
722                 nil,
723         }}
724
725         for _, tc := range testCases {
726                 got, gotErr := parseTimeout(tc.s)
727                 if got != tc.want || gotErr != tc.wantErr {
728                         t.Errorf("parsing %q:\ngot  %v, %v\nwant %v, %v", tc.s, got, gotErr, tc.want, tc.wantErr)
729                 }
730         }
731 }