github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/test/dsl_test.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"path"
    11  	"reflect"
    12  	"regexp"
    13  	"sort"
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/keybase/client/go/kbfs/data"
    20  	"github.com/keybase/client/go/kbfs/kbfsmd"
    21  	"github.com/keybase/client/go/kbfs/libfs"
    22  	"github.com/keybase/client/go/kbfs/libkbfs"
    23  	"github.com/keybase/client/go/kbfs/test/clocktest"
    24  	"github.com/keybase/client/go/kbfs/tlf"
    25  	kbname "github.com/keybase/client/go/kbun"
    26  	"github.com/keybase/client/go/protocol/keybase1"
    27  )
    28  
    29  type m map[string]string
    30  
    31  const (
    32  	alice   = username("alice")
    33  	bob     = username("bob")
    34  	charlie = username("charlie")
    35  	eve     = username("eve")
    36  )
    37  
    38  type opt struct {
    39  	ver                      kbfsmd.MetadataVer
    40  	usernames                []kbname.NormalizedUsername
    41  	teams                    teamMap
    42  	implicitTeams            teamMap
    43  	tlfName                  string
    44  	expectedCanonicalTlfName string
    45  	tlfType                  tlf.Type
    46  	tlfRevision              kbfsmd.Revision
    47  	tlfTime                  string
    48  	tlfRelTime               string
    49  	users                    map[kbname.NormalizedUsername]User
    50  	stallers                 map[kbname.NormalizedUsername]*libkbfs.NaïveStaller
    51  	tb                       testing.TB
    52  	initOnce                 sync.Once
    53  	engine                   Engine
    54  	blockSize                int64
    55  	blockChangeSize          int64
    56  	batchSize                int
    57  	bwKBps                   int
    58  	timeout                  time.Duration
    59  	clock                    *clocktest.TestClock
    60  	isParallel               bool
    61  	journal                  bool
    62  }
    63  
    64  // run{Test,Benchmark}OverMetadataVers are copied from
    65  // libkbfs/bare_root_metadata_test.go, so as to avoid having libkbfs
    66  // depend on testing.
    67  
    68  // Also copy testMetadataVers, so that we can set it independently
    69  // from libkbfs tests.
    70  var testMetadataVers = []kbfsmd.MetadataVer{
    71  	kbfsmd.InitialExtraMetadataVer, kbfsmd.ImplicitTeamsVer,
    72  }
    73  
    74  // runTestOverMetadataVers runs the given test function over all
    75  // metadata versions to test.
    76  func runTestOverMetadataVers(
    77  	t *testing.T, f func(t *testing.T, ver kbfsmd.MetadataVer)) {
    78  	for _, ver := range testMetadataVers {
    79  		ver := ver // capture range variable.
    80  		t.Run(ver.String(), func(t *testing.T) {
    81  			// Don't do t.Parallel() for now, as FUSE DSL
    82  			// tests might not like it.
    83  			f(t, ver)
    84  		})
    85  	}
    86  }
    87  
    88  // runBenchmarkOverMetadataVers runs the given benchmark function over
    89  // all metadata versions to test.
    90  func runBenchmarkOverMetadataVers(
    91  	b *testing.B, f func(b *testing.B, ver kbfsmd.MetadataVer)) {
    92  	for _, ver := range testMetadataVers {
    93  		ver := ver // capture range variable.
    94  		b.Run(ver.String(), func(b *testing.B) {
    95  			f(b, ver)
    96  		})
    97  	}
    98  }
    99  
   100  func runOneTestOrBenchmark(
   101  	tb testing.TB, ver kbfsmd.MetadataVer, actions ...optionOp) {
   102  	o := &opt{
   103  		ver:    ver,
   104  		tb:     tb,
   105  		engine: createEngine(tb),
   106  	}
   107  	defer o.close()
   108  	for _, omod := range actions {
   109  		omod(o)
   110  	}
   111  }
   112  
   113  func test(t *testing.T, actions ...optionOp) {
   114  	runTestOverMetadataVers(t, func(t *testing.T, ver kbfsmd.MetadataVer) {
   115  		runOneTestOrBenchmark(t, ver, actions...)
   116  	})
   117  }
   118  
   119  func benchmark(b *testing.B, actions ...optionOp) {
   120  	runBenchmarkOverMetadataVers(
   121  		b, func(b *testing.B, ver kbfsmd.MetadataVer) {
   122  			runOneTestOrBenchmark(silentBenchmark{b}, ver, actions...)
   123  		})
   124  }
   125  
   126  func parallel(actions ...optionOp) optionOp {
   127  	return func(o *opt) {
   128  		o.isParallel = true
   129  		wg := &sync.WaitGroup{}
   130  		for _, omod := range actions {
   131  			wg.Add(1)
   132  			go func(omod optionOp) {
   133  				omod(o)
   134  				wg.Done()
   135  			}(omod)
   136  		}
   137  		wg.Wait()
   138  	}
   139  }
   140  
   141  func sequential(actions ...optionOp) optionOp {
   142  	return func(o *opt) {
   143  		for _, omod := range actions {
   144  			omod(o)
   145  		}
   146  	}
   147  }
   148  
   149  type errorList struct {
   150  	el []error
   151  }
   152  
   153  func (el errorList) Error() string {
   154  	return fmt.Sprintf("%v", el.el)
   155  }
   156  
   157  func (o *opt) close() {
   158  	var el []error
   159  	// Make sure Shutdown is called properly for every user, even
   160  	// if any of the calls fail.
   161  	for _, user := range o.users {
   162  		err := o.engine.Shutdown(user)
   163  		if err != nil {
   164  			el = append(el, err)
   165  		}
   166  	}
   167  
   168  	var err error
   169  	if len(el) > 0 {
   170  		err = errorList{el}
   171  	}
   172  
   173  	o.expectSuccess("Shutdown", err)
   174  }
   175  
   176  func (o *opt) runInitOnce() {
   177  	o.initOnce.Do(func() {
   178  		o.clock = &clocktest.TestClock{}
   179  		o.clock.Set(time.Unix(1, 0))
   180  		o.users = o.engine.InitTest(o.ver, o.blockSize,
   181  			o.blockChangeSize, o.batchSize, o.bwKBps, o.timeout, o.usernames,
   182  			o.teams, o.implicitTeams, o.clock, o.journal)
   183  		o.stallers = o.makeStallers()
   184  	})
   185  }
   186  
   187  func (o *opt) makeStallers() (
   188  	stallers map[kbname.NormalizedUsername]*libkbfs.NaïveStaller) {
   189  	stallers = make(map[kbname.NormalizedUsername]*libkbfs.NaïveStaller)
   190  	for username, user := range o.users {
   191  		stallers[username] = o.engine.MakeNaïveStaller(user)
   192  	}
   193  	return stallers
   194  }
   195  
   196  func ntimesString(n int, s string) string {
   197  	var bs bytes.Buffer
   198  	for i := 0; i < n; i++ {
   199  		bs.WriteString(s)
   200  	}
   201  	return bs.String()
   202  }
   203  
   204  type optionOp func(*opt)
   205  
   206  func blockSize(n int64) optionOp {
   207  	return func(o *opt) {
   208  		o.blockSize = n
   209  	}
   210  }
   211  
   212  func blockChangeSize(n int64) optionOp {
   213  	return func(o *opt) {
   214  		o.blockChangeSize = n
   215  	}
   216  }
   217  
   218  func batchSize(n int) optionOp {
   219  	return func(o *opt) {
   220  		o.batchSize = n
   221  	}
   222  }
   223  
   224  func bandwidth(n int) optionOp {
   225  	return func(o *opt) {
   226  		o.bwKBps = n
   227  	}
   228  }
   229  
   230  func opTimeout(n time.Duration) optionOp {
   231  	return func(o *opt) {
   232  		o.timeout = n
   233  	}
   234  }
   235  
   236  func journal() optionOp {
   237  	return func(o *opt) {
   238  		o.journal = true
   239  	}
   240  }
   241  
   242  func skip(implementation, reason string) optionOp {
   243  	return func(o *opt) {
   244  		if o.engine.Name() == implementation {
   245  			o.tb.Skip(reason)
   246  		}
   247  	}
   248  }
   249  
   250  func users(ns ...username) optionOp {
   251  	return func(o *opt) {
   252  		var a []string
   253  		for _, u := range ns {
   254  			username := kbname.NewNormalizedUsername(string(u))
   255  			o.usernames = append(o.usernames, username)
   256  			a = append(a, string(username))
   257  		}
   258  		// Default to the private TLF shared by all the users.
   259  		sort.Strings(a)
   260  		tlfName := strings.Join(a, ",")
   261  		inPrivateTlf(tlfName)(o)
   262  	}
   263  }
   264  
   265  func team(teamName kbname.NormalizedUsername, writers string,
   266  	readers string) optionOp {
   267  	return func(o *opt) {
   268  		if o.ver < kbfsmd.SegregatedKeyBundlesVer {
   269  			o.tb.Skip("mdv2 doesn't support teams")
   270  		}
   271  		if o.teams == nil {
   272  			o.teams = make(teamMap)
   273  		}
   274  		var writerNames, readerNames []kbname.NormalizedUsername
   275  		for _, w := range strings.Split(writers, ",") {
   276  			writerNames = append(writerNames, kbname.NormalizedUsername(w))
   277  		}
   278  		if readers != "" {
   279  			for _, r := range strings.Split(readers, ",") {
   280  				readerNames = append(readerNames, kbname.NormalizedUsername(r))
   281  			}
   282  		}
   283  		o.teams[teamName] = teamMembers{writerNames, readerNames}
   284  	}
   285  }
   286  
   287  func implicitTeam(writers string, readers string) optionOp {
   288  	return func(o *opt) {
   289  		if o.ver < kbfsmd.ImplicitTeamsVer {
   290  			o.tb.Skip("mdv2 doesn't support teams")
   291  		}
   292  		if o.implicitTeams == nil {
   293  			o.implicitTeams = make(teamMap)
   294  		}
   295  
   296  		var writerNames, readerNames []kbname.NormalizedUsername
   297  		for _, w := range strings.Split(writers, ",") {
   298  			writerNames = append(writerNames, kbname.NormalizedUsername(w))
   299  		}
   300  		isPublic := false
   301  		if readers != "" {
   302  			for _, r := range strings.Split(readers, ",") {
   303  				readerNames = append(readerNames, kbname.NormalizedUsername(r))
   304  			}
   305  			isPublic = len(readerNames) == 1 && readers == "public"
   306  		}
   307  		var teamName tlf.CanonicalName
   308  		if isPublic {
   309  			teamName = tlf.MakeCanonicalName(
   310  				writerNames, nil, nil, nil, nil)
   311  		} else {
   312  			teamName = tlf.MakeCanonicalName(
   313  				writerNames, nil, readerNames, nil, nil)
   314  		}
   315  		o.implicitTeams[kbname.NormalizedUsername(teamName)] =
   316  			teamMembers{writerNames, readerNames}
   317  	}
   318  }
   319  
   320  func inPrivateTlf(name string) optionOp {
   321  	return func(o *opt) {
   322  		o.tlfName = name
   323  		o.expectedCanonicalTlfName = name
   324  		o.tlfType = tlf.Private
   325  	}
   326  }
   327  
   328  func inPrivateTlfAtRevision(name string, rev kbfsmd.Revision) optionOp {
   329  	return func(o *opt) {
   330  		o.tlfName = name
   331  		o.expectedCanonicalTlfName = name
   332  		o.tlfType = tlf.Private
   333  		o.tlfRevision = rev
   334  	}
   335  }
   336  
   337  func inPrivateTlfAtTime(name string, timeString string) optionOp {
   338  	return func(o *opt) {
   339  		o.tlfName = name
   340  		o.expectedCanonicalTlfName = name
   341  		o.tlfType = tlf.Private
   342  		o.tlfTime = timeString
   343  	}
   344  }
   345  
   346  func inPrivateTlfAtRelativeTime(name string, relTimeString string) optionOp {
   347  	return func(o *opt) {
   348  		o.tlfName = name
   349  		o.expectedCanonicalTlfName = name
   350  		o.tlfType = tlf.Private
   351  		o.tlfRelTime = relTimeString
   352  	}
   353  }
   354  
   355  func inPrivateTlfNonCanonical(name, expectedCanonicalName string) optionOp {
   356  	return func(o *opt) {
   357  		o.tlfName = name
   358  		o.expectedCanonicalTlfName = expectedCanonicalName
   359  		o.tlfType = tlf.Private
   360  	}
   361  }
   362  
   363  func inPublicTlf(name string) optionOp {
   364  	return func(o *opt) {
   365  		o.tlfName = name
   366  		o.expectedCanonicalTlfName = name
   367  		o.tlfType = tlf.Public
   368  	}
   369  }
   370  
   371  func inPublicTlfNonCanonical(name, expectedCanonicalName string) optionOp {
   372  	return func(o *opt) {
   373  		o.tlfName = name
   374  		o.expectedCanonicalTlfName = expectedCanonicalName
   375  		o.tlfType = tlf.Public
   376  	}
   377  }
   378  
   379  func inSingleTeamTlf(name string) optionOp {
   380  	return func(o *opt) {
   381  		o.tlfName = name
   382  		o.expectedCanonicalTlfName = name
   383  		o.tlfType = tlf.SingleTeam
   384  	}
   385  }
   386  
   387  func inSingleTeamNonCanonical(name, expectedCanonicalName string) optionOp {
   388  	return func(o *opt) {
   389  		o.tlfName = name
   390  		o.expectedCanonicalTlfName = expectedCanonicalName
   391  		o.tlfType = tlf.SingleTeam
   392  	}
   393  }
   394  
   395  func addNewAssertion(oldAssertion, newAssertion string) optionOp {
   396  	return func(o *opt) {
   397  		o.tb.Logf("addNewAssertion: %q -> %q", oldAssertion, newAssertion)
   398  		for _, u := range o.users {
   399  			err := o.engine.AddNewAssertion(u, oldAssertion, newAssertion)
   400  			o.expectSuccess("addNewAssertion", err)
   401  			// Sync the TLF to wait for the TLF handle change
   402  			// notifications to be processed. Ignore the error since
   403  			// the user might not even have access to that TLF.
   404  			_ = o.engine.SyncFromServer(u, o.tlfName, o.tlfType)
   405  		}
   406  	}
   407  }
   408  
   409  func changeTeamName(oldName, newName string) optionOp {
   410  	return func(o *opt) {
   411  		o.teams[kbname.NormalizedUsername(newName)] =
   412  			o.teams[kbname.NormalizedUsername(oldName)]
   413  		delete(o.teams, kbname.NormalizedUsername(oldName))
   414  		o.tb.Logf("changeTeamName: %q -> %q", oldName, newName)
   415  		for _, u := range o.users {
   416  			err := o.engine.ChangeTeamName(u, oldName, newName)
   417  			o.expectSuccess("changeTeamName", err)
   418  		}
   419  	}
   420  }
   421  
   422  type fileOp struct {
   423  	operation   func(*ctx) error
   424  	flags       fileOpFlags
   425  	description string
   426  }
   427  type fileOpFlags uint32
   428  
   429  const (
   430  	Defaults = fileOpFlags(0)
   431  	IsInit   = fileOpFlags(1)
   432  )
   433  
   434  type ctx struct {
   435  	*opt
   436  	user       User
   437  	username   kbname.NormalizedUsername
   438  	rootNode   Node
   439  	noSyncInit bool
   440  	staller    *libkbfs.NaïveStaller
   441  	noSyncEnd  bool
   442  }
   443  
   444  func runFileOpHelper(c *ctx, fop fileOp) (string, error) {
   445  	desc := fmt.Sprintf("(%s) %s", c.username, fop.description)
   446  	c.tb.Log(desc)
   447  	err := fop.operation(c)
   448  	if err != nil {
   449  		c.tb.Logf("%s failed with %s", desc, err)
   450  	}
   451  	return desc, err
   452  }
   453  
   454  func runFileOp(c *ctx, fop fileOp) (string, error) {
   455  	if c.rootNode == nil && fop.flags&IsInit == 0 {
   456  		initOp := initRoot()
   457  		desc, err := runFileOpHelper(c, initOp)
   458  		if err != nil {
   459  			desc = fmt.Sprintf("%s for %s", desc, fop.description)
   460  			return desc, err
   461  		}
   462  	}
   463  	return runFileOpHelper(c, fop)
   464  }
   465  
   466  func expectError(op fileOp, reasonPrefix string) fileOp {
   467  	return fileOp{func(c *ctx) error {
   468  		_, err := runFileOp(c, op)
   469  		if err == nil {
   470  			return fmt.Errorf("Didn't get expected error (success while expecting failure): %q", reasonPrefix)
   471  		}
   472  		// Real filesystems don't give us the exact errors we wish for.
   473  		if c.engine.Name() == "libkbfs" &&
   474  			!strings.HasPrefix(err.Error(), reasonPrefix) {
   475  			return fmt.Errorf("Got the wrong error: expected prefix %q, got %q", reasonPrefix, err.Error())
   476  		}
   477  		return nil
   478  	}, IsInit, /* So that we can use expectError with e.g. initRoot(). */
   479  		fmt.Sprintf("expectError(%s, %s)",
   480  			op.description, reasonPrefix)}
   481  }
   482  
   483  func noSync() fileOp {
   484  	return fileOp{func(c *ctx) error {
   485  		c.noSyncInit = true
   486  		return nil
   487  	}, IsInit, "noSync()"}
   488  }
   489  
   490  func resetTimer() fileOp {
   491  	return fileOp{func(c *ctx) error {
   492  		switch b := c.tb.(type) {
   493  		case *testing.B:
   494  			b.ResetTimer()
   495  		case silentBenchmark:
   496  			b.ResetTimer()
   497  		}
   498  		return nil
   499  	}, Defaults, "resetTimer()"}
   500  }
   501  
   502  func startTimer() fileOp {
   503  	return fileOp{func(c *ctx) error {
   504  		switch b := c.tb.(type) {
   505  		case *testing.B:
   506  			b.StartTimer()
   507  		case silentBenchmark:
   508  			b.StartTimer()
   509  		}
   510  		return nil
   511  	}, Defaults, "startTimer()"}
   512  }
   513  
   514  func stopTimer() fileOp {
   515  	return fileOp{func(c *ctx) error {
   516  		switch b := c.tb.(type) {
   517  		case *testing.B:
   518  			b.StopTimer()
   519  		case silentBenchmark:
   520  			b.StopTimer()
   521  		}
   522  		return nil
   523  	}, Defaults, "stopTimer()"}
   524  }
   525  
   526  func getBenchN(n *int) fileOp {
   527  	return fileOp{func(c *ctx) error {
   528  		switch b := c.tb.(type) {
   529  		case *testing.B:
   530  			*n = b.N
   531  		case silentBenchmark:
   532  			*n = b.N
   533  		}
   534  		return nil
   535  	}, Defaults, "getBenchN()"}
   536  }
   537  
   538  // noSyncEnd turns off the SyncFromServer call at the end of each `as`
   539  // block.  It's also turned off by a `disableUpdates()` call or a
   540  // `noSync()` call.
   541  func noSyncEnd() fileOp {
   542  	return fileOp{func(c *ctx) error {
   543  		c.noSyncEnd = true
   544  		return nil
   545  	}, IsInit, "noSyncEnd()"}
   546  }
   547  
   548  func (o *opt) expectSuccess(reason string, err error) {
   549  	if err != nil {
   550  		if o.isParallel {
   551  			// FailNow/Fatalf can only be called from the goroutine running the Test
   552  			// function. In parallel tests, this is not always true. So we use Errorf
   553  			// to mark the test as failed without an implicit FailNow.
   554  			o.tb.Errorf("Error %s: %v", reason, err)
   555  		} else {
   556  			o.tb.Fatalf("Error %s: %v", reason, err)
   557  		}
   558  	}
   559  }
   560  
   561  func addTime(d time.Duration) fileOp {
   562  	return fileOp{func(c *ctx) error {
   563  		c.clock.Add(d)
   564  		return nil
   565  	}, Defaults, fmt.Sprintf("addTime(%s)", d)}
   566  }
   567  
   568  func as(user username, fops ...fileOp) optionOp {
   569  	return func(o *opt) {
   570  		o.tb.Log("as:", user)
   571  		o.runInitOnce()
   572  		u := kbname.NewNormalizedUsername(string(user))
   573  		ctx := &ctx{
   574  			opt:      o,
   575  			user:     o.users[u],
   576  			username: u,
   577  			staller:  o.stallers[u],
   578  		}
   579  
   580  		for _, fop := range fops {
   581  			desc, err := runFileOp(ctx, fop)
   582  			ctx.expectSuccess(desc, err)
   583  		}
   584  
   585  		// Sync everything to disk after this round of operations.
   586  		if !ctx.noSyncInit {
   587  			err := ctx.engine.SyncAll(ctx.user, ctx.tlfName, ctx.tlfType)
   588  			ctx.expectSuccess("SyncAll", err)
   589  			if !ctx.noSyncEnd {
   590  				err := ctx.engine.SyncFromServer(
   591  					ctx.user, ctx.tlfName, ctx.tlfType)
   592  				ctx.expectSuccess("SyncFromServer", err)
   593  			}
   594  		}
   595  	}
   596  }
   597  
   598  // initRoot initializes the root for an invocation of as(). Usually
   599  // not called directly.
   600  func initRoot() fileOp {
   601  	return fileOp{func(c *ctx) error {
   602  		if !c.noSyncInit {
   603  			// Do this before GetRootDir so that we pick
   604  			// up any TLF name changes.
   605  			err := c.engine.SyncFromServer(c.user, c.tlfName, c.tlfType)
   606  			if err != nil {
   607  				return err
   608  			}
   609  		}
   610  		var root Node
   611  		var err error
   612  		switch {
   613  		case c.tlfRevision != kbfsmd.RevisionUninitialized:
   614  			root, err = c.engine.GetRootDirAtRevision(
   615  				c.user, c.tlfName, c.tlfType, c.tlfRevision,
   616  				c.expectedCanonicalTlfName)
   617  		case c.tlfTime != "":
   618  			root, err = c.engine.GetRootDirAtTimeString(
   619  				c.user, c.tlfName, c.tlfType, c.tlfTime,
   620  				c.expectedCanonicalTlfName)
   621  		case c.tlfRelTime != "":
   622  			root, err = c.engine.GetRootDirAtRelTimeString(
   623  				c.user, c.tlfName, c.tlfType, c.tlfRelTime,
   624  				c.expectedCanonicalTlfName)
   625  		default:
   626  			root, err = c.engine.GetRootDir(
   627  				c.user, c.tlfName, c.tlfType, c.expectedCanonicalTlfName)
   628  		}
   629  		if err != nil {
   630  			return err
   631  		}
   632  		c.rootNode = root
   633  		return nil
   634  	}, IsInit, "initRoot()"}
   635  }
   636  
   637  func custom(f func(func(fileOp) error) error) fileOp {
   638  	return fileOp{func(c *ctx) error {
   639  		return f(func(fop fileOp) error { return fop.operation(c) })
   640  	}, Defaults, "custom()"}
   641  }
   642  
   643  func mkdir(name string) fileOp {
   644  	return fileOp{func(c *ctx) error {
   645  		_, _, err := c.getNode(name, createDir, resolveAllSyms)
   646  		return err
   647  	}, Defaults, fmt.Sprintf("mkdir(%s)", name)}
   648  }
   649  
   650  func write(name string, contents string) fileOp {
   651  	return writeBS(name, []byte(contents))
   652  }
   653  
   654  func writeBS(name string, contents []byte) fileOp {
   655  	return pwriteBS(name, contents, 0)
   656  }
   657  
   658  func pwriteBS(name string, contents []byte, off int64) fileOp {
   659  	return pwriteBSSync(name, contents, off, true)
   660  }
   661  
   662  func pwriteBSSync(name string, contents []byte, off int64, sync bool) fileOp {
   663  	return fileOp{func(c *ctx) error {
   664  		f, _, err := c.getNode(name, createFile, resolveAllSyms)
   665  		if err != nil {
   666  			return err
   667  		}
   668  		return c.engine.WriteFile(c.user, f, contents, off, sync)
   669  	}, Defaults, fmt.Sprintf("pwriteBSSync(%s, %d bytes, off=%d, sync=%t)",
   670  		name, len(contents), off, sync)}
   671  }
   672  
   673  func truncate(name string, size uint64) fileOp {
   674  	return fileOp{func(c *ctx) error {
   675  		f, _, err := c.getNode(name, createFile, resolveAllSyms)
   676  		if err != nil {
   677  			return err
   678  		}
   679  		return c.engine.TruncateFile(c.user, f, size, true)
   680  	}, Defaults, fmt.Sprintf("truncate(%s, %d)", name, size)}
   681  }
   682  
   683  func read(name string, contents string) fileOp {
   684  	return preadBS(name, []byte(contents), 0)
   685  }
   686  func preadBS(name string, contents []byte, at int64) fileOp {
   687  	return fileOp{func(c *ctx) error {
   688  		file, _, err := c.getNode(name, noCreate, resolveAllSyms)
   689  		if err != nil {
   690  			return err
   691  		}
   692  		bs := make([]byte, len(contents))
   693  		l, err := c.engine.ReadFile(c.user, file, at, bs)
   694  		if err != nil {
   695  			return err
   696  		}
   697  		bs = bs[:l]
   698  		if !bytes.Equal(bs, contents) {
   699  			return fmt.Errorf("Read (name=%s) got=%d, expected=%d bytes: contents=%s differ from expected=%s", name, len(bs), len(contents), bs, contents)
   700  		}
   701  		return nil
   702  	}, Defaults, fmt.Sprintf("preadBS(%s, %d bytes, at=%d)",
   703  		name, len(contents), at)}
   704  }
   705  
   706  func exists(filename string) fileOp {
   707  	return fileOp{func(c *ctx) error {
   708  		_, _, err := c.getNode(filename, noCreate, resolveAllSyms)
   709  		return err
   710  	}, Defaults, fmt.Sprintf("exists(%s)", filename)}
   711  }
   712  func notExists(filename string) fileOp {
   713  	return fileOp{func(c *ctx) error {
   714  		_, _, err := c.getNode(filename, noCreate, resolveAllSyms)
   715  		if err == nil {
   716  			return fmt.Errorf("File that should not exist exists: %q", filename)
   717  		}
   718  		return nil
   719  	}, Defaults, fmt.Sprintf("notExists(%s)", filename)}
   720  }
   721  
   722  func mkfileexcl(name string) fileOp {
   723  	return fileOp{func(c *ctx) error {
   724  		_, _, err := c.getNode(name, createFileExcl, resolveAllSyms)
   725  		return err
   726  	}, Defaults, fmt.Sprintf("mkfileexcl(%s)", name)}
   727  }
   728  
   729  func mkfile(name string, contents string) fileOp {
   730  	return fileOp{func(c *ctx) error {
   731  		f, wasCreated, err := c.getNode(name, createFile, resolveAllSyms)
   732  		if err != nil {
   733  			return err
   734  		}
   735  		if !wasCreated {
   736  			return fmt.Errorf("File %s already existed when mkfile was called",
   737  				name)
   738  		}
   739  		// Skip the write if the requested contents is empty.
   740  		if len(contents) == 0 {
   741  			return nil
   742  		}
   743  		return c.engine.WriteFile(c.user, f, []byte(contents), 0, true)
   744  	}, Defaults, fmt.Sprintf("mkfile(%s, %d bytes)", name, len(contents))}
   745  }
   746  
   747  func link(fromName, toPath string) fileOp {
   748  	return fileOp{func(c *ctx) error {
   749  		dir, name := path.Split(fromName)
   750  		parent, _, err := c.getNode(dir, noCreate, resolveAllSyms)
   751  		if err != nil {
   752  			return err
   753  		}
   754  		return c.engine.CreateLink(c.user, parent, name, toPath)
   755  	}, Defaults, fmt.Sprintf("link(%s => %s)", fromName, toPath)}
   756  }
   757  
   758  func setex(filepath string, ex bool) fileOp {
   759  	return fileOp{func(c *ctx) error {
   760  		file, _, err := c.getNode(filepath, noCreate, resolveAllSyms)
   761  		if err != nil {
   762  			return err
   763  		}
   764  		return c.engine.SetEx(c.user, file, ex)
   765  	}, Defaults, fmt.Sprintf("setex(%s, %t)", filepath, ex)}
   766  }
   767  
   768  func setmtime(filepath string, mtime time.Time) fileOp {
   769  	return fileOp{func(c *ctx) error {
   770  		file, _, err := c.getNode(filepath, noCreate, dontResolveFinalSym)
   771  		if err != nil {
   772  			return err
   773  		}
   774  		return c.engine.SetMtime(c.user, file, mtime)
   775  	}, Defaults, fmt.Sprintf("setmtime(%s, %s)", filepath, mtime)}
   776  }
   777  
   778  func mtime(filepath string, expectedMtime time.Time) fileOp {
   779  	return fileOp{func(c *ctx) error {
   780  		// If the expected time is zero, use the clock's current time.
   781  		if expectedMtime.IsZero() {
   782  			expectedMtime = c.clock.Now()
   783  		}
   784  
   785  		file, _, err := c.getNode(filepath, noCreate, dontResolveFinalSym)
   786  		if err != nil {
   787  			return err
   788  		}
   789  		mtime, err := c.engine.GetMtime(c.user, file)
   790  		if err != nil {
   791  			return err
   792  		}
   793  		if !libfs.TimeEqual(mtime, expectedMtime) {
   794  			return fmt.Errorf("Mtime (name=%s) got=%s, expected=%s", filepath,
   795  				mtime, expectedMtime)
   796  		}
   797  		return nil
   798  	}, Defaults, fmt.Sprintf("mtime(%s, %s)", filepath, expectedMtime)}
   799  }
   800  
   801  func rm(filepath string) fileOp {
   802  	return fileOp{func(c *ctx) error {
   803  		dir, name := path.Split(filepath)
   804  		parent, _, err := c.getNode(dir, noCreate, resolveAllSyms)
   805  		if err != nil {
   806  			return err
   807  		}
   808  		return c.engine.RemoveEntry(c.user, parent, name)
   809  	}, Defaults, fmt.Sprintf("rm(%s)", filepath)}
   810  }
   811  
   812  func rmdir(filepath string) fileOp {
   813  	return fileOp{func(c *ctx) error {
   814  		dir, name := path.Split(filepath)
   815  		parent, _, err := c.getNode(dir, noCreate, resolveAllSyms)
   816  		if err != nil {
   817  			return err
   818  		}
   819  		return c.engine.RemoveDir(c.user, parent, name)
   820  	}, Defaults, fmt.Sprintf("rmdir(%s)", filepath)}
   821  }
   822  
   823  func rename(src, dst string) fileOp {
   824  	return fileOp{func(c *ctx) error {
   825  		sdir, sname := path.Split(src)
   826  		sparent, _, err := c.getNode(sdir, noCreate, resolveAllSyms)
   827  		if err != nil {
   828  			return err
   829  		}
   830  		ddir, dname := path.Split(dst)
   831  		dparent, _, err := c.getNode(ddir, createDir, resolveAllSyms)
   832  		if err != nil {
   833  			return err
   834  		}
   835  		return c.engine.Rename(c.user, sparent, sname, dparent, dname)
   836  	}, Defaults, fmt.Sprintf("rename(%s => %s)", src, dst)}
   837  }
   838  
   839  func disableUpdates() fileOp {
   840  	return fileOp{func(c *ctx) error {
   841  		c.noSyncEnd = true
   842  		err := c.engine.SyncFromServer(c.user, c.tlfName, c.tlfType)
   843  		if err != nil {
   844  			return err
   845  		}
   846  		return c.engine.DisableUpdatesForTesting(c.user, c.tlfName, c.tlfType)
   847  	}, IsInit, "disableUpdates()"}
   848  }
   849  
   850  func stallOnMDPut() fileOp {
   851  	return fileOp{func(c *ctx) error {
   852  		// TODO: Allow test to pass in a more precise maxStalls limit.
   853  		c.staller.StallMDOp(libkbfs.StallableMDPut, 100, false)
   854  		return nil
   855  	}, Defaults, "stallOnMDPut()"}
   856  }
   857  
   858  func waitForStalledMDPut() fileOp {
   859  	return fileOp{func(c *ctx) error {
   860  		c.staller.WaitForStallMDOp(libkbfs.StallableMDPut)
   861  		return nil
   862  	}, IsInit, "waitForStalledMDPut()"}
   863  }
   864  
   865  func undoStallOnMDPut() fileOp {
   866  	return fileOp{func(c *ctx) error {
   867  		c.staller.UndoStallMDOp(libkbfs.StallableMDPut)
   868  		return nil
   869  	}, IsInit, "undoStallOnMDPut()"}
   870  }
   871  
   872  func stallOnMDGetForTLF() fileOp {
   873  	return fileOp{func(c *ctx) error {
   874  		// TODO: Allow test to pass in a more precise maxStalls limit.
   875  		c.staller.StallMDOp(libkbfs.StallableMDGetForTLF, 100, false)
   876  		return nil
   877  	}, Defaults, "stallOnMDGetForTLF()"}
   878  }
   879  
   880  func waitForStalledMDGetForTLF() fileOp {
   881  	return fileOp{func(c *ctx) error {
   882  		c.staller.WaitForStallMDOp(libkbfs.StallableMDGetForTLF)
   883  		return nil
   884  	}, IsInit, "waitForStalledMDGetForTLF()"}
   885  }
   886  
   887  func unstallOneMDGetForTLF() fileOp {
   888  	return fileOp{func(c *ctx) error {
   889  		c.staller.UnstallOneMDOp(libkbfs.StallableMDGetForTLF)
   890  		return nil
   891  	}, IsInit, "unstallOneMDGetForTLF()"}
   892  }
   893  
   894  func undoStallOnMDGetForTLF() fileOp {
   895  	return fileOp{func(c *ctx) error {
   896  		c.staller.UndoStallMDOp(libkbfs.StallableMDGetForTLF)
   897  		return nil
   898  	}, IsInit, "undoStallOnMDGetForTLF()"}
   899  }
   900  
   901  func stallOnMDGetRange() fileOp {
   902  	return fileOp{func(c *ctx) error {
   903  		// TODO: Allow test to pass in a more precise maxStalls limit.
   904  		c.staller.StallMDOp(libkbfs.StallableMDGetRange, 100, false)
   905  		return nil
   906  	}, Defaults, "stallOnMDGetRange()"}
   907  }
   908  
   909  func waitForStalledMDGetRange() fileOp {
   910  	return fileOp{func(c *ctx) error {
   911  		c.staller.WaitForStallMDOp(libkbfs.StallableMDGetRange)
   912  		return nil
   913  	}, IsInit, "waitForStalledMDGetRange()"}
   914  }
   915  
   916  func unstallOneMDGetRange() fileOp {
   917  	return fileOp{func(c *ctx) error {
   918  		c.staller.UnstallOneMDOp(libkbfs.StallableMDGetRange)
   919  		return nil
   920  	}, IsInit, "unstallOneMDGetRange()"}
   921  }
   922  
   923  func undoStallOnMDGetRange() fileOp {
   924  	return fileOp{func(c *ctx) error {
   925  		c.staller.UndoStallMDOp(libkbfs.StallableMDGetRange)
   926  		return nil
   927  	}, IsInit, "undoStallOnMDGetRange()"}
   928  }
   929  
   930  func stallOnMDResolveBranch() fileOp {
   931  	return fileOp{func(c *ctx) error {
   932  		// TODO: Allow test to pass in a more precise maxStalls limit.
   933  		c.staller.StallMDOp(libkbfs.StallableMDResolveBranch, 100, false)
   934  		return nil
   935  	}, Defaults, "stallOnMDResolveBranch()"}
   936  }
   937  
   938  func waitForStalledMDResolveBranch() fileOp {
   939  	return fileOp{func(c *ctx) error {
   940  		c.staller.WaitForStallMDOp(libkbfs.StallableMDResolveBranch)
   941  		return nil
   942  	}, IsInit, "waitForStalledMDResolveBranch()"}
   943  }
   944  
   945  func unstallOneMDResolveBranch() fileOp {
   946  	return fileOp{func(c *ctx) error {
   947  		c.staller.UnstallOneMDOp(libkbfs.StallableMDResolveBranch)
   948  		return nil
   949  	}, IsInit, "unstallOneMDResolveBranch()"}
   950  }
   951  
   952  func undoStallOnMDResolveBranch() fileOp {
   953  	return fileOp{func(c *ctx) error {
   954  		c.staller.UndoStallMDOp(libkbfs.StallableMDResolveBranch)
   955  		return nil
   956  	}, IsInit, "undoStallOnMDResolveBranch()"}
   957  }
   958  
   959  func reenableUpdates() fileOp {
   960  	return fileOp{func(c *ctx) error {
   961  		c.noSyncEnd = false
   962  		err := c.engine.ReenableUpdates(c.user, c.tlfName, c.tlfType)
   963  		if err != nil {
   964  			return err
   965  		}
   966  		return c.engine.SyncFromServer(c.user, c.tlfName, c.tlfType)
   967  	}, IsInit, "reenableUpdates()"}
   968  }
   969  
   970  func reenableUpdatesNoSync() fileOp {
   971  	return fileOp{func(c *ctx) error {
   972  		return c.engine.ReenableUpdates(c.user, c.tlfName, c.tlfType)
   973  	}, IsInit, "reenableUpdatesNoSync()"}
   974  }
   975  
   976  func forceQuotaReclamation() fileOp {
   977  	return fileOp{func(c *ctx) error {
   978  		err := c.engine.ForceQuotaReclamation(c.user, c.tlfName, c.tlfType)
   979  		if err != nil {
   980  			return err
   981  		}
   982  		// Wait for QR to finish.
   983  		return c.engine.SyncFromServer(c.user, c.tlfName, c.tlfType)
   984  	}, IsInit, "forceQuotaReclamation()"}
   985  }
   986  
   987  func rekey() fileOp {
   988  	return fileOp{func(c *ctx) error {
   989  		return c.engine.Rekey(c.user, c.tlfName, c.tlfType)
   990  	}, IsInit, "rekey()"}
   991  }
   992  
   993  func enableJournal() fileOp {
   994  	return fileOp{func(c *ctx) error {
   995  		return c.engine.EnableJournal(c.user, c.tlfName, c.tlfType)
   996  	}, IsInit, "enableJournal()"}
   997  }
   998  
   999  func pauseJournal() fileOp {
  1000  	return fileOp{func(c *ctx) error {
  1001  		return c.engine.PauseJournal(c.user, c.tlfName, c.tlfType)
  1002  	}, IsInit, "pauseJournal()"}
  1003  }
  1004  
  1005  func resumeJournal() fileOp {
  1006  	return fileOp{func(c *ctx) error {
  1007  		return c.engine.ResumeJournal(c.user, c.tlfName, c.tlfType)
  1008  	}, IsInit, "resumeJournal()"}
  1009  }
  1010  
  1011  func flushJournal() fileOp {
  1012  	return fileOp{func(c *ctx) error {
  1013  		return c.engine.FlushJournal(c.user, c.tlfName, c.tlfType)
  1014  	}, IsInit, "flushJournal()"}
  1015  }
  1016  
  1017  func checkUnflushedPaths(expectedPaths []string) fileOp {
  1018  	return fileOp{func(c *ctx) error {
  1019  		paths, err := c.engine.UnflushedPaths(c.user, c.tlfName, c.tlfType)
  1020  		if err != nil {
  1021  			return err
  1022  		}
  1023  
  1024  		sort.Strings(expectedPaths)
  1025  		sort.Strings(paths)
  1026  		if !reflect.DeepEqual(expectedPaths, paths) {
  1027  			return fmt.Errorf("Expected unflushed paths %v, got %v",
  1028  				expectedPaths, paths)
  1029  		}
  1030  		return nil
  1031  	}, IsInit, fmt.Sprintf("checkUnflushedPaths(%s)", expectedPaths)}
  1032  }
  1033  
  1034  func checkPrevRevisions(filepath string, counts []uint8) fileOp {
  1035  	return fileOp{func(c *ctx) error {
  1036  		file, _, err := c.getNode(filepath, noCreate, resolveAllSyms)
  1037  		if err != nil {
  1038  			return err
  1039  		}
  1040  		pr, err := c.engine.GetPrevRevisions(c.user, file)
  1041  		if err != nil {
  1042  			return err
  1043  		}
  1044  		if len(pr) != len(counts) {
  1045  			return fmt.Errorf("Wrong number of prev revisions: %v", pr)
  1046  		}
  1047  		for i, c := range counts {
  1048  			if c != pr[i].Count {
  1049  				return fmt.Errorf("Unexpected prev revision counts: %v", pr)
  1050  			}
  1051  		}
  1052  		return nil
  1053  	}, Defaults, fmt.Sprintf("prevRevisions(%s, %v)", filepath, counts)}
  1054  }
  1055  
  1056  type expectedEdit struct {
  1057  	tlfName      string
  1058  	tlfType      keybase1.FolderType
  1059  	writer       string
  1060  	files        []string
  1061  	deletedFiles []string
  1062  }
  1063  
  1064  func checkUserEditHistoryWithSort(
  1065  	expectedEdits []expectedEdit, doSort bool) fileOp {
  1066  	return fileOp{func(c *ctx) error {
  1067  		history, err := c.engine.UserEditHistory(c.user)
  1068  		if err != nil {
  1069  			return err
  1070  		}
  1071  
  1072  		// Convert the history to expected edits.
  1073  		hEdits := make([]expectedEdit, len(history))
  1074  		for i, h := range history {
  1075  			if len(h.History) != 1 {
  1076  				return fmt.Errorf(
  1077  					"Unexpected history of size %d: %#v", len(h.History), h)
  1078  			}
  1079  			hEdits[i].tlfName = h.Folder.Name
  1080  			hEdits[i].tlfType = h.Folder.FolderType
  1081  			hEdits[i].writer = h.History[0].WriterName
  1082  			for _, we := range h.History[0].Edits {
  1083  				hEdits[i].files = append(hEdits[i].files, we.Filename)
  1084  			}
  1085  			if doSort {
  1086  				sort.Strings(hEdits[i].files)
  1087  				sort.Strings(expectedEdits[i].files)
  1088  			}
  1089  			for _, we := range h.History[0].Deletes {
  1090  				hEdits[i].deletedFiles = append(
  1091  					hEdits[i].deletedFiles, we.Filename)
  1092  			}
  1093  			if doSort {
  1094  				sort.Strings(hEdits[i].deletedFiles)
  1095  				sort.Strings(expectedEdits[i].deletedFiles)
  1096  			}
  1097  		}
  1098  
  1099  		if !reflect.DeepEqual(expectedEdits, hEdits) {
  1100  			return fmt.Errorf("Expected edit history %v, got %v",
  1101  				expectedEdits, hEdits)
  1102  		}
  1103  		return nil
  1104  	}, Defaults, "checkUserEditHistory()"}
  1105  }
  1106  
  1107  func checkUserEditHistory(expectedEdits []expectedEdit) fileOp {
  1108  	return checkUserEditHistoryWithSort(expectedEdits, false)
  1109  }
  1110  
  1111  func checkDirtyPaths(expectedPaths []string) fileOp {
  1112  	return fileOp{func(c *ctx) error {
  1113  		paths, err := c.engine.DirtyPaths(c.user, c.tlfName, c.tlfType)
  1114  		if err != nil {
  1115  			return err
  1116  		}
  1117  
  1118  		sort.Strings(expectedPaths)
  1119  		sort.Strings(paths)
  1120  		if !reflect.DeepEqual(expectedPaths, paths) {
  1121  			return fmt.Errorf("Expected dirty paths %v, got %v",
  1122  				expectedPaths, paths)
  1123  		}
  1124  		return nil
  1125  	}, IsInit, fmt.Sprintf("checkDirtyPaths(%s)", expectedPaths)}
  1126  }
  1127  
  1128  func forceConflict() fileOp {
  1129  	return fileOp{func(c *ctx) error {
  1130  		return c.engine.ForceConflict(c.user, c.tlfName, c.tlfType)
  1131  	}, IsInit, "forceConflict()"}
  1132  }
  1133  
  1134  func clearConflicts() fileOp {
  1135  	return fileOp{func(c *ctx) error {
  1136  		return c.engine.ClearConflicts(c.user, c.tlfName, c.tlfType)
  1137  	}, IsInit, "clearConflicts()"}
  1138  }
  1139  
  1140  func lsfavoritesOp(c *ctx, expected []string, t tlf.Type) error {
  1141  	favorites, err := c.engine.GetFavorites(c.user, t)
  1142  	if err != nil {
  1143  		return err
  1144  	}
  1145  	c.tb.Log("lsfavorites", t, "=>", favorites)
  1146  	for _, f := range expected {
  1147  		if favorites[f] {
  1148  			delete(favorites, f)
  1149  			continue
  1150  		}
  1151  
  1152  		p, err := tlf.CanonicalToPreferredName(c.username, tlf.CanonicalName(f))
  1153  		if err != nil {
  1154  			return err
  1155  		}
  1156  		if favorites[string(p)] {
  1157  			delete(favorites, string(p))
  1158  		} else {
  1159  			return fmt.Errorf("Missing favorite %s", f)
  1160  		}
  1161  	}
  1162  
  1163  	for f := range favorites {
  1164  		return fmt.Errorf("Unexpected favorite %s", f)
  1165  	}
  1166  	return nil
  1167  }
  1168  
  1169  func lspublicfavorites(contents []string) fileOp {
  1170  	return fileOp{func(c *ctx) error {
  1171  		return lsfavoritesOp(c, contents, tlf.Public)
  1172  	}, Defaults, fmt.Sprintf("lspublicfavorites(%s)", contents)}
  1173  }
  1174  
  1175  func lsprivatefavorites(contents []string) fileOp {
  1176  	return fileOp{func(c *ctx) error {
  1177  		return lsfavoritesOp(c, contents, tlf.Private)
  1178  	}, Defaults, fmt.Sprintf("lsprivatefavorites(%s)", contents)}
  1179  }
  1180  
  1181  func lsteamfavorites(contents []string) fileOp {
  1182  	return fileOp{func(c *ctx) error {
  1183  		return lsfavoritesOp(c, contents, tlf.SingleTeam)
  1184  	}, Defaults, fmt.Sprintf("lsteamfavorites(%s)", contents)}
  1185  }
  1186  
  1187  func lsdir(name string, contents m) fileOp {
  1188  	return fileOp{func(c *ctx) error {
  1189  		folder, _, err := c.getNode(name, noCreate, resolveAllSyms)
  1190  		if err != nil {
  1191  			return err
  1192  		}
  1193  		entries, err := c.engine.GetDirChildrenTypes(c.user, folder)
  1194  		if err != nil {
  1195  			return err
  1196  		}
  1197  		c.tb.Log("lsdir =>", entries)
  1198  	outer:
  1199  		for restr, ty := range contents {
  1200  			re := regexp.MustCompile(restr)
  1201  			for node, ty2 := range entries {
  1202  				// Windows does not mark "executable" bits in any way.
  1203  				if re.MatchString(node) && (ty == ty2 ||
  1204  					(c.engine.Name() == "dokan" && ty == "EXEC" && ty2 == "FILE")) {
  1205  					delete(entries, node)
  1206  					continue outer
  1207  				}
  1208  			}
  1209  			return fmt.Errorf("%s of type %s not found", restr, ty)
  1210  		}
  1211  		// and make sure everything is matched
  1212  		for node, ty := range entries {
  1213  			return fmt.Errorf("unexpected %s of type %s found in %s", node, ty, name)
  1214  		}
  1215  		return nil
  1216  	}, Defaults, fmt.Sprintf("lsdir(%s, %d bytes)", name, len(contents))}
  1217  }
  1218  
  1219  // createType specifies whether getNode should create any nodes that
  1220  // don't exist.
  1221  type createType int
  1222  
  1223  const (
  1224  	noCreate createType = iota
  1225  	createDir
  1226  	createFile
  1227  	createFileExcl
  1228  )
  1229  
  1230  func (c createType) String() string {
  1231  	switch c {
  1232  	case noCreate:
  1233  		return "noCreate"
  1234  	case createDir:
  1235  		return "createDir"
  1236  	case createFile:
  1237  		return "createFile"
  1238  	case createFileExcl:
  1239  		return "createFileExcl"
  1240  	default:
  1241  		return fmt.Sprintf("unknownCreateType:%d", c)
  1242  	}
  1243  }
  1244  
  1245  // symBehavior specifies what getNode should do with symlinks.
  1246  type symBehavior int
  1247  
  1248  const (
  1249  	resolveAllSyms symBehavior = iota
  1250  	dontResolveFinalSym
  1251  )
  1252  
  1253  func (c *ctx) getNode(filepath string, create createType, sym symBehavior) (
  1254  	Node, bool, error) {
  1255  	if filepath == "" || filepath == "/" {
  1256  		return c.rootNode, false, nil
  1257  	}
  1258  	if filepath[len(filepath)-1] == '/' {
  1259  		filepath = filepath[:len(filepath)-1]
  1260  	}
  1261  	components := strings.Split(filepath, "/")
  1262  	c.tb.Log("getNode:", filepath, create, components, len(components))
  1263  	var symPath string
  1264  	var err error
  1265  	var node, parent Node
  1266  	parent = c.rootNode
  1267  	wasCreated := false
  1268  	for i, name := range components {
  1269  		node, symPath, err = c.engine.Lookup(c.user, parent, name)
  1270  		c.tb.Log("getNode:", i, name, node, symPath, err)
  1271  
  1272  		if i+1 == len(components) { // last element in path
  1273  			switch {
  1274  			case err == nil:
  1275  				if create == createFileExcl {
  1276  					return nil, false, data.NameExistsError{}
  1277  				}
  1278  			case create == createFileExcl:
  1279  				c.tb.Log("getNode: CreateFileExcl")
  1280  				node, err = c.engine.CreateFileExcl(c.user, parent, name)
  1281  				wasCreated = true
  1282  			case create == createFile:
  1283  				c.tb.Log("getNode: CreateFile")
  1284  				node, err = c.engine.CreateFile(c.user, parent, name)
  1285  				wasCreated = true
  1286  			case create == createDir:
  1287  				c.tb.Log("getNode: CreateDir")
  1288  				node, err = c.engine.CreateDir(c.user, parent, name)
  1289  				wasCreated = true
  1290  			case create == noCreate:
  1291  				// let it error!
  1292  			default:
  1293  				panic("unreachable")
  1294  			}
  1295  		} else if err != nil && create != noCreate {
  1296  			// intermediate element in path
  1297  			c.tb.Log("getNode: CreateDir")
  1298  			node, err = c.engine.CreateDir(c.user, parent, name)
  1299  			wasCreated = true
  1300  		} // otherwise let it error!
  1301  
  1302  		if err != nil {
  1303  			return nil, false, err
  1304  		}
  1305  
  1306  		parent = node
  1307  		// If this is a symlink, and either we're supposed to resolve
  1308  		// all symlinks or this isn't the final one in the path, then
  1309  		// go ahead and recurse on the resolved path.
  1310  		if len(symPath) > 0 &&
  1311  			(sym == resolveAllSyms || i != len(components)-1) {
  1312  			var tmp []string
  1313  			if symPath[0] == '/' {
  1314  				tmp = []string{symPath}
  1315  			} else {
  1316  				tmp = components[:i]
  1317  				tmp = append(tmp, symPath)
  1318  			}
  1319  			tmp = append(tmp, components[i+1:]...)
  1320  			newpath := path.Clean(path.Join(tmp...))
  1321  			c.tb.Log("getNode: symlink ", symPath, " redirecting to ", newpath)
  1322  			return c.getNode(newpath, create, sym)
  1323  		}
  1324  	}
  1325  	return node, wasCreated, nil
  1326  }
  1327  
  1328  // crnameAtTime returns the name of a conflict file, at a given
  1329  // duration past the default time.
  1330  func crnameAtTime(path string, user username, d time.Duration) string {
  1331  	cre := libkbfs.WriterDeviceDateConflictRenamer{}
  1332  	return cre.ConflictRenameHelper(time.Unix(1, 0).Add(d), string(user),
  1333  		"dev1", path)
  1334  }
  1335  
  1336  // crnameAtTimeEsc returns the name of a conflict file with regular
  1337  // expression escapes, at a given duration past the default time.
  1338  func crnameAtTimeEsc(path string, user username, d time.Duration) string {
  1339  	return regexp.QuoteMeta(crnameAtTime(path, user, d))
  1340  }
  1341  
  1342  // crname returns the name of a conflict file.
  1343  func crname(path string, user username) string {
  1344  	return crnameAtTime(path, user, 0)
  1345  }
  1346  
  1347  // crnameEsc returns the name of a conflict file with regular expression escapes.
  1348  func crnameEsc(path string, user username) string {
  1349  	return crnameAtTimeEsc(path, user, 0)
  1350  }
  1351  
  1352  type silentBenchmark struct{ *testing.B }
  1353  
  1354  func (silentBenchmark) Log(args ...interface{})                 {}
  1355  func (silentBenchmark) Logf(format string, args ...interface{}) {}